创建Arrow functions 是为了简化函数scope 并通过使其更简单来解决this 关键字。他们使用=> 语法,看起来像一个箭头。
注意:它不会取代现有的功能。如果将每个函数语法都替换为箭头函数,则并非在所有情况下都有效。
让我们看看现有的 ES5 语法。如果 this 关键字在对象的方法(属于对象的函数)中,它指的是什么?
var Actor = {
name: 'RajiniKanth',
getName: function() {
console.log(this.name);
}
};
Actor.getName();
上面的 sn-p 将引用 object 并打印出名称 "RajiniKanth"。让我们探索下面的 sn-p,看看这里会指出什么。
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’s function 内部呢?
这里指的是window object,而不是inner function,因为它脱离了scope。因为this,总是引用它所在函数的所有者,对于这种情况 — 因为它现在超出范围 — 窗口/全局对象。
当它在 object 的方法内时 — function 的所有者就是对象。因此 this 关键字被绑定到对象。然而,当它在函数内部时,无论是独立的还是在另一个方法中,它总是会引用 window/global 对象。
var fn = function(){
alert(this);
}
fn(); // [object Window]
在我们的 ES5 本身中有解决这个问题的方法。在深入研究 ES6 箭头函数如何解决它之前,让我们先研究一下。
通常你会在方法的内部函数之外创建一个变量。现在‘forEach’ 方法可以访问this 和object’s 属性及其值。
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 关键字附加到method’s inner function。
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参数与其外部函数相同。
使用箭头 函数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();
Actor.getName 是用箭头函数定义的,但在调用时它会发出未定义警报,因为 this.name 是 undefined,因为上下文仍然是 window。
这是因为箭头函数在词法上将上下文与window object... 即外部作用域绑定在一起。执行this.name等价于window.name,未定义。
对象原型
在prototype object 上定义方法时适用相同的规则。而不是使用箭头函数来定义 sayCatName 方法,这会带来不正确的context window:
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
调用构造函数
this 在构造调用中是新创建的对象。执行 new Fn() 时,constructor Fn 的上下文是一个新对象:this instanceof Fn === true。
this 是从封闭上下文设置的,即外部范围,使其不分配给新创建的对象。
var Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');
动态上下文回调
箭头函数在声明时静态绑定context,并且不可能使其成为动态的。将事件侦听器附加到 DOM 元素是客户端编程中的常见任务。一个事件以 this 作为目标元素触发处理函数。
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
this 是在全局上下文中定义的箭头函数中的窗口。当单击事件发生时,浏览器会尝试使用按钮上下文调用处理函数,但箭头函数不会更改其预定义的上下文。 this.innerHTML 等价于window.innerHTML,没有意义。
你必须应用一个函数表达式,它允许根据目标元素改变它:
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
当用户单击按钮时,处理函数中的this 就是按钮。因此this.innerHTML = 'Clicked button' 正确地修改了按钮文本以反映点击状态。
参考文献