ES6中class的prototype引用不可修改2017-07-27

今天在一个前端微信群里有人提出了这样一个问题:

class A {
  say() {
    console.log('i am a');
  }
}

// 直接覆盖A.prototype无效
A.prototype = {
  say() {
    console.log('i am b');
  }
};

let a  = new A();
a.say();
// => i am a

通过ES6class创建一个类,然后修改其prototype的引用是无效的。如果用ES5的方式,则可以随意修改:

function A1() {}
A1.prototype = {
  say: function() {
    console.log('i am a1');
  }
};
A1.prototype = {
  say: function() {
    console.log('i am b1');
  }
};
var a1  = new A1();
a1.say();
// => i am b1

可见ES6class的实现跟function相比在这一点上是截然不同的

从表现上来看,通过class声明的类的prototype是“不可写”的,我们知道Object.defineProperty方法可以设置对象的某个属性是否可写,例如:

let obj = {};
Object.defineProperty(obj, 'prop1', {
  value: 1, // 属性值
  writable: false // 是否可写
});
console.log(obj.prop1); // => 1
obj.prop1 = 2;
console.log(obj.prop1); // => 1

可以看到这样定义(writable: false)的obj.prop1对其赋值是无效的

那么如何判断一个对象的某个属性是不是可写呢?很简单:

class AnotherA {
  say() {
    console.log('i am another a');
  }
}
let prototypeDescriptor = Object.getOwnPropertyDescriptor(AnotherA, 'prototype');
console.log(prototypeDescriptor);
// => {value: Object, writable: false, enumerable: false, configurable: false}

可以看到class声明的类的prototypewritablefalse,所以对其prototype再次赋值是无效的,当然可以通过AnotherA.prototype.say = ...对其成员进行修改

经过查阅ECMA2015规范也证实了这一点:

es6-class-prototype1

上图是ES6中关于class的定义方案,在第16步中,调用MakeConstructor方法,注意第二个参数为false,然后再看下MakeConstructor这个方法:

es6-class-prototype2

可以看到,正是第二个参数决定了prototype是否可写

有兴趣的朋友可以再查一下function在这一步是怎么实现的,你会发现同样也调用了MakeConstructor方法,但是第二个参数是true

参考资料:


EOF


分享

评论