剑走偏锋的JavaScript原型链

ECMAScript 2015引入了类机制。为了保证最小的改动,JavaScript 通过原型模式来实现 OO 语义。

像 GoF 里介绍的原型模式,其实都是基于 Java 这样标准的面向对象语言,所需要的就是对原型对象的深拷贝。原型设计模式避免了工厂模式中复杂的创建者,也就是针对每种对象的工厂。

在 JavaScript 里一开始就有了原型。但这种原型是浅拷贝的,这基本上是基于效率的考虑。从这个机制上,JavaScript 的原型更接近于静态全局对象,比如 Scala 里的伴生对象。

基于这个机制,JavaScript 实现继承就和 Java 这样传统的面向对象语言没有特别大的区别,原型链和 JVM 中的动态分派(dispatch)是一致的,都需要沿着继承关系向上查找方法。当然 JVM 为了性能,会通过建表的方式避免进行链式搜索,这就是虚方法表。JavaScript 还没有类似的机制,可能对于浏览器而言,对于内存的需求要优于时间的需求吧。

接下来看看这种设计的巧妙。比如,我希望模拟一种类似于 Java 的类[1]

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
复制代码

这里this绑定的执行上下文是一个尚未完全初始化的Rectangle。对于弱类型的 JavaScript 而言,这种暴露是完全被接受的。对于静态方法,其实就相当于使用call进行调用。

对于this,类语法和此前函数的语法有一个区别:

class Animal { 
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined
复制代码

不论在严格还是非严格模式下,上述代码都是undefined。但如果是函数嵌套的调用,那么会在非严格模式下绑定到global对象,即window。这是因为class中的代码从语法上必须严格绑定到类上。

其实上面和原型链没有太大关系,但他体现了 JavaScript 这一套设计的历史遗留。class是 ES6 才引入的,而new早已出现。相比之前 JavaScript 复杂的对象创建方式(可以参考《JavaScript 高级程序设计》),ES6 的类设计是一个重要的改进。

调用new Rectangle()的结果是继承了函数中的prototype到对象中(注意到__proto__这个字段并不在规范中,应该使用Object.getPrototypeOf)。这一方式实现了类似于 Java 静态域的效果。所以如果使用Array.prototypeProp = 3这样的写法,只是绑定值到Array这个构造方法,而不是原型中,无法通过实例对象来访问——但是Array.prototype.prototypeProp = 3可以让每个实例进行访问。从对象的语义上,前者是不完备的,比如 Java 就允许通过实例对象访问静态域。

最后是如何实现继承的链式结构。这一点原型链的实现很简单,只需要让每个原型中保存(这里保存的是指针)父类的原型即可,这也正是原型设计模式的思路。

和 JavaScript 奇怪的作用域链一样,基于原型的对象模型将很多概念和实现混杂在了一起,构成了前端学习的不必要困难之一。

参考资料


  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Prototype_methods ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值