下面是用 ES6 语法
写的类式继承
图1:ES6 的面向对象高仿语法
很完美、很 Java 有木有 ...
这 ... 也再次证明了 ...
JavaScript 确实很牛逼 ...
但是
虽然我们可以用
class、extends、new、instanceof
写出高仿 Java 的类与继承
但它的原理、特性
与 Java 完全是两码事
下面进入今天的正题
JavaScript 没有类,只有对象!所谓的类、继承都只是借助 JavaScript 原型链机制模拟实现的。事实上 ES6 中新加入的 class、extends、super 关键字也只不过是现有原型链特性的语法糖,只是起到了简化编程的作用。
1. 函数、构造函数、new
JavaScript 函数:
图2:用 Function 生产一个函数
图3:查看函数对象拥有的属性与方法
“构造函数”与 new:
图4:演示构造函数与 new
实际上,NothingSpecial 和你程序中的其他函数没有任何区别。函数本身并不是构造函数,然而,当你在普通的函数调用前面加上 new 关键字之后,就会把这个函数调用变成一个“构造函数调用”。实际上,new 会劫持所有普通函数并用构造对象的形式来调用它。
——《你不知道的 JavaScript (上卷)》p150
换句话说,在 JavaScript 中对于“构造函数”最准确的解释是,所有带 new 的函数调用。
——《你不知道的 JavaScript (上卷)》p150
2. prototype、[[Prototype]]、__proto__
[[试试牛刀]]
如果都能给出答案
你已经掌握 JavaScript 的终极奥义
前端扛把子就是你
图5:原型小测试
[[原理分析]]
[[答案公布]]
3. .constructor 属性不可靠、不安全
.constructor 从哪来?
.constructor 为啥不靠谱?
Car.prototype 的 .constructor 属性只是 Car 函数在声明时的默认属性。如果你创建了一个新对象并替换了函数默认的 .prototype 对象引用,那么新对象并不会自动获得 .constructor 属性。
.constructor 并不是一个不可变属性。它是不可枚举的,但是它的值是可写的。你可以任意对其赋值。所以 .constructor 是一个非常不可靠并且不安全的引用。
.constructor 该如何利用?
虽然 .constructor 属性不可靠也不安全,但是它可以很方便的用于运行时对象的内省。可以重置 .constructor 属性使其指向期望的构造函数而不会影响其功能,这是由于该属性主要是用于提供对象的信息。
——《JavaScript 模式》
图8:使用.constructor属性的正确姿势
4. instanceof 的本质是什么?
在传统面向类环境中(例如:Java),检查一个实例的继承祖先通常使用 instanceof 关键字实现;
Java 怎么用 instanceof 的?
Java 有的,JavaScript 也有
但是
不要被表象蒙蔽
john instanceof Employee john instanceof Person
JavaScript 中,instanceof 操作符的左操作数是一个普通的对象,右操作数是一个函数。instanceof 回答的问题是:在 john 的整条 [[Prototype]] 链中是否有 Employee.prototype 指向的对象?
Employee.prototype.isPrototypeOf(john) Person.prototype.isPrototypeOf(john)
ES6 的 .isPrototypeOf 接口还原了 instanceof 操作符的本质含义;
举例说明
5. 模拟类式继承的常见方法
5.1. 原型链继承
5.2. 借用构造函数
5.3. 组合继承*(原型链继承 + 借用构造函数)
5.4. 共享原型
5.5. 临时构造函数
上面几种模拟继承的方法各有利弊
篇幅有限,水平一般
建议大家
6. ES6 新特性
ES6 提供了更接近传统语言的语法,通过 class 关键字可以定义类。ES6 中的 class可以看作只是一个语法糖,新的 class 写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
——《ES6 标准入门》
参考:
《JavaScript 启示录》 《JavaScript 忍者秘籍》 《JavaScript 模式》 《你不知道的 JavaScript (上卷)》 《JavaScript 面向对象精要》 《JavaScript 面向对象编程指南》 《深入理解 ES6》 《ES6 标准入门》 《JavaScript 框架设计》