关于class中的函数问题

为什么在class中的普通函数会挂在className.prototype下面,而箭头函数是挂在类的实例下面。举个例子吧

class F {
  handleX() {}
  handleY = () => {}
}

typeof F.prototype.handleX === 'function' // true
typeof F.prototype.handleY === 'function' // false 主要疑问点???

let ff = new F
typeof ff.handleY // function

阅读 2.5k
2 个回答

因为语法就是这样设计的呀,为啥要这样设计呢?TC39 的大佬们讨论出来的结果呀。

handleY 这个语法严格意义上还不是 ES 标准呢,现在还只是个处于 stage 3 的提案。https://github.com/tc39/proposal-class-fields 但 Babel 早就支持了所以大家用得都很欢快了,尤其是在 React 中。

这里的迷惑点是箭头函数和普通函数,其实不然啦。你所说的普通函数叫方法定义,这个是 ES6 class 中就有的,而箭头函数那个,这里的定义叫做字段定义。你把箭头函数换成一个其他的值,然后尝试去理解,你看,它是不是看起来就和 handleX 毛关系都没有?

class F {
  handleX() {}
  handleY = 1
}

其实这个等价于:

class F {
  handleX() {}
  constructor() {
    this.handleY = 1;
  }
}

看起来还是很迷惑?为什么并列写一块儿,一个是在 prototype 上,另一个是在实例上? 这个提案其实吵了很久了,现在基本没有逆转的余地了。所以你就记住就好了,method 定义在 prototype 上,field 定义在实例上。而题目中的 handleY 赋值一个箭头函数它其实是 field 定义,所以不会在原型上啦。

handleY = () => { console.log(this)}

其实输出你会发现这个 this 指向的是实例对象
而箭头函数内部的 this,记录的是词法环境下的 this,并不是运行时确定的,什么意思呢,就是当箭头函数声明时其内部的 this 值就已经确定了。一般情况下使用箭头函数时,按照声明时的环境,就可以知道其内部的 this 的值了,而且不能被改变的。而且你看输出是实例对象,表明其声明环境应该在实例化的过程中,其实这个是新语法,使用 =,而不是 : 号的,而类本质是是一个普通函数,你可以看看下面的例子

function F() {
  this.handler = () => { console.log(this)}
}
F.prototype.handler = () => { console.log(this)}

你是希望 this 指向实例化对象呢,还是指向外部的 this 呢,在严格模式下,如果是挂载在原型上 this 其实是 undefined,没有太多意义。可能你会想把实例化过程中去挂载原型,这样 this 也能指向实例对象,但是这种每次实例化都生成新的声明,就不用挂载到原型了,原型本来就是方便重用的。
你的上面代码可以通过 babel 的转码为

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

class F {
  constructor() {
    _defineProperty(this, "handleY", () => {});
  }

  handleX() {}

}

constructor 函数执行时的环境就是实例环境,这样箭头函数内部可以通过 this 访问实例对象,比用在原型更有实用价值。或者反过来想,如果这个是原型方法,但是 this 只是 undefined 或者是 window,那这个方法本质是没有意义的,可能你会说,可以不访问 this,那这个方法更应该放置的位置是静态方法

推荐问题
宣传栏