创建箭头函数是为了简化函数范围,并通过简化this关键字来解决它。它们使用了=>语法,它看起来像一个箭头。
备注:不替换已有功能。如果你用箭头函数替换所有的函数语法,它不会在所有情况下都有效。
让我们看一看现有的ES5语法。如果this关键字在对象的方法(属于对象的函数)中,它指的是什么?
var Actor = {
name: 'RajiniKanth',
getName: function() {
console.log(this.name);
}
};
Actor.getName();
上面的代码片段将引用一个对象并打印出名称“RajiniKanth”。让我们探索下面的代码片段,看看这里会指出什么。
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
如果this关键字在method的函数中呢?
这里this指的是窗口对象,而不是内部函数,因为它超出了作用域。因此,总是引用它所在函数的所有者,在这种情况下——因为它现在超出了作用域——是window/global对象。
当它在对象的方法内部时,函数的所有者就是该对象。因此,this关键字被绑定到对象。然而,当它在函数内部时,无论是单独的还是在另一个方法中,它总是引用窗口/全局对象。
var fn = function(){
alert(this);
}
fn(); // [object Window]
我们的ES5本身就有解决这个问题的方法。让我们在深入ES6箭头函数之前研究一下如何解决它。
通常你会在方法的内部函数之外创建一个变量。现在' forEach '方法可以访问这个对象,从而访问对象的属性及其值。
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
var _this = this;
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
使用bind将引用该方法的this关键字附加到该方法的内部函数。
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(this.name + " has acted in " + movie);
}.bind(this));
}
};
Actor.showMovies();
现在,使用ES6的箭头函数,我们可以以更简单的方式处理词汇范围问题。
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach((movie) => {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
箭头函数更像函数语句,只是它们将this绑定到父作用域。如果箭头函数在顶部作用域,则this参数将引用窗口/全局作用域,而常规函数内部的箭头函数的this参数将与其外部函数相同。
对于箭头函数,它在创建时被绑定到外围作用域,并且不能更改。new操作符、bind、call和apply对此没有影响。
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`
asyncFunction(o, function (param) {
// We made a mistake of thinking `this` is
// the instance of `o`.
console.log('param === this?', param === this);
});
}
};
o.doSomething(); // param === this? false
在上面的例子中,我们失去了对它的控制。我们可以通过使用this的变量引用或bind来解决上面的例子。在ES6中,管理this变得更容易,因为它绑定到词法作用域。
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`.
//
// Because this arrow function is created within
// the scope of `doSomething` it is bound to this
// lexical scope.
asyncFunction(o, (param) => {
console.log('param === this?', param === this);
});
}
};
o.doSomething(); // param === this? true
何时不使用箭头函数
在一个对象文字的内部。
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
getName: () => {
alert(this.name);
}
};
Actor.getName();
演员。getName是用一个箭头函数定义的,但在调用时它警告为未定义,因为this.name是未定义的,因为上下文仍然是窗口。
发生这种情况是因为箭头函数在词法上将上下文绑定到窗口对象…也就是外部作用域。执行this.name相当于window.name,而window.name是未定义的。
对象原型
同样的规则也适用于在原型对象上定义方法。而不是使用箭头函数来定义sayCatName方法,这会带来一个不正确的上下文窗口:
function Actor(name) {
this.name = name;
}
Actor.prototype.getName = () => {
console.log(this === window); // => true
return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined
调用构造函数
这在构造调用中是新创建的对象。当执行new Fn()时,构造函数Fn的上下文是一个新对象:this instanceof Fn === true。
这是从封闭上下文(即外部作用域)中设置的,这使得它没有分配给新创建的对象。
var Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');
带有动态上下文的回调
箭头函数在声明时静态地绑定上下文,不可能使其动态。将事件监听器附加到DOM元素是客户端编程中的常见任务。事件以此作为目标元素触发处理程序函数。
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
这是在全局上下文中定义的箭头函数中的窗口。当单击事件发生时,浏览器尝试调用带有按钮上下文的处理程序函数,但箭头函数不会更改其预定义上下文。这一点。innerHTML相当于window。innerHTML,没有任何意义。
你必须应用一个函数表达式,它允许改变这取决于目标元素:
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
当用户单击按钮时,处理程序函数中的这个就是按钮。因此这个问题。innerHTML = 'Clicked button'正确修改按钮文本,以反映单击状态。
参考文献
何时“不”使用箭头函数