JS中的原型链,多种继承方式及其案例

原型链

原型链是由__Proto__串联起来的链状结构。

每一个函数都有一个prototype属性,也有__proto__,每一个对象只有一个__proto__属性;一般我们把属性方法放在构造函数中,把方法挂在prototype下面,对象的__proto__指向构造函数的prototype,原型链的终点为Object.prototype,再往上Object.prototype.proto=null

1、实例没有具体的构造函数 复制代码
    let obj = {}
    console.log(obj.__proto__== Object.prototype) //true
    onsole.log(Object.prototype.__proto__) //null
2、实例的构造函数为自定义的构造函数 复制代码
    function Persion(){}//构造函数
    let p1 = new Persion()//实例
    console.log(p.__proto__==Persion.prototype) //true
    console.log(Persion.prototype.__proto__==Object.prototype) //true
    console.log(Object.prototype.__proto__) //null
3、实例的构造函数为内置构造函数 复制代码
    let p = new Array()
    console.log(p.__proto__ == Array.prototype) //true
    console.log(Array.__proto__ ==Function.prototype)//true
    console.log(Function.__proto__ == Function.prototype)//true ,Function 是构造函数,也是对象
    console.log(Function.prototype.__proto__==Object.prototype)//true
    console.log(Object.prototype.__proto__)//null

1、原型链继承子类的原型指向父类的实例(子类的.prototype=new 父类())

javascript 复制代码
function Child() {} //子类
// Child.prototype.sleep=function(){
//     console.log(this.name+"is sleeping")
// }1
Child.prototype = new Parent("yzh")
//为了不破坏原型链,将Male的constructor指向本身
Child.prototype.constructor=Child
Child.prototype.sleep=function(){
     console.log(this.name+" is sleeping")
     }//2
     Parent.prototype.eat=function(){
     console.log(this.name+" is eating")
    }

var child=new Child()
console.log(Child)
child.introduce()
child.hobby("唱歌")
child.sleep()
child.eat()
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true

注:如上如果Male的原型方法sleep放在1位置时,会报错child.sleep is not a function,放在2位置, 输出 yzh is sleeping

此种继承方法的特点:

1、子类与父类的关系为指向关系,实例是子类的实例,也是父类的实例 2、父类新增的原型方法或属性,子类都能访问 3、简单易用 缺点:

1、如果要为子类新增属性或者方法,只能在new Parent() 之后,并不能放在构造函数中,如上的代码 例,如果新增的方法放在改变子类原型的指向之前,改变指向之后新增的方法自然就没用了,子类的prot otype已经指向了父类了

2、子类的所有实例,共用所有的父类属性,子类不能拥有自己的属性,如果有多个实例时,其中一个实修 改了父类引用类型的值,那么所有的实例都会发生改变,例如我只想其中的一个实例的eat方法改为"is eating apple",那么所有的实例该方法都会发生改变

3、不能多继承,因为是改变了原型链的指向,不能指向多个父类,因此只能单继承 4、创建子类时,无法向父类构造函数传参,因为在改变子类的原型链指向之后,子类的属性和方法是无效 的

2、构造继承(call,apply继承)把 父对象的所有属性和方法,拷贝进子对象

javascript 复制代码
function Child(name){ 
      Parent.call(this,name) 
}
var child=new Child("yzh")
console.log(child.name) //yzh
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //false
console.log(child instanceof Child) //true

优点:

1、解决了原型链继承中的子类共享父类属性的问题

2、创建的子类实例可以向父类传递参数

3、可以实现多继承,call改变父类的this

缺点:

1、实例是子类的实例,不是父类的

2、只能继承父类的实例属性和方法,不能继承父类原型上的方法

3、无法实现函数复用,每个子类都有父类函数的属性和方法的副本,当child调用Parent上的方法时,Parent内部的this指向的是child,Parent内部的this上的属性和方法都被复制到了child上面,如果每个子类的实例都复制一遍父类的属性和方法,就会占用很大的内存,而且当父类的方法发生改变了时,已经创建好的子类实例并不能更新方法,因为已经复制了原来的父类方法当成自己的方法了。

3、组合继承(原型链继承与构造继承)

javascript 复制代码
function Child(name) {
    Parent.call(this,name) //构造继承 ,第二次调用父类
}
//原型链继承
Child.prototype=new Parent()
Child.prototype.constructor=Child

var child=new Child("yzh") //子类的实例向父类传递参数,第一次调用
父类
console.log(child.name) 
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true

优点:结合了原型链继承和构造继承的优点

1、子类可向父类传参

2、实例既是子类的实例,也是父类的实例

3、多个实例之间不存在公用父类的引用属性的问题

4、实例可以继承父类实例的属性和方法,也可以继承原型上的属性和方法

缺点:这种方式调用了两次父类的构造函数,生成了两份实例,相同的属性既存在于实例中也存在于原型中

4、拷贝继承

javascript 复制代码
function Child(name){
    var parent = new Parent(name);
    for(var key in parent){
        Child.prototype[key] = parent[key];
    }
}
var child=new Child("yzh")
console.log(child.name) //yzh
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //false
console.log(child instanceof Child) //true

优点:

1、 支持多继承

缺点:

1、无法获取父类不可枚举的方法,这种方法是用for in 来遍历Parent中的属性,例如多选框的checked属性,这种就是不可枚举的属性

2、 效率很低,内存占用高

5、寄生组合继承

寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法.

思路:不必为了指定子类的原型而调用父类的构造函数,我们所需要的无非就是父类原型的一个副本而已.本质上,就是使用寄生式继承来继承父类的原型,然后在将结果指定给子类的原型

其实就是在不需要实例化父类构造函数的情况下,也能得到父类的属性和方法,就是直接实例化一个临时的父类的副本,实现原型链继承

javascript 复制代码
function Child(name) {
	Parent.call(this,name) //构造继承            
}

(function(){
    //创建一个临时的类
    var Temp=function(){}
    Temp.prototype=Parent.prototype
    //子类的原型指向父类的实例
    Child.prototype=new Temp()
})()

var p=new Child("yzh")
console.log(p.name)//yzh
p.hobby("sing")//yzh like sing
p.introduce()//my name is yzh

优点:结合组合继承的所有优点,此方法比较完美

6、ES6继承

javascript 复制代码
class Parent{
     constructor(name){
         this.name=name
     }
     introduce(){
         console.log("my name is " + this.name)
     }
     hobby(hobby){
         console.log(this.name + " like " + hobby)
     }
 }
class Child extends Parent{
    constructor(name,age){
        super(name) //构造继承,可以继承Parent构造函数上的属性
        this.age=age
    }
}
var  p = new Child("yzh")
p.introduce() //my name is yzh
p.hobby("apple")//yzh like apple
console.log(p instanceof Parent) // true
console.log(p instanceof Child) //true

我们用es6的写法让对象的写法更加的清晰,更像面对对象的编程;在这个方法中使用extends 和 super 两个关键字,在子类的constructor方法中调用super方法,用来继承父类的this对象

相关推荐
真的很上进1 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
qq_278063712 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl2 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码2 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347542 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js
喝旺仔la2 小时前
VSCode的使用
java·开发语言·javascript
辛-夷2 小时前
VUE面试题(单页应用及其首屏加载速度慢的问题)
前端·javascript·vue.js
一个很帅的帅哥4 小时前
axios(基于Promise的HTTP客户端) 与 `async` 和 `await` 结合使用
javascript·网络·网络协议·http·async·promise·await
dream_ready4 小时前
linux安装nginx+前端部署vue项目(实际测试react项目也可以)
前端·javascript·vue.js·nginx·react·html5
编写美好前程4 小时前
ruoyi-vue若依前端是如何防止接口重复请求
前端·javascript·vue.js