为什么 Object.defineProperty 配置 value 描述符时,其他描述符都为 true ?

62 阅读2分钟

背景

在学习手写 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)

打印出来的结果:

image.png

奇怪了,按道理说 Object.definePropertyconfigurable和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)

运行结果如下:

image.png