1. 定义对象方法
JS 中对象方法的定义方式是在对象上定义一个指向函数的属性,当方法被调用的时候,方法内的 this 就会指向方法所属的对象。
let obj = { array: [1, 2, 3], sum: () => { console.log(this === window); // true return this.array.reduce((result, item) => result + item); } }; console.log(this === window); //true obj.sum();//报错:Uncaught TypeError: Cannot read property 'reduce' of undefined at Object.sum
运行时 this.array 是未定义的,调用 obj.sum 的时候,执行上下文里面的 this 仍然指向的是 window,原因是箭头函数把函数上下文绑定到了 window 上,this.array 等价于 window.array,显然后者是未定义的。
修改方式:使用函数表达式或者方法简写(ES6 中已经支持)来定义方法,这样能确保 this 是在运行时是由包含它的上下文决定的。代码如下:
let obj = { array: [1, 2, 3], sum() { console.log(this === window); // false return this.array.reduce((result, item) => result + item); } }; console.log(this === window); //true console.log(obj.sum());//6
2.定义原型方法
同样的规则适用于原型方法(prototype method)的定义,使用箭头函数会导致运行时的执行上下文错误。比如下面代码:
function Cat(name) { this.name = name; } Cat.prototype.sayCatName = () => { console.log(this === window); // => true return this.name; }; const cat = new Cat('Tom'); console.log(cat.sayCatName()); // undefined
使用传统的函数表达式就能解决问题,代码如下所示:
function Cat(name) { this.name = name; } Cat.prototype.sayCatName = function () { console.log(this === window); // => false return this.name; } const cat = new Cat('Tom'); console.log(cat.sayCatName()); // Tom
sayCatName 变成普通函数之后,被调用时的执行上下文就会指向新创建的 cat 实例。
3. 定义事件回调函数
箭头函数在声明的时候就绑定了执行上下文,要动态改变上下文是不可能的,在需要动态上下文的时候它的弊端就凸显出来。
比如在客户端编程中常见的 DOM 事件回调函数(event listenner)绑定,触发回调函数时 this 指向当前发生事件的 DOM 节点,而动态上下文这个时候就非常有用,比如下面这段代码试图使用箭头函数来作事件回调函数。
const button = document.getElementById('myButton'); button.addEventListener('click', () => { console.log(this === window); // true this.innerHTML = 'Clicked button'; });
在全局上下文下定义的箭头函数执行时 this 会指向 window,当单击事件发生时,this.innerHTML 就等价于 window.innerHTML,而后者是没有任何意义的。
使用函数表达式就可以在运行时动态的改变 this,修正后的代码:
const button = document.getElementById('myButton'); button.addEventListener('click', function () { console.log(this === button); // true this.innerHTML = 'Clicked button'; });
4. 定义构造函数
构造函数中的 this 指向新创建的对象,当执行 new Car() 的时候,构造函数 Car 的上下文就是新创建的对象,也就是说 this instanceof Car === true。显然,箭头函数是不能用来做构造函数, 实际上 JS 会禁止你这么做,如果你这么做了,它就会抛出异常。
比如下面的代码就会报错:
const Message = (text) => { this.text = text; }; const helloMessage = new Message('Hello World!');//报错: Throws "TypeError: Message is not a constructor"
构造新的 Message 实例时,JS 引擎抛了错误,因为 Message 不是构造函数。可以通过使用函数表达式或者函数声明来声明构造函数修复上面的例子。
const Message = function(text) { this.text = text; }; const helloMessage = new Message('Hello World!'); console.log(helloMessage.text); // 'Hello World!'