你不知道的 JavaScript 系列上( 53 ) - 原型继承

function Foo(name) {
  this.name = name;
}
Foo.prototype.myName = function () {
  return this.name;
}

var a = new Foo('a');
a.myName(); // 'a'

JS 中太多模拟类的行为方法,但是如果没有 “继承” 机制的话, JS 中的类就只是一个空架子。a 可以继承 Foo.prototype 并访问 Foo.prototype 的 myName() 函数。通常被称作原型继承的机制。

 

下面这段代码使用的就是典型的 “原型风格”

 

function Foo(name) {
  this.name = name;
}
Foo.prototype.myName = function () {
  return this.name;
}

function Bar(name, label) {
  Foo.call(this, name);
  this.label = label
}
// 我们创建一个新的 Bar.prototype 对象并关联到 Foo.prototype
Bar.prototype = Object.create(Foo.prototype);

// 注意,现在没有 Bar.prototype.constructor 了
// 如果你需要这个属性的话可能需要手动修复一下它

Bar.prototype.myLabel = function() {
  return this.label
}
var a = new Bar('a', 'obj a');
a.myName(); // 'a'
a.myLabel(); // 'obj a'

这段代码的核心部分是 Bar.prototype = Object.create(Foo.prototype); 创建一个新的 Bar.prototype 对象并把它关联到 Foo.prototype。

 

注意下面两种方式是常见的错误做法,实际上它们都存在一些问题:
// 和你想要的机制不一样
Bar.prototype = Foo.prototype;

// 基本上满足你的需求,但是可能会产生一些副作用:
Bar.prototype = new Foo();

Bar.prototype = Foo.prototype 并不会创建一个关联到 Bar.prototype 的新对象,它只是让 Bar.prototype 直接引用 Foo.prototype,因此赋值语句会直接修改 Foo.prototype 对象本身。

Bar.prototype = new Foo() 的确会创建一个关联到 Bar.prototype 的新对象。但是它使用构造函数调用,如果函数 Foo 有一些副作用,就会影响 Bar() 的后代,后果不堪设想

Object.create 唯一的缺点就是需要创建一个新对象然后把旧对象抛弃掉,不能直接修改已有的默认对象。

 

ES6 添加了辅助函数 Object.setPrototypeOf(...) 可以标准并且可靠的方法来修改关联
// ES6 之前需要抛弃默认的 Bar.prototype
Bar.prototype = Object.create(Foo.prototype);


// ES6 开始可以直接修改现有的 Bar.prototype
Object.setPrototypeOf(Bar.prototype, Foo.prototype);

如果忽略掉 Object.create 带来的轻微性能的损失(抛弃的对象需要进行垃圾回收),它实际上比 ES6 及其之后的方法更短而且可读性更高。不过无论如何,这是两种不同的语法。



posted @ 2020-04-16 06:52  wzndkj  阅读(129)  评论(0编辑  收藏  举报