ES6带来了箭头函数。本以为它是匿名函数的语法糖,于是就掉进了js的大坑里~
进入正题
简单来说js的this优先指向windows,除非挂到对象上才指向对象(隐式绑定和显式用call/apply/bind)。
那么正常(es6以前)来说,我们写类:
function A {
this.init();
}
A.prototype.init = function() {
console.dir(this);
}
var a = new A(); //this指向a
A(); //this指向window
当我们new之后,this就会指向实例a。 然而当我们用箭头函数时:
function A {
this.init();
}
A.prototype.init = () => {
console.dir(this);
}
var a = new A(); //this指向window
new的时候,箭头函数并没有改变this指向(???一脸懵逼)。查阅相关资料之后,发现引入箭头函数是解决这个痛点:当使用匿名函数返回的时候,匿名函数的this是指向this的。
function A {
this.f = function() {
return function() { console.dir(this);};
}
this.f()();
}
A(); //this指向window
var a = new A(); //this指向window
于是,当我们使用箭头函数时:它竟然“惊喜地”把this指向了实例a!
function A {
this.f = function() {
return () => { console.dir(this);};
}
this.f()();
}
A(); //this指向window
var a = new A(); //this指向a
真是无心插柳柳成荫,没想到箭头函数还有这样的this指向机制(明明写在外部一点用都没有)。为了深入了解箭头函数的this指向,我整理如下情况:
-
定义在类内:匿名函数和箭头函数的this指向都是实例。
function A { this.f = function() { console.dir(this); }; this.g = () => { console.dir(this); }; this.f(); //this指向a this.g(); //this指向a } var a = new A();
-
定义在类外:箭头函数this指向window。
function A { this.f(); //this指向a this.g(); //this指向window } A.prototype.f = function() { console.dir(this); }; A.prototype.g = () => { console.dir(this); }; var a = new A();
-
定义在另一个类内:和第二种情况类似。这样我们更清晰this机制:匿名函数永远跟随绑定时context的this指向;箭头函数总跟随定义时context的this指向。
function A() { this.f(); this.g(); } function B() { A.prototype.f = function() { console.dir(this); }; A.prototype.g = () => { console.dir(this); }; } var b = new B(); var a = new A(); //匿名函数this指向a, 箭头函数this指向b //ohter test B(); var a = new A(); //匿名函数this指向a, 箭头函数this指向window
-
返回函数:规则是无论嵌套多少层(至少一层),最终返回若是匿名函数,那么this永远指向window;最终返回若是箭头函数,那么this指向总跟随最近的嵌套层(倒数第一层)的this指向(还是总跟随定义时context的this指向)。
function A() { this.f = function() { return (function(){ console.dir(this); })(); } this.af = function() { return function() { console.dir(this); }; }; this.bf = () => { return function() { console.dir(this); }; }; this.aaf = function() { return function() { return function() { console.dir(this); }; }; }; this.abf = function() { return () => { return function() { console.dir(this); }; }; }; this.baf = () => { return function() { return function() { console.dir(this); }; }; }; this.bbf = () => { return () => { return function() { console.dir(this); }; }; }; this.g = function() { return (() => { console.dir(this); })(); } this.ag = function() { return () => { console.dir(this); }; }; this.bg = () => { return () => { console.dir(this); }; }; this.aag = function() { return function() { return () => { console.dir(this); }; }; }; this.abg = function() { return () => { return () => { console.dir(this); }; }; }; this.bag = () => { return function() { return () => { console.dir(this); }; }; }; this.bbg = () => { return () => { return () => { console.dir(this); }; }; }; this.f(); //this指向window this.af()(); //this指向window this.bf()(); //this指向window this.aaf()()(); //this指向window this.abf()()(); //this指向window this.baf()()(); //this指向window this.bbf()()(); //this指向window this.g(); //this指向a this.ag()(); //this指向a this.bg()(); //this指向a this.aag()()(); //this指向window this.abg()()(); //this指向a this.bag()()(); //this指向window this.bbg()()(); //this指向a } var a = new A();
-
返回匿名函数实例:这里就比较能清楚分辨在嵌套返回匿名函数时,何时this是指向window的。
function A() { this.f = function() { this.ff = function() { console.dir(this); }; return this.ff; } this.g = function() { this.gg = function() { console.dir(this); }; return this.gg(); } this.h = function() { var/* let/const */ hh = function() { console.dir(this); }; return hh; /* return hh(); */ /* 此时写法相当于: return (function(){ console.dir(this); })(); */ } this.f()(); //this指向window this.g(); //this指向a this.h()(); // this.h(); 包括注释所有组合,this都是指向window //let/const 不会使匿名函数的this隐式绑定。 } var a = new A();
-
显式绑定
var f = function() { console.dir(this); }; var g = () => { console.dir(this); }; var a = {}; f.call(a); //this指向a g.call(a); //this指向window
总结
箭头函数不是匿名函数的语法糖,而是一个补充匿名函数机制的新的“匿名函数”。箭头函数不会的this指向不会隐形绑定(显式绑定也没用),解释器的处理是固定的:this总指向定义时context。
越是自由的语言,坑点是真的多。遇到问题,要踩遍所有坑点才放心;对比隔壁静态语言,都放在静态编译多好呀 😦 狗头