简单理解JavaScript对象继承

原型链

用法:把实例的父类给子类(子函数的原型)
缺点:因为修改obj1arr后obj2.arr也变化了,因为来自原型对象的引用属性里实例共享的。
2> 创建子类实例时,无法父类构造函数传递参数。

//父亲函数
function Parent(){
    this.userName="父亲函数";
    this.arr=[1,2,3];
}
//子函数
function Child(){
    this.age=18;
}
//对象实例赋值是引用类型
Child.prototype=new Parent();
var obj1=new Child();
var obj2=new Child();
// 由于是引用类型,改一个内容则全部都变化
obj1.arr[0]="张三";
console.log(obj1.arr);
console.log(obj2.arr);

借用构造函数

核心:借用父类的构造函数来增强子类实例,就是说,相当于复制了一份父类的属性或者方法给子类了。
优点:1> 解决了子类实例共享父类引用属性的问题。2> 创建子类实例时,可以向父类构造函数传递参数
缺点:无法实现复用,每一个子类实例都有一个新的run函数,如果实例的对象多了,内存消耗过大。

//父亲函数
function Parent(name,arr){
    this.userName=name;
    this.arr=arr;
}
//子函数
function Child(name,arr){
    this.age=18;
    //借用构造函数的核心代码
    Parent.call(this,name,arr); 
}

var obj1 = new Child("张三", [1,2,3]);
var obj2 = new Child("张三", [1,2,3]);
//借用,不会产生关联
obj1.arr[0]="你好构造函数";
console.log(obj1.arr); //(3) ["你好构造函数", 2, 3]
console.log(obj2.arr); //[1, 2, 3]

组合继承(最常用的方式)

优点:1>不存在引用属性共享的问题 2> 可传递参数 3> 方法可复用
缺点: 子类原型上有一份多余的父类实例的属性

//组合继承
//父亲函数
function Parent(name,arr){
    //父类的私有属性 子类可以通过call() 使用
    this.userName=name;
    this.arr=arr;
}
//原型链可以复用函数 ,子类实例也可以使用
Parent.prototype.run=function(){

}
//子函数
function Child(name,arr){
    this.age=18;
    //借用父亲元素
    Parent.call(this,name,arr); //借用构造函数(核心语句)  不能复用(具有私有的特点)
}
Child.prototype=new Parent();  //原型链(核心语句)  arr是引用属性   一个改变 互相影响
var obj1 = new Child("张三", [1,2,3]);
var obj2 = new Child("张三", [1,2,3]);
//借用,不会产生关联
obj1.arr[0]="你好构造函数";
console.log(obj1.arr); //(3) ["你好构造函数", 2, 3]
console.log(obj2.arr); //[1, 2, 3]

原型式继承

核心: 用一个函数(child)生成一个新的对象(F)
优点: 从已有对象繁衍出新的对象,不需要创建自定义类型
缺点; 原型的引用属性会互相影响(公用一个地址) 无法实现代码复用,属性是后添加的,都没用到函数封装。

function fn(obj){ //用来生成新对象
    function F(){} //构造函数
    F.prototype=obj; //新的对象
    return new F();
}
function Child(){ //构造函数
    this.val=1;
    this.age=18;
    this.arr=[1,2,3];
}
var sub=new Child(); // 实例化child函数
var obj1=fn(sub);
var obj2=fn(sub);
obj1.arr[1]="张三";
console.log(obj1.arr); //[1, "张三", 3]
console.log(obj2.arr); //[1, "张三", 3]

寄生式继承

核心:创建新对象=>增强对象(添加属性或者方法),与工厂模式相近

function fn(obj){
    var F=function(){};
    F.prototype=obj;
    return new F();
}

//原型式
// function Sub(){
//     this.name="张三";
//     this.age=18;
//     this.arr=[1,2,3];
// }
function getSub(obj){
    //寄生核心
    //新对象
    var clone=fn(obj);
    //增强
    clone.attr1="class";
    clone.att2="id";
    return clone;
}
var obj1=new getSub({
    name:"张三",
    age:18,
    arr:[1,2,3]
})
console.log(obj1.name); //张三
console.log(obj1.attr1); //class


function fn(obj){
    var F=function(){};
    F.prototype=obj;
    return new F();
}

//原型式
// function Sub(){
//     this.name="张三";
//     this.age=18;
//     this.arr=[1,2,3];
// }
function getSub(obj){
    //寄生核心
    //新对象
    var clone=fn(obj);
    //增强
    clone.attr1="class";
    clone.att2="id";
    clone.brr=[1,2,3];
    return clone;
}
var o={
    
        name: "张三",
        age: 18,
        arr: [1, 2, 3]
    
}
var obj1=new getSub(o);
var obj2=new getSub(o);
//改变obj1的brr[0]的值,观察obj1和obj2对象中brr数组的变化
obj1.brr[0]=555;
console.log(obj1.brr); //[555, 2, 3]
console.log(obj2.brr); //[1, 2, 3]


寄生组合式继承

优点: 修正组合继承的缺点,只使用一次构造函数
缺点: 写法繁琐

function fn(obj){
    var F=function(){

    }
    //把obj实例挂着F的原型上
    F.prototype=obj;
    //返回新的对象F的实例
    return new F();
}
//
function Sub(){
    this.str="张三";
    //基本属性
    this.arr=[1,2,3];
    //引用属性
}
//run是共享的
Sub.prototype.run=function(){
    return "共享的run方法";
}
function SubType(){
    Sub.call(this);  //核心代码  借用sub构造函数
    // Sub.call(this,参数1,参数2...)
}
//三步走
var obj1=fn(Sub.prototype); //核心  传递原型   改变this指向
obj1.constructor=SubType; //改变构造函数指向 
Sub.prototype=obj1;  //实例对象赋值给sub原型

//创建对象实例
var obj2=new SubType();
var obj3=new SubType();
obj2.arr[0]="张三";
console.log(obj2.arr); //["张三",2,3]
console.log(obj3.arr); // [1,2,3]
发布了49 篇原创文章 · 获赞 3 · 访问量 5108

猜你喜欢

转载自blog.csdn.net/weixin_43487066/article/details/104349671