call 和 apply 的区别仅仅是要传给fn的参数的形式不同:对于apply,传给fn的参数argument是个数组,数组由所有参数组成;对于call,传给fn的参数argument直接是所有参数的排列, 直接一个个写入就可以。
例如要传给函数fn三个参数: 1、2、3. 则对于 call和apply调用的方法分别是:
fn.call(targetThis, 1, 2, 3); // 把 1,2,3直接传入 fn.apply(targetThis, [1,2,3]); // 把1,2,3合成数组后作为参数
2.5 箭头函数 和 bind 函数
箭头函数和bind函数对于this的处理与普通函数不同, 要单独拿出来说。
2.5.1 箭头函数
与传统函数不同, 箭头函数本身不包含this, 它的 this 继承自它定义时的作用域链的上一层。而且箭头函数不能作为构造函数,它也没有文章 第1部分 所说的arguments属性。
下面用一个例子引出箭头函数中this的来源:
function Person(){ this.age = 24; setTimeout(function(){ console.log(this.age); // undefined console.log(this === window); // true }, 1000); } var p = new Person(); // 创建一个实例的时候就立即执行了定时器
可以看到, 在定时器内定义的普通匿名函数无法访问到 Person 的 age 属性, 这是因为setTimeout是个全局函数, 它的内部的this指向的是window, 而 window 上没有 age 这个属性, 所以就得到了 undefined。 从下面this === window 为 true 也说明了匿名函数中this指向的是window。
将普通的函数换成箭头函数之后可以看到如下结果:
function Person(){ this.age = 24; setTimeout(() => { console.log(this.age); // 24 console.log(this === p); // true }, 1000); } var p = new Person();
由上面的代码可以看出箭头函数内的 this 指向实例 p, 即它的 this 指向的是定义时候的作用域链的上一层。
说明: 这个例子仅用来引出箭头函数的this指向的来源, 不要像这样使用构造函数。
2.5.2 bind函数
bind函数的作用是根据一个旧函数而创建一个新函数,语法为newFn = oldFn.bind(thisTarget)。它会将旧函数复制一份作为新函数, 然后将新函数的this永远绑定到thisTarget指向的上下文中, 然后返回这个新函数, 以后每次调用这个新函数时, 无论用什么方法都无法改变这个新函数的 this 指向。例如:
// 创建一个对象有 name 和 sayName 属性 let p1 = { name: 'P1', sayName(){ console.log(this.name); // 访问函数指向的 this 的 name 属性 } } p1.sayName(); // P1 // 创建一个对象 p2, 并把这个对象作为bind函数绑定的this let p2 = { name: 'P2' } // 将p1的 sayName 函数的 this 绑定到 p2 上, 生成新函数 sayP2Name 并返回 let sayP2Name = p1.sayName.bind(p2); // 由于此时 sayP2Name 的内部 this 已经绑定了 p2, // 所以即使是按 文章2.1部分 所说的直接调用 sayP2Name, 它的 this 也是指向 p2 的, 并不是指向全局上下文或者 undefined sayP2Name(); // P2 // 定义新对象, 尝试将 sayP2Name 的 this 指向到 p3 上 let p3 = { name: 'P3' } // 尝试使用 call和apply 函数来将 sayP2Name 函数的 this 指向p3, // 但是由于 sayP2Name 函数的this 已经被bind函数永远绑定到p2上了, 所以this.name仍然是p2.name sayP2Name.call(p3); // P2 sayP2Name.apply(p3); // P2
通过以上内容可知一旦通过 bind 函数绑定了 this, 就再也无法改变 this 的指向了.