Open felix-cao opened 5 years ago
为什么call 和 apply不能解决this的隐式丢失?而bind可以,是因为bind返回一个绑定好this的函数吗?请帮忙解决一下这个疑问,谢谢
为什么call 和 apply不能解决this的隐式丢失?而bind可以,是因为bind返回一个绑定好this的函数吗?请帮忙解决一下这个疑问,谢谢
谢邀,特殊原因,回复慢了。
JavaScript
的 Function.property.call()
、Function.property.apply()
、Function.property.bind()
,存在的意义都是改变 this
的指向,这是函数式编程的非常重要的特点,函数式编程就是以函数为抽象单元和行为单元,有了这三个,使得 JavaScript
的函数式编程灵活强大。
call, apply
与 bind
的区别为:
call
, apply
修正 this
指向后立即执行函数bind
返回一个函数,而不立即执行您所提的问题并没这个说法。有没有‘场景’,可复现?
为什么call 和 apply不能解决this的隐式丢失?而bind可以,是因为bind返回一个绑定好this的函数吗?请帮忙解决一下这个疑问,谢谢
谢邀,特殊原因,回复慢了。
JavaScript
的Function.property.call()
、Function.property.apply()
、Function.property.bind()
,存在的意义都是改变this
的指向,这是函数式编程的非常重要的特点,函数式编程就是以函数为抽象单元和行为单元,有了这三个,使得JavaScript
的函数式编程灵活强大。
call, apply
与bind
的区别为:
call
,apply
修正this
指向后立即执行函数bind
返回一个函数,而不立即执行您所提的问题并没这个说法。有没有‘场景’,可复现?
sorry,是我问的问题有些偏差,还请谅解,我看到 《你不知道的JavaScript上卷》中第二部分第二章2.2.3显示绑定小节中介绍到,显示绑定并不能解决this丢失绑定的问题,而硬绑定可以解决这个问题,这里不是很清楚,所以资讯一下您的见解。
- 我所理解的显示绑定
function foo () { console.log(this.name) } var obj = { name: 'objName' } foo.call(obj) // 显示绑定, 虽然绑定了this但是我还可以随便改变this的指向,所以作者在这里说不能解决this丢失绑定的问题。
- 硬绑定
function foo () { console.log(this.name) } var obj = { name: 'objName' } var bar = function () { foo.call(obj) } bar() // 这时候bar无论怎么绑定都会执行foo.call(obj),也就是绑死了,所以this就不会发生丢失问题,是不是这个意思。 bar.call(null) // objName
// 还有bind的实例 function foo () { console.log(this.name) } var obj = { name: 'objName' } var bar = foo.bind(obj) bar.call(null) // 还是会打印objName bar() // objName 这两种一个意思吧, 您在文中提到,bind返回一个绑定好this的函数,而上面的例子也是返回了一个绑定好了this的函数。所以这就是硬绑定 能解决this丢失的问题吧。
@ComponentTY
谢谢,非常好的一个 Case
, 您的例子更加说明了“JavaScript 是一门非常灵活的语言,以至于你永远都不知道是否已经真的掌握了这么语言”
我觉得要结合词法作用域和执行上下文栈去理解,把您的例子更改一下,方便理解
function foo () {
console.log(`this in ${arguments.callee.name}:`, this.name)
}
var obj = {
name: 'objName'
}
var bar = function () {
console.log(`this in ${arguments.callee.name}:`, this.name);
foo.call(obj) // 改变 foo 中的 this,注意这里访问了 obj, 指向的是 window.obj
}
var name = 'widowName'; // JavaScript 顶层变量的属性与全局变量是等价的, 即等价于 window.name
foo() // code1 不改变this 指向时
bar() // code2, 不改变bar 中的 this, 但改变 foo 中的 this
bar.call(null) // code3 改变 bar 中的 this, 也改变 foo 中的 this
code1
, 执行 foo()
时,找不到 this
,根据作用域链原则,向上查找,即 var name = 'widowName';
code2
, 执行 bar()
时,bar()
这个函数执行上下文的 this
指向 window
, 在这里执行 foo
函数的同时更改了 foo
函数内部的 this
指向,指向 obj
, 但 bar
内部并没有定义 obj
这个变量,所以向上搜索,code3
, 执行 bar()
时,同时更改了 bar
函数内的 this
指向, 在内部执行 foo
函数的同时也更改了 foo
函数内部的 this
指向再改下看看(主要想说明 bar
内部的变量 obj
):
function foo () {
console.log(`this in ${arguments.callee.name}:`, this.name)
}
var obj = {
name: 'objName'
}
var bar = function () {
obj = {
name: 'objName in bar'
}
console.log(`this in ${arguments.callee.name}:`, this.name);
foo.call(obj) // 改变 foo 中的 this, 这里 `bar` 函数内部有自己定义的 `obj`
}
var name = 'widowName';
foo()
bar()
bar.call(null)
再改下看看:
function foo () {
console.log(`this in ${arguments.callee.name}:`, this.name)
}
var bar = function () {
console.log(`this in ${arguments.callee.name}:`, this.name);
foo.call(this)
}
var name = 'widowName';
foo()
bar()
bar.call(null)
在 《JavaScript 之 this 绑定/指向》 一文中,我们聊了
this
绑定的四种场景,在“隐式绑定”场景中,经常会导致this
丢失的情形,本篇主要来聊一聊这种情况。隐式丢失是指被隐式绑定的函数丢失绑定对象,这种情况容易出错却又常见,为什么呢?因为
JavaScript
中函数可以作为参数,也可以作为返回值被四处传播,传着传着就丢了this
指向,被传的晕头转向了。下面来聊一聊
this
丢失的场景一、函数别名
我们将 《JavaScript 之 this 绑定/指向》 第四节中给的案例代码改一下:
我们只是给
person.getAge
换了个getPersonAge
名字。就是这个换名赋值语句操作出事的,因为在javascript
中,函数是对象,对象之间是引用传递而不是传值传递,因此这里的赋值实际上是这样的:getPersonAge = person.getAge = getAge
getPersonAge = getAge
,getPersonAge
是getAge
的别名,最终引用的是getAge
函数的地址,而与person
对象无关了,这就是所谓的丢失上下文。二、 函数作为参数进行参数传递
我们知道
JavaScript
的参数传递时传参而不时传值。上面的代码 把
obj.foo
当作参数传递给bar
函数时,在bar
函数里,有隐式的函数赋值fn=obj.foo
。与上例类似,只是把foo
函数赋给了fn
,而fn
与obj
对象则毫无关系。三、 setTimeout 等内置函数
其实也是把 obj.foo 作为参数传递给 setTimeout() 函数
若想获得obj对象中的a属性值,可以将obj.foo函数放置在定时器中的匿名函数中进行隐式绑定
或者也可以使用bind方法将foo()方法的this绑定到obj上
四、 间接引用
函数的"间接引用"一般都在无意间创建,最容易在赋值时发生,会造成隐式丢失
五、其他
5.1、理解一下对象的存储
在
javascript
引擎内部,obj
和obj.foo
储存在两个内存地址,简称为M1
和M2
。只有obj.foo()
这样调用时,是从M1
调用M2
,因此this
指向obj
。但是,下面三种情况,都是直接取出M2
进行运算,然后就在全局环境执行运算结果(还是M2
),因此this
指向全局环境5.2、document.getElementById
document.getElementById
这个方法名有点长,程序员有个通病:喜欢用个短的函数来代替它现在,让下面的代码在浏览器中跑一把试试:
结果我们发现,这段代码抛出了异常。
这是因为
JavaScript
引擎的document.getElementById
方法的内部实现中需要用到this
,这个this
本来是被期望指向document
,当getElementById
作为document
对象的属性被调用时,方法内部的this
确实是指向document
,但当用getId
来引用document.getElementById
方法之后,再调用getId
,情况就不一样了,this
被丢失了,函数内部的this
指向了window
,而不是document
。说白了,就是 函数别名 引起的。现在知道原因了,我们利用
apply
把document
当做this
传入 getId函数,从而修正this
指向