JS继承的6种方式

JS实现继承的集中方式

逛了一圈博客,对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(){};//原型函数(引用类型)
//在这个原型函数中,可以使用它所定义的所有属性

1.简单原型链

基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法
构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针
具体实现:

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

2.核心

用父类实例充当子类的原型对象

3.优缺点

优点:简单,易于实现
缺点:
。1.修改sub1.arr之后sub2.arr也发生了改变,因为来自原型对象的引用属性是所有实例共享的
可以这样理解执行过程:
执行sub1.arr.push(2)是先对sub1进行属性查找,找遍实例属性(在这个例子中未定义实例属性),没找到就顺着原型链向上找,找到了sub1的原型对象,发现arr属性,于是在arr末尾插入2,所以sub2.arr也变了
2.创建子类实例时,无法向父类构造函数传参数

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的原因这就是太多了影响性能,内存爆炸

3.组合继承

思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对函数实例属性的继承.这样,既通过在原型上定义方法实现了函数的复用,又保证本了每个实例都有它自己的属性
实现:

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.子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的(在代码的注释部分给予解释),内存浪费

4.寄生组合式继承

基本思想:通过借用函数来继承属性,通过原型链的混成形式来继承方法
基本模型:

 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)切掉了原型对象上多余的那份父类实例属性
优点:比较完美
缺点:比较麻烦

5.原型式继承

这种继承借助原型并基于已有的对象创建新对象,同时还不用创建自定义的方式称为原型式继承
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.无法实现代码的复用

6.寄生方式

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.无法实现函数的复用

你可能感兴趣的:(js,继承,继承,js)