Appearance
手写 call / apply / bind
来源:
js/实现/this.html、js/实现/new.html,手写实现call、apply、bind改变 this 指向。
共享执行环境:代码块间可互相访问变量和函数定义
此页面启用共享 iframe 模式,所有代码块共享执行环境。
速查导航
- 核心思路 — 给目标对象临时挂方法
- 手写 call — 逐个传参
- 手写 apply — 数组传参
- 手写 bind — 返回函数,延迟执行
- bind 与 new — 构造函数场景的 this 判断
- Function.prototype.apply.call — 安全调用原生方法
核心思路
call/apply/bind 本质上就是:给目标对象临时添加一个方法并执行,执行完再删除。
js
简单理解:
call/apply/bind就是普通方法,跟join等没区别- 执行函数时将函数内的
this指向第一个参数,同时传入其余参数- 第一个参数为
null/undefined时指向window
手写 call
js
手写 apply
与 call 区别:第二个参数是数组。
注意: 原生
apply会检查第二个参数是否为数组或类数组对象,传入非数组会报TypeError。手写版如果没有做类型判断,传入非数组不会报错但行为可能不符合预期。
js
手写 bind
bind 与 call/apply 不同:不立即执行,返回一个改变了 this 的函数。
参数两次截取:
- 绑定时截取:
...args获取bind(fn, arg1, arg2)中的参数- 执行时截取:
...arg获取返回函数被调用时的参数- 最终合并:
[...arg, ...args]执行时参数在前,绑定时参数在后
js
js
bind 与 new
当 bind 返回的函数被 new 调用时,this 指向实例而非 con。
this instanceof fBound ? this : con的含义:
new调用时this指向实例 →instanceof为true→ 用this(忽略con)- 普通调用时
this指向window→instanceof为false→ 用con
如果写反了会怎样?
- 改成
this instanceof fBound ? null : con:new调用时this被替换为null,实例变成空对象,无法获得绑定函数上的属性(如habit)- 普通调用不受影响,仍指向
con
原型链处理:
fNOP.prototype = this.prototype; fBound.prototype = new fNOP();- 用中间函数
fNOP避免直接修改fBound.prototype = this.prototype导致原函数原型被污染- 让
bind返回的函数作为构造函数时,实例能继承原函数原型上的属性
Function.prototype.apply.call
当目标函数自身定义了 apply 方法时,不能直接用 fn.apply(obj, args),因为会调用到函数自己的 apply 而非原生的。
js
总结:
Function.prototype.apply.call(fn, thisArg, args)是一种安全的调用方式,确保使用原生的apply而非对象自定义的方法。