继承初体验

剧透:继承方式  call apply  原型继承 原型链继承 原型拷贝继承(完美) 混合继承(完美) 寄生继承(完美) 

前戏什么的就省了,直接步入正题。

继承是什么呢?

js里面的一大特性,

js是作为面向对象的弱类型语言,

面向对象的三大核心部分是:

抽象:抓住核心问题

封装:不考虑内部实现,只考虑功能使用

继承:从已有的对象上继承出新的对象,子级继承父级

多态:js中不存在的 php java中的 难度?颗星

寻根问源 找到继承之后

如何实现继承呢?

call apply

先了解一下call apply 两者的区别:

相同点:

改变this的指向

差异:

call(,)两个参数:第一个是this的指向 第二个是需要传递的值

apply(,)同上,但是第二个是传入一个数组

 举个栗子:

var arr = [1,2,3]
//数组去借Math对象里面的max方法 console.log(Math.max.apply([],arr))
//3

 简单粗暴 上代码

 ①call apply  继承

复制代码
//先请出吴彦祖
var person = {
    name:"吴彦祖",
//吴彦祖有个抛媚眼的技能
shuai:function(skill){
  console.log(this.name+"向你抛了个"+skill)  
}      
}
person.shuai("媚眼")
//吴彦祖向你抛了个媚眼
//这个时候 黄渤来了
var son ={
name:"黄渤"
}
//黄渤也想向你抛媚眼
//这个时候浏览器就看不下去了赶紧报了个错
son.shuai()
//not a function 不是一个方法

//然后黄渤就去找吴彦祖拿这个技能
//吴彦祖就很勉强的把这个撩妹的技能给了黄渤
person.shuai.call(son,"抛了个媚眼")
//这里的call()里面的两个参数:①this的指向②需要传递的值,值可以传递多个,每个之间用 逗号 , 隔开。
//直接找到吴彦祖而不去使用他的技能 不传值 会报undefind
person.shuai.call(son)//黄渤向你抛了个undefind
person.shuai.call(son,"媚眼")//黄渤向你抛了个媚眼

复制代码

总结:call apply 是最糙的继承方式。多用于属性继承(子级继承父级),也可继承方法,但是需要写在函数内,而写在函数内的方法占用内存,损耗资源,通常会把方法写在原型上。故而call apply 继承不到。方法写在原型的好处就是优化性能 减少内存占用 提高用户体验。

话不多说 上代码 接下来使用构造函数

复制代码//人具有的通用属性 名字,年龄,还有吃这个技能等等
function Person(name,age){
        this.name = name;
        this.age = age;
        this.eat = function(){} 
}
//具体到某个人的话 每个人有自己独特的Y染色体等
function Son(y,name,age){
     //使用call继承上面的通用属性
//this指向当前Son ,然后Son去借Person的哪些属性
//通用属性传入之后需要给到Son的形参中 继承的属性语法要写在本身属性之前 否则本身会被覆盖
Person.call(this,name,age) this.y = y
} //实例化对象 //这里实例化new的过程中具体做了些什么操作呢? var wuyanzu = newSon(12,"吴彦祖","18") //此时的吴彦祖不仅有通用属性还有吃的这个技能

复制代码

 前面说到方法需要写在原型上 下面进行一番改造

复制代码
function Person(name,age){
        this.name = name;
        this.age = age;
        //this.eat = function(){} 
}
Person.Prototype.eat = function(){代码块...}
function Son(y,name,age){
    Person.call(this,name,age)
    this.y = y
}
var wuyanzu = newSon(12,"吴彦祖","18") 
//此时的吴彦祖继承到了属性 但是没有继续到方法
//那么问题来了 我们应该如何继承到写在原型上的方法呢?

那么什么是原型呢?

看完概念上代码:

Prototype:每一个函数里面都有一个Prototype属性,这个属性叫做原型,这个原型指向一个对象,我们把这个对象叫做原型对象。

原型对象里面有两个东西:

①:constructor:构造器 作用是指向创建自己的那个构造函数

②__proto__

a.通过new实例化对象之后,里面都会有一个__proto__这个属性

b.__proto__指向一个对象,这个对象是原型对象

c.实例化对象可以直接访问__proto__里面的一些方法

原型继承

function Father(age,name){
    this.name = name;
    this.age= age;
}

Father.prototype.eat = function(){}
Father.prototype.sleep=function(){}

function Son(y,age,name){
    Father.call(this,age,name)
    this.y = y;
}
//重点来了
Son.prototype = Father.prototype;
Son.prototype.work = function(){}

 var wuyanzu = new Son(12,"吴彦祖",18)
 var w = new Father()
 console.log(wuyanzu)
 console.log(w)

是的 细心的你肯定发现了什么。

原型继承的缺点正是在此。

父级被子级污染。(黄色箭头)

那么有没有更好的继承方式呢?

答案是有的。

 原型链继承

首先说一下什么是原型链呢?链?链条?一环扣一环。这正是原型链的形式,在jQuery中我们会发现我们的代码会经常....点下去,这正是链式操作的强大。在原型当中,由__proto__组成的链条叫做原型链。实例化对象通过链条向父级,子级,借用方法,通俗点的说您通过DNA染色体和您的父亲建立了血缘关系。

比如我们实例化了一个对象 W 然后W.__proto__.__proto__.toString

W本身没有toString这个方法,但是实例化对象可以直接访问__proto__里面的一些方法,而__proto__指向一个对象,这个对象是原型对象,原型对象中的方法和属性可以被访问到。

话不多说 上代码

function Father(age,name){
    this.age=age;
this.name=name; } Father.prototype.eat = function(){} Father.prototype.sleep = function(){} function Son(y,age,name){ Father.call(this,age,name) this.y=y } Son.prototype = new Father(); Son.prototype.work = function(){} var wuyanzu = new Son() var w = new Father() console.log(wuyanzu) console.log(w)


原型链继承的特点:
①代码简易,实现简单

②子类能访问到父类原型新增的方法或属性

   ③实例是子类的实例,也是父类的实例,两者关联性强


但是细心的你肯定又发现了
①实例化对象的__proto__应该指向构造函数 object 这里的却指向继承的Father
②少了constructor
③多了一些无用的属性

那么有没有更完美的继承方式呢?
答案依旧是有的。


混合继承(完美)

话不多说 上代码

function Father(age,name){
    this.age=age;
    this.name=name
}
Father.prototype.eat = function(){}
Father.prototype.sleep = function(){}

function Son(y,age,name){
    Father.call(this,age,name)
    this.y=y
}
//划重点 少啥补啥
Son.prototype ={
     constructor:Son,
     __proto__:Father.prototype (等于Son.prototype = new Father())
 }
Son.prototype.work = function(){}

var wuyanzu = new Son()
var w = new Father()
console.log(wuyanzu)
console.log(w)

怎么个完美呢?

①父级没有被污染

②指向正确

推荐使用:5星

再来几个

寄生继承(完美)

何为寄生?既生瑜何生亮?nonono

寄生----虫

通过一个寄生壳子 依附功能

话不多说 上代码

function Father(age,name){
    this.age=age;
    this.name=name
}
Father.prototype.eat = function(){}
Father.prototype.sleep = function(){}

function Son(y,age,name){
    Father.call(this,age,name)
    this.y=y
}

//寄生壳子
 function fn(){}
 fn.prototype = Father.prototype;
 Son.prototype = new fn();
 Son.prototype.constructor = Son
 Son.prototype.work = function(){}

var wuyanzu = new Son()
var w = new Father()
console.log(wuyanzu)
console.log(w)

 优点:

混合继承会有两次调用的情况,寄生继承解决了这个问题。

最后再上一个

原型拷贝继承(完美)

function Father(age,name){
    this.age=age;
    this.name=name
}
Father.prototype.eat = function(){}
Father.prototype.sleep = function(){}

function Son(y,age,name){
    Father.call(this,age,name)
    this.y=y
}
Son.extend = function(Father){
    for(var key in Father){
        this[key] = Father[key]
    }
}
Son.prototype.work = function(){}
var wuyanzu = new Son()
var w = new Father()
console.log(wuyanzu)
console.log(w)

优点在于可以进行多继承

缺点就是同时拷贝到了父类的属性,对于本身来说是负重的,增加了自身的体积,占用了内存资源,效率较低的。

除此之外ES6也提供了继承方法诸如class super.....

tips:

instanceOf 检测是否是一个数组

Object{[native code]}这是一个系统封装好的函数 无法查看

null作为原型链的终点(object.prototype.__proto__   //null)

点到即止 

今天就到这吧。。。

最后灵魂画手上手了。。。

中间误删了草稿(感谢管理contact@cnblogs.com的恢复!)

原文地址:https://www.cnblogs.com/522040-m/p/9052128.html