一个原型链
- 通过 构造函数A 创建的实例A-instance的`[[proto]]`属性会自动指向 构造函数A的原型对象`prototype`,这种关系是在实例被创建时便自动创建的。
- 而我们的 构造函数A 的原型对象 `prototype` 本身就是一个 `Object`的实例。
JavaScript动态特性的副作用
function car(){this.broken = true;}const carA = new car();car.prototype.WhetherBroken = function(){return this.broken;}console.log(carA.WhetherBroken()); // output:car.prototype = { //line 1expensive: function(){return true; }}console.log(carA.WhetherBroken()); // output: trueconst carB = new car();console.log(carB.expensive()); // output: true;console.log(carB.WhetherBroken()); // Uncaught TypeError: carB.WhetherBroken is not a function复制代码
- 当我们在 line 1修改了构造函数car的原型,例carB时原型链的样子。
- 当我们对构造函数car原型上的属性和方法进行删改时, car的所有实例都可以访问新的方法或属性。
- 当我们完全更换构造函数car的原型时(如line1), 之前创建的car的实例会保留旧的原型的引用, 而在更换完原型之后通过构造函数创建的实例将会保留新的原型的引用。对新的原型上的属性的删改在旧的实例(保留旧的原型的引用的实例)上无法体现。
通过instanceof判断实例类型
function Car(){};const carA = new Car();console.log(carA instanceof Car); // line 1 output: trueCar.prototype = {}; // line 2console.log(carA instanceof Car); // line 3 output: false复制代码
- 用法: object instanceof constructor
- 原理: instanceof 通过判断构造函数constructor的prototype是否在object的原型链上。即检测右边函数原型是否在左侧对象的原型链上。
- line2 改变构造函数`Car`的原型之后原型链的样子
- 如图所示, 显然构造函数Car的原型是不在CarA的原型链上的
- 同时我们还发现, 尽管我们在 line 2 重新定义了构造函数Car的原型, 但是新定义的原型对象并没有consturctor属性, 而原来的原型对象的constructor属性仍然指向构造函数Car。
- 这就会产生如下问题:
console.log(carA.constructor === Car);// output:trueconsole.log(carA instanceof Car); // output:false复制代码
function Car(){}function miniCar(){}miniCar.prototype = new Car();// line 1let miniA = new miniCar();console.log( miniA.constructor === miniCar); // falseconsole.log( miniA instanceof miniCar); //trueconsole.log( miniA.constructor); //Car复制代码
- 由于javascript 的垃圾回收机制, 实际情况应是如下图所示
- 为了解决上述问题, 我们应该在上述代码 line1 之后设置新原型的constructor属性
function Car(){}function miniCar(){}miniCar.prototype = new Car();// line 1Object.defineProperty(miniCar.prototype, 'constructor', {enumerable: false,value: miniCar,writable: true}) // added linelet miniA = new miniCar();console.log( miniA.constructor === miniCar); // trueconsole.log( miniA instanceof miniCar); //trueconsole.log( miniA.constructor); //miniCar复制代码
function SuperClass(){};function SubClass(){};SubClass.prototype = new SuperClass();Object.defineProperty(SubClass.prototype, 'constructor', {enumerable: false,value: SubClass,writable: true})let instance = new SubClass();复制代码
ES6 Class
- 由于ES5实现原型继承的过程繁琐复杂, ES6引入class语法糖, 来模拟类继承。 但其底层仍然是基于原型的实现。
// before ES6function Car(name){ //构造函数this.name;}Car.showName = function(car){//静态方法return car.name;}Car.prototype.run = function(){ //原型方法return true;}function miniCar(){};miniCar.prototype = new Car('BMW');Object.defineProperty(SubClass.prototype, 'constructor', {eumerable: false,value:SubClass,writable: true,})// after ES6class Car {constructor(name){ // 构造函数this.name; }// 静态方法static showName(car){return car.name; }// 如下为原型方法 run(){return true; }}// 通过 extends 关键字来实现继承。class miniCar extends Car{//...}复制代码