背景
在学习手写 call 的时候,需要在 this 显示绑定的对象 thisArg 上添加被调用的方法,然后通过 thisArg.xxx 的形式执行。这样虽然能实现 this 的显示绑定,但最后这个 thisArg 会被添加一个新的方法,改变了原来的结构。
手写 call 利用了函数 this 的隐式绑定:方法(或函数)被调用时,这个方法(或函数)内的 this 指向调用它的对象。由于函数也是对象,因此,
foo.call(thisArg[, arg1, arg2, ...])可以解读为:函数 foo 调用了 call 方法,而这个 call 方法内的 this 就指向了其调用者 foo;然后将 call 内的this(即 foo )作为方法(bar)添加到 thisArg 上;最后,thisArg 再调用 bar,就实现了将 foo 显式绑定到 thisArg。
script
Function.prototype.myCall = function (thisArg, ...args) {
const _this = thisArg
const func = this
_this.func = func // ①
Object.defineProperty(_this, 'func', {
value: func // ②
})
console.log(Object.getOwnPropertyDescriptor(_this, 'func'))
_this.func(...args)
delete _this.func
}
function foo(...args) { console.log(this, args) }
foo.myCall({ name: '张三' }, 1, 2, 3, 4, 5)
打印出来的结果:
奇怪了,按道理说 Object.defineProperty 的 configurable和enumerable 的默认值都为 false,为什么在这里打印出来却都是 true ?
最开始猜测是不是因为给的value值是一个函数(②处)?于是将value改为一个字符串试试,结果还是一样:configurable和enumerable 打印出来都是 true。
又猜测是不是因为 ① 处的问题,于是把这一行注释,然后再看结果,都为 false。恍然大悟,原来 ① 处的代码本来就是普通的赋值操作,相当于
Object.defineProperty(o, p, {
configurable: true,
enumerable: true,
value: xxx,
writable: true
})
② 只是修改了值而已,其他几个描述符依然是 true。所以最好的方式就是直接删掉①处的代码,更简单明了:
Function.prototype.myCall = function (thisArg, ...args) {
const _this = thisArg
const func = this
Object.defineProperty(_this, 'func', {
// 配置为 true 的作用是让下面 delete 操作符能删除 `func` 属性
configurable: true,
value: func // ②
})
_this.func(...args)
delete _this.func
}
function foo(...args) { console.log(this, args) }
foo.myCall({ name: '张三' }, 1, 2, 3, 4, 5)
运行结果如下: