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());    // Mangoconsole.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 的 thislet 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();     // Mangolet 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指向windowfunc1.apply(obj);   // 28 this指向 objobj.func2.apply(null);  // 200 this 指向 windowobj.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']);//...