原型链
用法:把实例的父类给子类(子函数的原型)
缺点:因为修改obj1arr后obj2.arr也变化了,因为来自原型对象的引用属性里实例共享的。
2> 创建子类实例时,无法父类构造函数传递参数。
//父亲函数
function Parent(){
this.userName="父亲函数";
this.arr=[1,2,3];
}
//子函数
function Child(){
this.age=18;
}
//对象实例赋值是引用类型
Child.prototype=new Parent();
var obj1=new Child();
var obj2=new Child();
// 由于是引用类型,改一个内容则全部都变化
obj1.arr[0]="张三";
console.log(obj1.arr);
console.log(obj2.arr);
借用构造函数
核心:借用父类的构造函数来增强子类实例,就是说,相当于复制了一份父类的属性或者方法给子类了。
优点:1> 解决了子类实例共享父类引用属性的问题。2> 创建子类实例时,可以向父类构造函数传递参数
缺点:无法实现复用,每一个子类实例都有一个新的run函数,如果实例的对象多了,内存消耗过大。
//父亲函数
function Parent(name,arr){
this.userName=name;
this.arr=arr;
}
//子函数
function Child(name,arr){
this.age=18;
//借用构造函数的核心代码
Parent.call(this,name,arr);
}
var obj1 = new Child("张三", [1,2,3]);
var obj2 = new Child("张三", [1,2,3]);
//借用,不会产生关联
obj1.arr[0]="你好构造函数";
console.log(obj1.arr); //(3) ["你好构造函数", 2, 3]
console.log(obj2.arr); //[1, 2, 3]
组合继承(最常用的方式)
优点:1>不存在引用属性共享的问题 2> 可传递参数 3> 方法可复用
缺点: 子类原型上有一份多余的父类实例的属性
//组合继承
//父亲函数
function Parent(name,arr){
//父类的私有属性 子类可以通过call() 使用
this.userName=name;
this.arr=arr;
}
//原型链可以复用函数 ,子类实例也可以使用
Parent.prototype.run=function(){
}
//子函数
function Child(name,arr){
this.age=18;
//借用父亲元素
Parent.call(this,name,arr); //借用构造函数(核心语句) 不能复用(具有私有的特点)
}
Child.prototype=new Parent(); //原型链(核心语句) arr是引用属性 一个改变 互相影响
var obj1 = new Child("张三", [1,2,3]);
var obj2 = new Child("张三", [1,2,3]);
//借用,不会产生关联
obj1.arr[0]="你好构造函数";
console.log(obj1.arr); //(3) ["你好构造函数", 2, 3]
console.log(obj2.arr); //[1, 2, 3]
原型式继承
核心: 用一个函数(child)生成一个新的对象(F)
优点: 从已有对象繁衍出新的对象,不需要创建自定义类型
缺点; 原型的引用属性会互相影响(公用一个地址) 无法实现代码复用,属性是后添加的,都没用到函数封装。
function fn(obj){ //用来生成新对象
function F(){} //构造函数
F.prototype=obj; //新的对象
return new F();
}
function Child(){ //构造函数
this.val=1;
this.age=18;
this.arr=[1,2,3];
}
var sub=new Child(); // 实例化child函数
var obj1=fn(sub);
var obj2=fn(sub);
obj1.arr[1]="张三";
console.log(obj1.arr); //[1, "张三", 3]
console.log(obj2.arr); //[1, "张三", 3]
寄生式继承
核心:创建新对象=>增强对象(添加属性或者方法),与工厂模式相近
function fn(obj){
var F=function(){};
F.prototype=obj;
return new F();
}
//原型式
// function Sub(){
// this.name="张三";
// this.age=18;
// this.arr=[1,2,3];
// }
function getSub(obj){
//寄生核心
//新对象
var clone=fn(obj);
//增强
clone.attr1="class";
clone.att2="id";
return clone;
}
var obj1=new getSub({
name:"张三",
age:18,
arr:[1,2,3]
})
console.log(obj1.name); //张三
console.log(obj1.attr1); //class
function fn(obj){
var F=function(){};
F.prototype=obj;
return new F();
}
//原型式
// function Sub(){
// this.name="张三";
// this.age=18;
// this.arr=[1,2,3];
// }
function getSub(obj){
//寄生核心
//新对象
var clone=fn(obj);
//增强
clone.attr1="class";
clone.att2="id";
clone.brr=[1,2,3];
return clone;
}
var o={
name: "张三",
age: 18,
arr: [1, 2, 3]
}
var obj1=new getSub(o);
var obj2=new getSub(o);
//改变obj1的brr[0]的值,观察obj1和obj2对象中brr数组的变化
obj1.brr[0]=555;
console.log(obj1.brr); //[555, 2, 3]
console.log(obj2.brr); //[1, 2, 3]
寄生组合式继承
优点: 修正组合继承的缺点,只使用一次构造函数
缺点: 写法繁琐
function fn(obj){
var F=function(){
}
//把obj实例挂着F的原型上
F.prototype=obj;
//返回新的对象F的实例
return new F();
}
//
function Sub(){
this.str="张三";
//基本属性
this.arr=[1,2,3];
//引用属性
}
//run是共享的
Sub.prototype.run=function(){
return "共享的run方法";
}
function SubType(){
Sub.call(this); //核心代码 借用sub构造函数
// Sub.call(this,参数1,参数2...)
}
//三步走
var obj1=fn(Sub.prototype); //核心 传递原型 改变this指向
obj1.constructor=SubType; //改变构造函数指向
Sub.prototype=obj1; //实例对象赋值给sub原型
//创建对象实例
var obj2=new SubType();
var obj3=new SubType();
obj2.arr[0]="张三";
console.log(obj2.arr); //["张三",2,3]
console.log(obj3.arr); // [1,2,3]