这是我参与8月更文挑战的第26天,活动详情查看: 8月更文挑战”
前言
这几天刷题目看文章的时候,发现手写apply、call、bind代码,我能看懂了。而且觉得缺少些有趣的细节。所以有了这篇文章,一方面是梳理这些函数的手写,一方面也是回顾JavaScript执行机制。
手写apply
call方法除了执行函数传参形式不太一样,其他地方都一样。
一些破局点:
-
关于apply中的this指向。this指向apply方法的调用者,即执行函数本身。例如已声明函数f,
f.apply(context, array)
。此时,apply函数中的this指向了f -
apply的功能就是将函数f中的this指向
context
并执行函数。this指向只有三种途径 -
函数执行,
this
指向全局对象。 -
作为对象的方法被调用,
this
指向对象。 -
new
+ 构造函数,this
指向新生成的对象 -
想改变函数中的
this
指向,明显是通过第二条途径,于是在对象上context
添加一个方法f就很自然 -
执行完之后再删除对象上的f方法,返回函数的返回值。一切又完好如初
Function.prototype.apply = function (context, array) {
context || (context=window)
let func = this
let caller = Symbol(“caller”)
context[caller] = func
let res = contextcaller delete context[caller]
return res
}
手写call
与上面相同,注意接收的参数的写法
Function.prototype.call = function (context, ...args) {
context || (context=window)
let func = this
let caller = Symbol("caller")
context[caller] = func
let res = context[caller](...args)
delete context[caller] return res
}
复制代码
手写bind
bind返回的是一个函数,而非函数的执行结果
-
这里通过
this instanceof F
判断是否是new的调用,并通过闭包保存了父作用域中的this还是很有味道的。Function.prototype.bind = function(context, …args) {
if (typeof this !== ‘function’) {
throw new Error(“Type Error”);
}
// 保存this的值
var self = this;return function F() {
// 考虑new的情况
if(this instanceof F) {
return new self(…args, …arguments)
}
return self.apply(context, […args, …arguments])
}
}
如果想参考一个复杂的版本
可以参考MDN bind之Polyfill
最后附上一个自己填补了一些异常校验的手写apply。
这里并没有考虑到new 操作符。主要是因为浏览器中实现的使用new去调用apply,浏览器会报一个f.apply is not a constructor的错误,而自己通过Function.prototype.apply去实现的apply,就算把prototype的constructor置为空,也不会走向这里,感觉这里深入下去阻力大了一些,目前的功力还是不够。
// 附加一个考虑更多异常情况的版本
Function.prototype.apply = function (context, array) {
if(typeof context !=="object"&&typeof context !=="function"){
throw new TypeError('context is not a object')
}
if(Object.getPrototypeOf(array)[@@iterator]){
throw new Type Error("CreateListFromArrayLike called on non-object")
}
context||(context=window)
let func = this
let caller = Symbol("caller")
context[caller] = func
let res = context[caller](...array)
delete context[caller]
return res
}
复制代码
PS:手写代码试了试水,感觉到因为自己JS相关体系还是有明显薄弱处,以及对底层规范的畏惧感,导致了这并不是很高效的梳理。如果,抱着学习的心态,又难以谈出自己的想法,有点人云亦云的感受。只能以学习为主,围绕一两个感兴趣的点进行适当的挖掘,保护自己自信的同时也能有所收获。把注意力放在JS的知识体系的拾遗上,而不是过度研究细节。
简而言之,按自己目前的水平,理应把注意力放在面的扩展上,不要把时间过度浪费在对于某些点的深入探究。日拱一卒,而非毕其功于一役,心态要好,要更冷静一些。