this
is not what you think it is.
考虑这样一则代码:
const A = function () {
this.member = 'a';
this.doSomething = () => {
console.log(this.member);
};
};
const B = function () {
this.a = new A();
this.doSomething = this.a.doSomething;
};
const b = new B();
b.doSomething(); // -> a
看起来很正常,或者表面上很正常,对吧。
但是再考虑这样一则代码:
const A = function () {
this.member = 'a';
this.doSomething = function () {
console.log(this.member);
};
};
const B = function () {
this.a = new A();
this.doSomething = this.a.doSomething;
};
const b = new B();
b.doSomething(); // -> undefined
为什么二者的行为不一样?
在 JS 中,this
的行为和其他「正常」的语言的行为都不一样。this
的值在调用时确定而不是根据声明时的位置确定。
一个简单的例子:
function foo() {
this.a = 'bar';
}
let a = {};
a.foo = foo;
foo(); // `this` is global
a.foo(); // `this` is `a`
foo.apply(a); // `this` is `a`
foo.bind(a)(); // `this` is `a`
其实也不算非常奇怪,考虑到许多语言的面向对象支持也是类似 fun(ctx, ...args)
这样的形式。可以把 ctx.fun
当作 fun(ctx,
的语法糖。
如果仅限如此,倒是还行。不过比较难受的是回调函数的设计:
let obj = new (function () {
this.member = 'a';
setTimeout(function () {
this.member = 'b';
}, 1000);
})();
这样做并不会更改 obj.member
的值,因为第 4 行的 this
被绑定到 setTimeout
了。要正确地实现,必须借助一个中间变量:
let obj = new (function () {
let instance = this;
this.member = 'a';
setTimeout(function () {
instance.member = 'b';
}, 1000);
})();
看起来不甚美观。所以,我们又有了箭头函数 () => {}
.
箭头函数是一个典型的匿名函数。它的特性就是打破了上面所有 function
关于 this
的约定。简单而言,箭头函数的 this
绑定到更上一层的 this
. 一个简单的例子:
const foo = () => {
this.a = 'bar';
};
let a = {};
a.foo = foo;
foo(); // `this` is global
a.foo(); // `this` is still global
foo.bind(a)(); // `this` is still global
foo.apply(a); // `this` is STILL global
注意到第 8 行,此处的 this
不再是 a
, 而是全局。而且 this
是在声明时就被绑定的,不能通过 bind
, apply
或者 call
修改。
这样,如下代码就会正确绑定 this
:
let obj = new (function () {
this.member = 'a';
setTimeout(() => {
this.member = 'b';
}, 1000);
})();
箭头函数所在的的上一层是 function () {...}
, 从而 this
得以正确地从其继承。
但所有内嵌函数(为了兼容性或者其他原因)都使用 function (.) {...}
形式。这就意味着如下代码必须进行 .bind
:
const C = function () {
this.things = [1, 2, 3];
this.forEach = this.things.forEach.bind(this.things);
}
Your comments will be submitted to a human moderator and will only be shown publicly after approval. The moderator reserves the full right to not approve any comment without reason. Please be civil.