以上是基础的 this
绑定问题, 在 obj
环境下执行 foo()
, this
绑定的是 obj
, 在全局环境下, fooo()
则绑定了系统环境.
如何让 fooo
绑定 obj
呢? 可以使用 Function.prototype.bind()
强行绑定 obj
:
再看一个现象:
看起来行得通, 但是获取不到 width
和 height
?
简化模型如下:
这里的 this
指向的是 parent
, 而非 obj
, 在传统 js 中是没有 block scope
的, 只有 function scope
和 global scope
, 证明如下:
在 es6
中 let, const
是 block scope
的.
将 foo
改为箭头函数, 这里就会发生变化, 第一个在 obj
环境下执行结果是 undefined
. 因为箭头函数是没有 this
的, this
虽然指向当前 scope, 但不包括 arrow function
.
经过 babel 转译是这样的:
在 vue 官方文档中有这么一段:
不要在选项属性或回调上使用箭头函数,比如
created: () => console.log(this.a)
或vm.$watch('a', newValue => this.myMethod())
。因为箭头函数是和父级上下文绑定在一起的,this
不会是如你所预期的 Vue 实例
可以将箭头函数理解为 created: this
, 那么这里的 this
指向的不会是 Vue 实例.
vue 实例的 data
共享外部 data
, 它们指向同一个 obj
.
再看一个比较明显的例子:
这里 foo
函数内的 a.f
因为是个箭头函数, 被转译后结果应该是这样:
而 bar
内的 a.f
是个传统js函数, 有自己的闭包, 在解析 this
时候会先去自己的环境查找, 即 x
为 2.
baz
在非 strict mode
下, 自己的环境找不到会去全局环境中查找, 对比 qux
可以看到现象.
所以在 component
章节中强调了 data
必须是一个函数:
防止多个 component
共享同一份 obj
.
Until arrow functions, every new function defined its own this value […]. This proved to be annoying with an object-oriented style of programming. Arrow functions capture the this value of the enclosing context […]
这里为什么 handleClick
没有 bind
但 handleClick2
却有 bind
呢?
让我们简化下模型:
这里有点奇怪, Person
的原型上没有 bar
, 如果原型上没有 bar
, 那么实例 Bob
的 bar
哪里来的呢?
通过 Babel 转移:
可以发现实例 Bob
在初始化时候定义了 bar
这个属性, 绑定了实例的属性, 而非原型上的方法.
再回过来看上面的 react
例子, 这么理解下:
对应的可以写一个:
这就可以解释了在 react
中手动绑定 this
的原因, 在 ele.foo()
会去 ele
的环境下查找 id
, 而 ele
并没有在 Cpnt
原型链上, 没有 id
属性.
Reference
- Methods in ES6 objects: using arrow functions
- ‘this’ inside object
- The Difference Between Function and Block Scope in JavaScript
- This is why we need to bind event handlers in Class Components in React
- Arrow Functions in Class Properties Might Not Be As Great As We Think
- Why do I have to .bind(this) for methods defined in React component class, but not in regular ES6 class