js 原型链继承,借用构造函数继承, 组合继承,寄生式继承,寄生组合继承

3,742 阅读5分钟
原文链接: zhuanlan.zhihu.com

2017.01.22更新 原型式继承 、寄生式继承,寄生组合继承

开头语:昨天放假一天,原本计划出去爬山,却躺了一天,无语~~~

原型式继承,上代码

var ob = {name:"小明",friends:['小明','小白']};

//原型式继承  参数o,引用类型值,实质就是一个内存地址
function object(o){
  function F(){}//创建一个构造函数F
  F.prototype = o;
  return new F();
}

var ob1 = object(ob);
ob1.name = "小小";
ob1.friends.push("小黑");

var ob2 = object(ob);
console.log(ob2.named);//小明
console.log(ob2.friends);//小明,小白,小黑

上关系图

第一个问题:为什么ob2.named还是小明

答:虽然ob1修改了name 值,但是这是在ob1自身添加了一个name值,在原生对象(ob)并没有改变,所以name还是小明

第二个问题 ob1.friends.push("小黑");console.log(ob2.friends);//小明,小白,小黑ob2没有添加小黑,为什么也随着ob1的变化而变化了。

答:ob1先从自身查找friends,发现没有找到,然后又从原型里查找 ,发现在原型里有这个值,然后追加,以为ob1和ob2共享了一个原型,所以当ob1修改的时候,ob2也发生了变化。

寄生式继承

var ob = {name:"小明",friends:['小花','小白']};
function object(o){
  function F(){}//创建一个构造函数F
  F.prototype = o;
  return new F();
}

//上面再ECMAScript5 有了一新的规范写法,Object.create(ob) 效果是一样的  

function createOb(o){
   var newob = object(o);//创建对象
   newob.sayname = function(){//增强对象
       console.log(this.name);
   }

   return newob;//指定对象
}

var ob1 = createOb(ob);
ob1.sayname();//小明

寄生式组合继承(据说是最好用的继承方式)

//寄生继承 创建一个新的对象返回一个新实例
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}
//父层,超级层
function Father(name){
  this.name = name;
  this.friends= ["小花","小草"];
}
Father.prototype.sayname = function(){
  console.log(this.name);
}
function Child(name,age){
  father.call(this,name);//继承属性
  this.age = age;
}

//继承父层的prototype
function inheritPrototype(child,father){
  var prototype = object(father.prototype);//返回一个新实例(对象)
  prototype.constructor = child;//增强对象
  child.prototype = prototype;
}
inheritPrototype(Child,Father);//继承父层的原型
Child.prototype.sayage = function(){
  console.log(this.age);
}

var c1 = new Child("小白",20);
c1.friends.push("小小");
c1.sayname();//小白
c1.sayage();//20

var c2 = new Child("小蓝",23);
c2.sayname();//小蓝
c2.sayage();//23

console.log(c2.friends);//"小花","小草"

上关系图

继承也是面向对象的一个重点,能理解里面的知识,对以后大型项目的编程和代码的复用,有很大的好处,到此结束。


----------------------------------------华丽丽且风骚的分割线-------------------------

开头语,昨天下了一夜的雪,天气很冷~~~,鞋底和雪的摩擦,沙沙的声音,甚是好听,上班的路上,一路踩过啦,得出结论,声音很美妙,但是不能听多了,为啥,听多了,鞋子就湿了。

早上又复习了一下js的继承,今天先讲三种方式,原型链继承,借用构造函数继承,两者结合的继承。

原型继承上案例

//首先声明一个最顶层
Function Father(name){
  this.name  = name;
}
father.prototype.father = function(){
  console.log(this.name);
}
//实例化
new Father("小花");

这是我们常见创建类用构造函数加原生的构建方式

下面我们画一下它们的关系图

其中__proto__是一个隐形属性,每个浏览器支持的并不一样,原理都是一样的,当我们实例化一个对象后,先从自身属性里找name,找到了就结束了,找不到在从__proto__指向的prototype对象找,继续往下看

//声明一个子类
function Child(age){
  this.age = age;
}
//把Father实例化赋给Child.prototype
Child.prototype = new Father();
Child.prototype.sayage = function(){
   console.log(this.age);
}

接下来我们看一下他们的关系图

通过这种方式就实现了原型(prototype)链上面的继承.,实例化之后,我们看一下关系网

//实例化
var c1 = new Child("16");
c1.sayname();//undefined

当我们执行c1.sayname();时候,首先自身属性里找,有没有这个方法,发现没有,然后通过__proto__自动查找prototype对象,然后还是没有找到,然后从new Father()里的__proto__里找prototype对象,发现存在这个函数,里面this.name找法一样,找到后发现没有赋值,然后输出undefined 。至此结束,这个整个流程就被成为原型链继承.


原型链继承.第一个问题,当实例化之后,实例修改原型属性时,会影响其他的实例属性,我们使用原型对象的原因就是,原型能在各个实例中共享属性和方法,当然,这也在一些情况下会出现问题。还是上面的例子,我们改造一下

Function Father(name){
  this.name  = name;
  this.num = ['12'];
}
father.prototype.father = function(){
  console.log(this.name);
}

function Child(age){
  this.age = age;
}
//把Father实例化赋给Child.prototype
Child.prototype = new Father();
Child.prototype.sayage = function(){
   console.log(this.age);
}

var c1 = new Child("16");
c1.num.push("13");
console.log(c1.num);//12,13;

var c2 = new Child("17");
console.log(c2.num);//12,13

通过上面的例子,我们发现,虽然没有给c2.num执行操作,但还是收之前实例化操作的影响,假如说你就是为了实现这样,那当然没问题,多数情况下,我们不希望各个实例之间相互影响。

原型链继承.第二个问题,我们创建子类型实例的时候,不能给父类型的构造函数传递参数,或者说,在不影响其他实例的情况下不能传参数。

基于上面的种种问题,我们引出了第二种继承方式:借用构造函数的方式

翠花,上代码~~~

function Father(){
   this.num = ['12'];
}
function Child(){
  //继承father
   Father.call(this);
}

var c1 = new Child();
c1.num.push(13);
console.log(c1.num);//12,13
var c2 = new Child();
console.log(c2.num);//12

通过这种方式,每一个实例都有自己的一份属性和方法,不受其他的实例所影响,我们想到代码的复用问题,所以这种方式还是不完美,接着引出下一种继承方式:组合继承

组合继承就是把前面的两者中方式结合起来,让属性值不受影响,让方法复用

翠花,上代码~~

Function Father(name){
  this.name  = name;
  this.num = ['12'];
}
father.prototype.father = function(){
  console.log(this.name);
}

function Child(age,name){
  Father.call(this,name);//继承属性
  this.age = age;
}
Child.prototype = new Father();//继承方法
Child.prototype.constructor = Child;
Child.prototype.sayage = function(){
  console.log(this.age);
}

var c1 = new Child(16,"小花");
c1.sayage();//16
c1.sayname();//小花
c1.num.push(13);
console.log(c1.num)//12,13

var c2 = new Child(18,"小明");
c2.sayage();//18
c2.sayname();//小明
console.log(c2.num)//12

附上关系图

到这里三种继承方式都讲完了,来来干了这碗酸菜.