javascript中的对象创建和继承

ECMA-262将对象定义为一组属性的无序集合。

一、对象创建的方式

  • 通过构造函数的方式new Object()
    var person = new Object()
    person.name = '张三';
    person.age = 30;
    person.sayName = function () {
        console.log(this.name);
    }
复制代码
  • 通过字面量的方式{}
    var person = {
        name: '张三',
        age: 30,
        sayName: function () {
            console.log(this.name);
        }
    }
复制代码

二、继承的方式

1、原型链

// 父级构造函数
function SuperFun() {
    this.name = 'qb';
    this.colors = ['red', 'green'];
}
SuperFun.prototype.getName = function () {
    return this.name;
}
// 子构造函数
function SubFun() {
    this.age = 30;
};
// 原型继承
SubFun.prototype = new SuperFun();
SubFun.prototype.getAge = function () {
    return this.age;
}

let obj = new SubFun();
console.log(obj.getName()); // 'qb'
console.log(obj.getAge()); // '30'
复制代码

以上通过SubFun.prototype = new SuperFun()的方式,将SubFun的原型以实例化SuperFun产生的对象进行赋值。实例化的对象上有name属性和getName的方法,因此,SubFun实例化的对象obj也可以进行name属性的获取和getName方法的调用。

存在的问题

  • 1、SubFun的构造函数指向了SuperFun,而不是预期的SubFun
    console.log(obj.constructor === SubFun) // false
    console.log(obj.constructor === SuperFun) // true
复制代码
  • 2、SubFun实例化的对象会共享原型上的引用数据,非各自独有
obj.colors.push('pink');
console.log(obj.colors); // ["red", "green", "pink"]
let obj2 = new SubFun();
console.log(obj2.colors); //["red", "green", "pink"]
复制代码

2、盗用构造函数

// 父级构造函数
function SuperFun() {
    this.name = 'qb';
    this.colors = ['red', 'green'];
}
SuperFun.prototype.getName = function () {
    return this.name;
}
// 子构造函数,并且盗用构造函数
function SubFun() {
    SuperFun.call(this);
};
let obj = new SubFun();
obj.colors.push('pink');
console.log(obj.colors); // ["red", "green", "pink"]
let obj2 = new SubFun();
console.log(obj2.colors); //["red", "green"]
复制代码

以上通过在SubFun中去使用call方法,把SuperFun中的属性赋值给SubFun,而且,每次实例化SubFun的时候都去执行SuperFun.call(this),这样,每次产生的都是新的引用数据。

存在的问题

  • SubFun实例化对象中无法访问SuperFun原型上的方法
console.log(obj.getName());
复制代码

以上代码的执行结果是:

image.png

3、组合继承

    // 父级构造函数
    function SuperFun(name) {
        this.name = name;
        this.colors = ['red', 'green'];
    }
    SuperFun.prototype.getName = function () {
        return this.name;
    }

    function SubFun(name, age) {
        // (1)继承:继承属性
        SuperFun.call(this, name);
        this.age = age;
    };
    // (2)继承:继承方法
    SubFun.prototype = new SuperFun();
    SubFun.prototype.getAge = function () {
        return this.age;
    }
    let obj = new SubFun('qb', '30');
    console.log(obj.getName()); // qb
    console.log(obj.getAge(30)); // 30 
    obj.colors.push('pink');
    console.log(obj.colors); // ["red", "green", "pink"]
    let obj2 = new SubFun('qb2', '31');
    console.log(obj2.colors); //["red", "green"]
复制代码

以上结合原型继承方法和盗用构造函数继承属性的方式进行结合,解决了SubFun实例化的对象会共享原型上的引用数据的问题,既可以把定义在父级原型上的方法进行重用,又可以让每个实例都有自己的属性。

存在的问题

  • SubFun的构造函数指向了SuperFun,而不是预期的SubFun的问题依然存在
    console.log(obj.constructor === SubFun) // false
    console.log(obj.constructor === SuperFun) // true
复制代码

4、原型式继承

  • 实现1
    // 原型式继承
    function object(o) {
        function F() {};
        F.prototype = o;
        return new F();
    }

    var obj = {
        name: 'qb',
        colors: ['red', 'gree']
    }

    var obj1 = object(obj);
    obj1.colors.push('pink');
    console.log(obj1.colors); // ["red", "gree", "pink"]

    var obj2 = object(obj);  
    console.log(obj2.colors); // ["red", "gree", "pink"]
复制代码

以上通过object函数,把目标对象o传入,作为空白构造函数的原型,然后通过实例化空白构造函数的方式返回新的对戏,新返回的对象就是以目标对象为原型新对象。但是,因为原型的对象总o,所以新创建的各个实例之间共享原型上的引用数据。

  • 实现2
    var obj = {
        name: 'qb',
        colors: ['red', 'gree']
    }

    var obj1 = Object.create(obj);
    obj1.colors.push('pink');
    console.log(obj1.colors); // ["red", "gree", "pink"]

    var obj2 = Object.create(obj);  
    console.log(obj2.colors); // ["red", "gree", "pink"]
复制代码

以上Object.create的实现和object函数实现效果一样。

存在的问题

  • 实例化的对象会共享原型上的引用数据,非各自独有

5、寄生式继承

    function createObj(o) {
        let clone = Object.create(o); // 通过调用Object.create创建一个以o为原型的新对象
        clone.sayHi = function () { // 通过定义方法增强函数
            console.log('hi');
        }
        return clone;
    }

    var obj = {
        name: 'qb',
        colors: ['red', 'gree']
    }

    var obj1 = createObj(obj);
    obj1.colors.push('pink'); //  ["red", "gree", "pink"]
    obj1.sayHi(); // hi
    console.log(obj1.colors);

    var obj2 = createObj(obj);
    obj2.sayHi(); // hi
    console.log(obj2.colors); //  ["red", "gree", "pink"]
复制代码

存在的问题

  • 和原型式继承一样,实例化的对象会共享原型上的引用数据而非各自独有

6.寄生式组合继承

    // 父级构造函数
    function SuperFun(name) {
        this.name = name;
        this.colors = ['red', 'green'];
    }
    SuperFun.prototype.getName = function () {
        return this.name;
    }

    function SubFun(name, age) {
        SuperFun.call(this, name); // (1)继承:继承属性
        this.age = age;
    };

    SubFun.prototype = Object.create(SuperFun.prototype); // (2)继承:继承原型上的方法
    SubFun.prototype.constructor = SubFun; // 注意:SubFun原型上的constructor重新指向它自己

    SubFun.prototype.sayAge = function () {
        return this.age;
    }

    var obj = new SubFun('qb', 30);
    console.log(obj); // {name: 'qb', colors: ['red', 'green'], age: 30}
    console.log(obj.sayAge());
    console.log(obj.getName());
    obj.colors.push('pink');
    console.log(obj); // {name: 'qb', colors: ['red', 'green', 'pink'], age: 30}

    var obj2 = new SubFun('qb', 30);
    console.log(obj2); // {name: 'qb', colors: ['red', 'green'], age: 30}
复制代码

以上通过SuperFun.call(this, name)的方式,让子类构造函数初始化父构造中的代码,结果就是让每个实例都拥有自己的colors属性,并通过Object.create继承父级构造函数的原型,继承其上的属性和方法。此时,SubFun的构造函数还是SuperFun,再通过SubFun.prototype.constructor = SubFun的方式指回SubFun。

对象是一组属性的无序集合,继承可以让对象的产生有迹可循,有类可分。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享