继承初体验
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了继承初体验相关的知识,希望对你有一定的参考价值。
剧透:继承方式 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的恢复!)
以上是关于继承初体验的主要内容,如果未能解决你的问题,请参考以下文章