关于javascript:R12thiscallapply

71次阅读

共计 3659 个字符,预计需要花费 10 分钟才能阅读完成。

1. this

除去不罕用的 withevalthis 的指向大抵分为以下 4 种:

  • 作为对象的办法应用
  • 作为一般函数调用
  • 结构器应用
  • Function.prototype.callFunction.prototype.apply 调用

1.1 作为对象的办法调用

当函数作为对象的办法调用时,this 指向该对象。如果对象的办法起了别名,this 指向全局对象,具体可见如下代码。

const person = {
    userName: 'Mango',
    sayName () {console.log(this);
        console.log(this.userName);
    }
}
person.sayName();       // Mango
// 起别名
let sayName = person.sayName;    
sayName();              // undefined 此时 this 指向全局对象

1.2 一般函数调用

一般函数分为:ES5 函数 和 箭头函数(ES6)。

  • ES5 中的函数,this 总是指向全局对象;
  • ES6 的箭头函数,this 与定义时的上下文相干
<body>
<div id="myDiv"> 我是一个 DIV </div>

<script>
    const divDom = document.querySelector('#myDiv');

    divDom.addEventListener('click', function () {
        // this 指向 divDom
        console.log('匿名函数的 this:', this);
        let callback = function () {
            // this 指向 window
            console.log('匿名函数中 callback this:', this);
        }
        let callback2 = () => {
            // this 指向 divDom
            console.log('匿名函数中 callback(箭头函数) this:', this);
        }
        callback();
        callback2();})
    divDom.addEventListener('click', () => { // 此时上下文在全局环境中
        // this 指向 window
        console.log('箭头函数的 this:', this);
        let callback = function () {
            // this 指向 window
            console.log('箭头函数中 callback this:', this);
        }
        callback();})

    const obj = {addEvent () {divDom.addEventListener('click', () => { // 此时上下文在 obj 对象环境中
                // this 指向 obj 对象
                console.log('在对象外部箭头函数的 this:', this);
                let callback = function () {
                    // this 指向 window
                    console.log('对象外部匿名函数中 callback this:', this);
                }
                callback();})
        }
    }
    obj.addEvent();
</script>
</body>

1.3 结构器调用

当用 new 运算符调用函数时,该函数总会返回一个对象,通常状况下,结构器中的 this 指向返回的这个对象。

const Person = function () {this.name = 'Mango';}

const person = new Person();
console.log('person name:', person.name);

1.4 callapply

Function.prototype.callFunction.prototype.apply 能够动静扭转 this 指向,两者惟一区别是调用形式不同。

const obj1 = {
    name: 'Mango',
    getName () {return this.name;}
}
const obj2 = {name: 'Mango2'}
console.log(obj1.getName());    // Mango
console.log(obj1.getName.call(obj2));       // Mango2

1.5 隐没的 this

在 1.1 节中,对象的办法赋值给一个变量时(相似起别名),this 不再指向该对象,而是指向全局对象。如下:

const person = {
    userName: 'Mango',
    sayName () {console.log(this);
        console.log(this.userName);
    }
}
let sayName = person.sayName;    
sayName();  // undefined

这种状况能够应用 apply 办法扭转 this 指向,最简略的是间接扭转指向,如下:

// 接下面案例
sayName.apply(person);  // Mango

为了保障 sayNamethis 不失落,能够再创立的时候就通过 apply 指明 this, 如下:

// 应用了闭包
let sayName = (function (func, target) {return function () {return func.apply(target, arguments);
                    } 
                })(person.sayName, person);

// 也有比较简单的形式
let sayName = function () {return person.sayName();
}

能够将起别名形象为一个函数,能够指定 this 指向:

function methodAlias (func, target) {return function () {return func.apply(target, arguments);
    }
}

let sayName3 = methodAlias(person.sayName, person);
sayName3();

同样的例子 还有给 document.querySelector 等函数起别名,如下所示:

// Error: 该办法外部会应用指向 document 的 this
let queryElem = document.querySelector;
queryElem('#id');   // Error

// 应用自定义的起别名办法,指明 this 指向
let queryElem = methodAlias(document.querySelector, document);
queryElem('#id');

应用 bind, 举荐

在大部分浏览器中都实现了 Function.prototype.bind 办法,能够如下应用:

let sayName4 = person.sayName.bind(person);
sayName4();     // Mango

let queryElem = document.querySelector.bind(document);
queryElem('#id');

bind办法的实现原理如下:

Function.prototype.bind = function (context) {
    let self = this;
    return function () {self.apply(context, arguments);
    }
}

2. call 与 apply

2.1 简述

callapply 只是在模式是不同,如下所示:

const func = function (a, b, c) {console.log([a, b, c]);
}
// apply 的第二个参数是数组或类数组
func.apply(null, [1, 2, 3]);
// call 会将函数的参数顺次往后排,call 是 apply 的一个语法糖
func.call(null, 1, 2, 3);
}

callapply 的第一个参数为 null 时,函数(一般函数与对象办法) 体内的 this 会指向默认的宿主对象,在浏览器中是 window

window.age = 200;
const obj = {
    age: 28,
    func2: function () {console.log('func2:', this.age);
    }
}
const func1 = function () {console.log('func1:', this.age);
}
func1.apply(null);  // 200 this 指向 window
func1.apply(obj);   // 28 this 指向 obj

obj.func2.apply(null);  // 200 this 指向 window
obj.func2.apply(obj);   // 28 this 指向 obj

2.2 call 和 apply 的其余用处

借用其余对象的办法

最典型的就是类数组借用 Array.prototype 中的办法。
比方函数的参数列表 arguments 是一个类数组对象,尽管它也有“下标”,但它不是真正的数组,所以不能像数组一样应用数组的办法。
这种状况就能够应用 Array.prototype 中的办法。
个别状况下,对象有“下标”和 length 属性即可应用数组的原型办法。

罕用办法如下所示:

// 将类数组转为数组
Array.prototype.slice.apply(arguments);

// 往类数组中增加元素
Array.prototype.push.apply(arguments, ['one']);

//...

正文完
 0