> 文章列表 > javascript 原型和继承

javascript 原型和继承

javascript 原型和继承

javascript 的原型

JS 的引用类型会内置一个特殊的属性 prototype。默认的 prototype 是 object 类型的,是引用类型。既然默认的 prototype 是 object 类型的,那么 prototype 也会有一个原型,并且指向 object 的原型。function 的原型可直接访问,object 的不行。

function SuperType(){};SuperType.age=18;SuperType.prototype.color=["red","blue"];var t1=new SuperType ();t1.name="hh";var t2=new SuperType ();t2.qq="aa";

每个对象(除 null 外)都会有的属性,叫做proto,这个属性会指向该对象的原型。

每个原型都有一个 constructor 属性,指向该关联的构造函数

function Person() {
}
console.log(Person===Person.prototype.constructor)  //truefunction Person() {}var person = new Person();console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // truefunction Person() {}
var person = new Person();
console.log(person.constructor === Person); // true

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止

function Person() {}Person.prototype.name = 'Kevin';var person = new Person();person.name = 'Daisy';
console.log(person.name) // Daisydelete person.name;
console.log(person.name) // Kevinconsole.log(Object.prototype.__proto__ === null) // true

原型链是什么

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。
就是实例对象和原型对象之间的链接,每一个对象都有原型,原型本身又是对象,原型又有原型,以此类推形成一个链式结构.称为原型链。
Javascript 是面向对象的,每个实例对象都有一个_proto_属性,该属性指向它的原型对象,这个实例对象的构造函数有一个原型属性
prototype,与实例的 proto 属性指向同一个对象。当一个对象在查找一个属性的时候,自身没有就会根据_proto_向它的原型进行查找,如果都没有,则向它的原型的原型继续查找,直到查到 Object.prototype._proto_为 null,这样也就形成了原型链。

如何利用原型实现继承

假设我们有一个 Shape 构造函数(父类)和 Rect 构造函数(子类)。代码如下:

// 父类
function Shape() {}
Shape.prototype.draw = function() {console.log('Shape Draw')
}
Shape.prototype.clear = function() {console.log('Shape Clear')
}// 子类
function Rect() {}/实现继承的代码放这里
/Rect.prototype.draw = function() {console.log('Rect Draw')
}

正常情况下使用 new Rect 创建的实例对象,它的原型链是这样的

rect -> Rect.prototype -> Object.protoype -> null

现在我们要实现的继承,其实就是在原型链中间再加一个原型对象 Shape.prototype。对此我们需要对 Rect.prototype 进行特殊的处理。

方法 1:Object.create
Rect.prototype = Object.create(Shape.prototype)
Rect.prototype.constructor = Rect // 选用,如果要用到 constructor

Object.create(proto) 是个神奇的方法,它能够创建一个空对象,并设置它的 [[prototype]] 为传入的对象。

因为我们无法通过代码的方式给 [[prototype]] 属性赋值,所以使用了 Object.create 方法作为替代。
因为 Rect.prototype 指向了另一个新的对象,所以把 constructor 给丢失了,可以考虑把它放回来,如果你要用到的话。
缺点是替换掉了原来的对象。

方法 2:直接修改 [[prototype]]

如果就是不想使用新对象,只想修改原对象,可以使用 废弃 的 __proto__ 属性,但不推荐。
不过另外还有一个方法 Object.setPrototypeOf() 可以修改对象的 [[prototype]],但因为性能的问题,也不推荐使用。

Object.setPrototypeOf(Rect.prototype, Shape.prototype)
// 或
Rect.prototype.__proto__ = Shape.prototype
方法 3:使用父类的实例
Rect.prototype = new Shape()
形成的原型链为:
rect -> shape(替代掉原来的 Rect.prototype) -> Shape.prototype -> Object.prototype -> null

用原型链的方式实现一个 JS 继承,其实就是希望构造函数 Son 创建出来的对象 son,它的原型链上加上父类 Parent.prototype,所以最后就是要修改 Son.prototype 的 [[prototype]]。

鉴于性能、兼容性、副作用等考虑,推荐使用方法 1,即通过 Object.create(Parent.prototype) 创建一个指定了 [[prototype]] 的新对象,替换掉原来的 Son.prototype 指向的对象