原型 && 原型链
原型: 在 JS 中,每当定义一个对象(函数也是对象)时,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。
原型链: 函数的原型链对象constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针是指向上一层的原型对象,而上一层的原型对象的结构依然类似。因此可以利用__proto__一直指向Object的原型对象上,而Object原型对象用Object.prototype.__ proto__ = null表示原型链顶端。如此形成了js的原型链继承。同时所有的js对象都有Object的基本防范
原型关系:
- 每个对象构造器都有显示原型 prototype
- 每个实例都有隐式原型 _ proto_
- 实例的_ proto_指向对应对象构造器的 prototype
特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
继承
原型链继承
要点:父类的实例赋值给子类的原型
缺点:
- 引用类型的属性被所有实例共享
- 在创建子类型的实例时,不能向超类的构造函数中个传递参数
示例:
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]
构造函数继承
要点:使用父类的构造函数call执行在子类内部,来增强子类实例
优点:
- 可以在子类构造函数中向父类传参数
- 父类的引用属性不会被共享
缺点:
- 子类上面存在非必要的属性
- 无法函数复用(方法都在构造函数中定义,每次创建实例都会创建一遍方法),浪费性能
- 实例只是子类的实例
示例:
function Father() {
this.name = 'elvin';
}
function Son() {
Father.call(this)
}
let son = new Son()
console.log(son instanceof Son) //true
console.log(son instanceof Father) //false
组合继承
要点:父类的实例赋值给子类的
优点:
- 父类的方法可以复用
- 可以在Child构造函数中向Parent构造函数中传参
- 父类构造函数中的引用属性不会被共享
缺点:
- 两次副本,影响性能
- 第一次调用
SuperType():给子类原型写入两个属性name,color。 - 第二次调用
SuperType():给子类实例写入两个属性name,color。
- 第一次调用
示例:
function SuperType (name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
alert(this.name);
};
function SubType (name, age) {
// 继承属性
// 第二次调用SuperType()
SuperType.call(this, name);
this.age = age;
}
// 继承方法
// 构建原型链
// 第一次调用SuperType()
SubType.prototype = new SuperType();
// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType
SubType.prototype.constructor = SubType;
原型式继承
要点:借助了原型可以基于已有的对象创建新对象,而且还不必为此去创建自定义的类型
优点:
缺点:
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
- 无法传递参数
示例:
var person = {
name:'A',
colors:['green','red','yello']
}
var p1 = Object.create(person)
var p2 = Object.create(person)
寄生式继承
要点:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象.
缺点:
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
- 无法传递参数
- 由于不能做到函数复用而降低效率
示例:
function createPerson(origin) {
var clone = Object.create(origin)
clone.sayGood = function () {
console.log('hello world');
}
return clone;
}
寄生组合式继承
要点:构造函数传递参数和寄生模式实现继承
优点:
只调用了一次 SuperType 构造函数,并且因此避免了在 SuperType.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变
示例:
function SuperType(name){
this.name = name
this.colors = ['red','yellow']
}
superType.prototype.sayName = function () {
console.log(this.name);
}
// 创建超类原型的一个副本
var anotherPrototype = Object.create(SuperType.prototype)
anotherPrototype.constructor = SubType
SubType.prototype = anotherPrototype
function SubType(name,age){
SuperType.call(this.name)
this.age = age;
}
SubType.prototype.sayAge = function(){
console.log(this.age);
}