逛了一圈博客,对JS继承的集中方式进行总结
在实现继承之前,先定义一个父类,并且对属性进行约定
function Fun(){
//私有属性
var val = 1;//私有属性
var arr = [1];//私有引用属性
function fun(){} //私有函数(引用属性)
//实例属性
this.val = 1; //实例基本属性
this.arr = [1]; //实例引用属性
this.fun = function(){};//实例函数(引用属性)
}
//原型属性
Fun.prototype.val = 1;//原型基本属性
Fun.prototypr.arr = [1];//原型引用属性
Fun.prototype.fun = function(){};//原型函数(引用类型)
//在这个原型函数中,可以使用它所定义的所有属性
基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法
构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针
具体实现:
function Super(){
this.val = 1;
this.arr = [1];
}
function Sub(){
}
Sub.prototype = new Super();//核心代码
var sub1 = new Sub();
var sub2 = new Sub();//用构造函数创建了两个对象
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val);//2
alert(sub2.val);//1
alert(sub1.arr);//1,2
alert(sub2.arr);//1,2
用父类实例充当子类的原型对象
优点:简单,易于实现
缺点:
。1.修改sub1.arr之后sub2.arr也发生了改变,因为来自原型对象的引用属性是所有实例共享的
可以这样理解执行过程:
执行sub1.arr.push(2)是先对sub1进行属性查找,找遍实例属性(在这个例子中未定义实例属性),没找到就顺着原型链向上找,找到了sub1的原型对象,发现arr属性,于是在arr末尾插入2,所以sub2.arr也变了
2.创建子类实例时,无法向父类构造函数传参数
思想:在子类构造函数的内部调用超类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数
实现:
function Super(val){
this.val = val;//可以传递参数
this.arr = [1];
this.fun = function(){
}
}
function Sub(val){
Super.call(this,val);//核心代码
//通过call()和apply()方法在新创建的对象上执行构造函数
}
var sub1 = new Sub(1);
var sub2 = new Sub(2);
sub1.arr.push(2);
alert(sub1.val);//1
alert(sub2.val);//2
alert(sub1.arr);//1,2
alert(sub2.arr);//1
console.log(sub1.fun === sub2.fun);//false
console.log(sub1.arr === sub2.arr);//false
2.核心
借用父类的构造函数来增强子类实例,等于是把父类的实例属性复制一份给子类实例装上(完全没用到原型)
3.优缺点
优点:
1.解决了子类实例共享父类引用的属性问题
2.创建子类实例时可以向父类构造函数传参
3.可以实现多继承(call多个父类对象)
缺点 :
1.实例并不是父类的实例,只是子类的实例
2.只能继承父类的实例属性和方法,不能继承原型属性/方法
3.无法实现函数的复用,每个子类都有父类实例函数的副本,例子中每个子类都持有一个新的fun函数,函数无法复用,这就是console.log(sub1.fun === sub2.fun)返回false的原因这就是太多了影响性能,内存爆炸
思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对函数实例属性的继承.这样,既通过在原型上定义方法实现了函数的复用,又保证本了每个实例都有它自己的属性
实现:
function Super(){
//再次只声明基本属性和引用属性
this.val = 1;
this.arr = [1];
//在此声明函数
Super.prototype.fun1 = function(){};
Super.protoype.fun2 = function(){};
function Sub(){
Super.call(this);//从父类拷贝一份父类的实例属性给子类作为子类的实例属性
}
Sub.prototype = new Super();//创建父类实例作为子类的原型 ,此时这个父类实例就又有了一份实例属性,但这份会被第一次拷贝来的实例属性屏蔽掉
var sub1 = new Sub(1);
var sub2 = new Sub(2);
alert(sub1.fun === sub2.fun);//true
}
2.核心:把 实例函数放在原型对象上,以实现函数的复用。同时还要保留借用构造函数方式的优点,通过Super.call(this);继承父类的基本属性和引用属性并且能够保留传参的优点,通过Sun.prototype = new Super();继承父类函数,以实现函数复用
优缺点:
优点:
1.不存在引用属性共享的问题
2.可传递参数
3.函数可复用
缺点:
1.子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的(在代码的注释部分给予解释),内存浪费
基本思想:通过借用函数来继承属性,通过原型链的混成形式来继承方法
基本模型:
function inheritProperty(subType,superType){
var prototype = object(superType.prototype);//创建对象
prototype.constructor = subType;//增强对象
subType.prototype = prototype;//指定对象
}
例子:
function beget(obj){//生孩子函数
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
//在此声明基本属性和引用属性
this.val = 1;
this.arr = [1];
}
//在此声明函数
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
function Sub(){
Super.call(this);//核心代码
}
var proto = beget(Super.prototype);//核心
proto.constructor = Sub; //核心
Sub.prototype = proto; //核心代码
var sub = new Sub();
2.核心
用生孩子函数得到一个“纯洁的对象”(无实例属性),再逐步增强之(填充实例属性)
beget(Super.prototype)切掉了原型对象上多余的那份父类实例属性
优点:比较完美
缺点:比较麻烦
这种继承借助原型并基于已有的对象创建新对象,同时还不用创建自定义的方式称为原型式继承
1.具体实现
实例1:
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
//拿到父类对象
var sup = new Super();
var sub = beget(sup);
sub.attr1 = 1;
sub.attr2 = 2;
实例2
function obj(o){
function F(){}
F.prototype = o;//将函数的原型设置为o
return new F();
}
var box = {
name:"trigkkit4",
arr: ['brother','sisiter','baba']
}
var b1 = obj(box);//返回一个以box函数为原型的对象
console.log(b1.name);//trigkkit4
console.log(b1.arr);//["brother","sister","baba"]
b1.name = 'mike';
console.log(b1.name);//mike
b1.arr.push('parents');
console.log(b1.arr);//["brother","sister","baba","parents"]
var b2 = obj(box);
console.log(b2.name);//trigkkit4
console.log(b2.arr);//["brother","sister","baba","parents"]
输出结果的原因是:
JS中基本类型与复杂类型存储的方式,可以参考一下JS堆栈和拷贝的理解
优缺点:
优点:
1.从已有对象衍生新的对象,不需要创建自定义类型
2.原型引用属性会被所有实例所共享,因为是用整个父类对象充当子类的原型对象,所以这个缺陷无法避免
3.无法实现代码的复用
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
function getSubObject(obj){
//创建新对象
var clone = beget(obj);//核心
clone.attr1 = 1;
clone.attr2 = 2;
return clone;
}
var sub = getSubObject(new Super());
console.log(sub.val);
console.log(sub.arr);
console.log(sub.arr1);
核心:
给原型式继承穿了个马甲
注意:beget函数不是必须的,创建对象-增强-返回该对象,这样的过程叫做寄生式继承,新的对象是如何创建的不重要
优缺点:
优点:1.不需要创建自定义类型
缺点:1.无法实现函数的复用