-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【进阶3-1期】JavaScript深入之史上最全--5种this绑定全面解析 #20
Comments
上面的思考题解代码1错了 checkscope(); |
谢谢提醒,已更新 |
setTimeout( obj2.foo, 10 ); // name: obj . 这个为什么会造成绑定丢失,是setTimeout的缘故吗 |
因为 this 的指向发生了变化, |
明白了,就像循环里套了setTimeout输出最后也会发生变化。想起了setTimeout常出的面试题。谢谢 |
并没有更新啊 |
var scope = "global scope"; checkscope(); 这个例子不存在闭包吧,执行checkscope函数的时候f函数还没有被回收,这样的话scope取值是从作用域链中(或者说是执行上下文的外部引用中获取的)获取的 |
有示例吗?call和apply都调用完函数了,怎么还有绑定丢失问题?? |
作者您好,“显示绑定无法解决丢失绑定问题“能否举个例子呢? |
this
的绑定规则总共有下面5种。1 调用位置
调用位置就是函数在代码中被调用的位置(而不是声明的位置)。
查找方法:
分析调用栈:调用位置就是当前正在执行的函数的前一个调用中
使用开发者工具得到调用栈:
设置断点或者插入
debugger;
语句,运行时调试器会在那个位置暂停,同时展示当前位置的函数调用列表,这就是调用栈。找到栈中的第二个元素,这就是真正的调用位置。2 绑定规则
2.1 默认绑定
undefined
。只有函数运行在非严格模式下,默认绑定才能绑定到全局对象。在严格模式下调用函数则不影响默认绑定。2.2 隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用。
被隐式绑定的函数特定情况下会丢失绑定对象,应用默认绑定,把this绑定到全局对象或者undefined上。
参数传递就是一种隐式赋值,传入函数时也会被隐式赋值。回调函数丢失this绑定是非常常见的。
2.3 显式绑定
通过
call(..)
或者apply(..)
方法。第一个参数是一个对象,在调用函数时将这个对象绑定到this。因为直接指定this的绑定对象,称之为显示绑定。显示绑定无法解决丢失绑定问题。
解决方案:
创建函数bar(),并在它的内部手动调用foo.call(obj),强制把foo的this绑定到了obj。这种方式让我想起了借用构造函数继承,没看过的可以点击查看 JavaScript常用八种继承方案
典型应用场景是创建一个包裹函数,负责接收参数并返回值。
创建一个可以重复使用的辅助函数。
ES5内置了
Function.prototype.bind
,bind会返回一个硬绑定的新函数,用法如下。JS许多内置函数提供了一个可选参数,被称之为“上下文”(context),其作用和
bind(..)
一样,确保回调函数使用指定的this。这些函数实际上通过call(..)
和apply(..)
实现了显式绑定。2.4 new绑定
构造函数
只是使用new
操作符时被调用的普通
函数,他们不属于某个类,也不会实例化一个类。Number(..)
)在内的所有函数都可以用new
来调用,这种函数调用被称为构造函数调用。使用
new
来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。[[Prototype]]
连接。this
。new
表达式中的函数调用会自动返回这个新对象。使用
new
来调用foo(..)
时,会构造一个新对象并把它(bar
)绑定到foo(..)
调用中的this。手写一个new实现
使用这个手写的new
代码原理解析:
1、用
new Object()
的方式新建了一个对象obj
2、取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以
arguments
会被去除第一个参数3、将
obj
的原型指向构造函数,这样obj
就可以访问到构造函数原型中的属性4、使用
apply
,改变构造函数this
的指向到新建的对象,这样obj
就可以访问到构造函数中的属性5、返回
obj
3 优先级
在
new
中使用硬绑定函数的目的是预先设置函数的一些参数,这样在使用new
进行初始化时就可以只传入其余的参数(柯里化)。4 绑定例外
4.1 被忽略的this
把
null
或者undefined
作为this
的绑定对象传入call
、apply
或者bind
,这些值在调用时会被忽略,实际应用的是默认规则。下面两种情况下会传入
null
apply(..)
来“展开”一个数组,并当作参数传入一个函数bind(..)
可以对参数进行柯里化(预先设置一些参数)总是传入
null
来忽略this绑定可能产生一些副作用。如果某个函数确实使用了this,那默认绑定规则会把this绑定到全局对象中。安全的做法就是传入一个特殊的对象(空对象),把this绑定到这个对象不会对你的程序产生任何副作用。
JS中创建一个空对象最简单的方法是**
Object.create(null)
**,这个和{}
很像,但是并不会创建Object.prototype
这个委托,所以比{}
更空。4.2 间接引用
间接引用下,调用这个函数会应用默认绑定规则。间接引用最容易在赋值时发生。
4.3 软绑定
new
除外),防止函数调用应用默认绑定规则。但是会降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改this。使用:软绑定版本的foo()可以手动将this绑定到obj2或者obj3上,但如果应用默认绑定,则会将this绑定到obj。
5 this词法
ES6新增一种特殊函数类型:箭头函数,箭头函数无法使用上述四条规则,而是根据外层(函数或者全局)作用域(词法作用域)来决定this。
foo()
内部创建的箭头函数会捕获调用时foo()
的this。由于foo()
的this绑定到obj1
,bar
(引用箭头函数)的this也会绑定到obj1
,箭头函数的绑定无法被修改(new
也不行)。ES6之前和箭头函数类似的模式,采用的是词法作用域取代了传统的this机制。
代码风格统一问题:如果既有this风格的代码,还会使用
seft = this
或者箭头函数来否定this机制。bind(..)
,尽量避免使用self = this
和箭头函数。上期思考题解
代码1:
代码2:
上面的两个代码中,
checkscope()
执行完成后,闭包f
所引用的自由变量scope
会被垃圾回收吗?为什么?解答:
checkscope()
执行完成后,代码1中自由变量特定时间之后回收,代码2中自由变量不回收。首先要说明的是,现在主流浏览器的垃圾回收算法是标记清除,标记清除并非是标记执行栈的进出,而是从根开始遍历,也是一个找引用关系的过程,但是因为从根开始,相互引用的情况不会被计入。所以当垃圾回收开始时,从Root(全局对象)开始寻找这个对象的引用是否可达,如果引用链断裂,那么这个对象就会回收。
闭包中的作用域链中 parentContext.vo 是对象,被放在堆中,栈中的变量会随着执行环境进出而销毁,堆中需要垃圾回收,闭包内的自由变量会被分配到堆上,所以当外部方法执行完毕后,对其的引用并没有丢。
每次进入函数执行时,会重新创建可执行环境和活动对象,但函数的
[[Scope]]
是函数定义时就已经定义好的(词法作用域规则),不可更改。checkscope()
执行时,将checkscope
对象指针压入栈中,其执行环境变量如下执行完毕后出栈,该对象没有绑定给谁,从Root开始查找无法可达,此活动对象一段时间后会被回收
checkscope()
执行后,返回的是f
对象,其执行环境变量如下此对象赋值给
var foo = checkscope();
,将foo
压入栈中,foo
指向堆中的f
活动对象,对于Root
来说可达,不会被回收。如果一定要自由变量
scope
回收,那么该怎么办???很简单,
foo = null;
,把引用断开就可以了。本期思考题
依次给出console.log输出的数值。
参考
进阶系列目录
交流
进阶系列文章汇总如下,内有优质前端资料,觉得不错点个star。
我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!
The text was updated successfully, but these errors were encountered: