JavaScript建立对象与继承方法(笔记)

一.建立对象
虽然Object构造函数或对象字面量能够用来建立单个对象,但有个明显缺点:使用同一个接口建立不少对象会产生大量重复代码。于是你们开始探索其余方式。
1.工厂模式es6

function createPerson (name, age) {
    var o = new Object():
    o.name = name;
    o.age = age;
    o.sayName = function () {
        console.log(this.name);
    }
    return o;
}
var person = createPerson('Lily', 12);

工厂模式特色:虽然解决了建立多个类似对象的问题,但却没法识别一个对象的类型。(instance of)
2.构造函数模式数组

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        console.log(this.name);
    }
}
var person = new Person('Lily', 12); //使用构造函数模式建立实例,必须使用new操做符。

构造函数模式特色:能够将它的实例标识为一种特定类型,但每一个方法都要在实力上从新建立一遍。
3.原型模式函数

function Person () {}
Person.prototype.name = 'Lily';
Person.prototype.age = 12;
Person.prototype.sayName = function () {
    console.log(this.name);
}
var person = new Person(); //使用hasOwnProperty方法能够检测一个属性存在于实例仍是原型。
console.log(person.hasOwnProperty('name')); //false
person.name = 'Tom'; //来自实例
console.log(person.hasOwnProperty('name')); //true

更简单的原型语法:this

function Person () {}
Person.prototype = {
    name: 'Lily',
    age: 12,
    sayName: function () {
        console.log(this.name);
    }
} //这样写会致使constructor属性再也不指向Person了。(不管什么时候建立一个新函数A,都会为它建立一个prototype属性,指向函数的原型对象,默认状况下,全部原型对象都会得到一个constructor属性,包含一个指向A的指针。)

于是可更改成:prototype

function Person () {}
Person.prototype = {
    constructor: Person, //增长constructor属性
    name: 'Lily',
    age: 12,
    sayName: function () {
        console.log(this.name);
    }
} //这种写法要注意,建立实例必定要在定义原型以后,由于重写原型对象就切断了构造函数与最初原型的联系。

原型模式的特色:实现了让全部实例共享原型对象所包含的属性和方法。但缺点也在于这种共享对于包含引用类型值的属性而言,存在一些问题,即全部实例会共享一个数组或者对象。
4.组合构造函数模式和原型模式指针

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['Tom', 'Bob'];
}
Person.prototype = {
    constructor: Person,
    sayName: function () {
        console.log(this.name);
    }
}

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这是目前最普遍,认同度最高的方法。
5.动态原型模式
有其余OO语言经验的开发人员看到独立的构造函数和原型,可能会困惑。动态原型模式则是致力于解决此问题的一个方案,它把全部信息都封装在了构造函数中,在必要状况下,经过在构造函数中初始化原型,保持了组合使用构造函数和原型的优势。code

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['Tom', 'Bob'];
    if (type of this.sayName != 'function') {
        Person.prototype.sayName = function () {
            console.log(this.name);
        }
    } //if语句只需检查一个初始化应该存在的共享属性或方法便可。
}

使用动态原型模式要记得不能用对象字面量重写原型,由于若是在建立了实例的状况下重写原型。那么就会切断现有实例与新原型的联系。
关于寄生构造模式和稳妥构造模式,在工程实践用的很少且稍显过期,就不赘述了。有时间能够了解es6的class。
二.继承
1.原型链对象

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property;
}
function SubType(){
    this.property = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
    return this.property;
}

须要注意的是,在经过原型链实现继承时,不能用对象字面量建立原型方法,由于这样会重写原型链。以下所示:继承

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property;
}
function SubType(){
    this.property = false;
}
SubType.prototype = new SuperType();
SubType.prototype = {
    getSubValue: function () {
        return this.subproperty;
    },
    someOtherMethod: function () {
        return false;
    }
} //这样会致使SubType的实例没法访问到getSuperValue()方法了。

原型链继承的问题:
1.共享实例属性,若是SupeType中定义一个数组colors,当subType经过原型链继承SuperType后,它也会拥有一个colors属性(就像专门建立了一个subType.prototype.colors属性同样),结果是SubType全部实例都会共享这个属性。对其中一个实例.colors属性会影响到全部其余实例。
2.在建立子类型的实例时,没办法在不影响全部对象实例的状况下,向超类型的构造函数传递参数。
所以实践中不多单独使用原型链继承。
2.借用构造函数接口

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
function SubType () 
    SuperType.call(this, 'Lily');
    this.age = 12;
} //使用这种方式就能够向超类型的构造函数传参啦。

借用构造函数的问题:仍是和构造函数建立对象同样,方法都在构造函数定义,函数复用就无从谈起了。
3.组合继承
使用原型链实现对原型属性和方法的继承,而经过构造函数实现对实例属性的继承。

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType (name, age) 
    // 继承实例属性
    SuperType.call(this, name); //第二次调用SuperType()
    this.age = age;
}
//继承方法
SubType.prototype = new SuperType(); //第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
    console.log(this.age);
}
var instance1 = new SubType('Lily', 12);
instance1.colors.push('green');
console.log(instance1.colors); // "red,blue,yellow,green"
var instance2 = new SubType('Lucy', 22);
console.log(instance1.colors); // "red,blue,yellow"

组合继承避免了原型链和借用构造函数的缺陷,是一种较为流行的继承方式。可是它还存在一个缺点:在第一次调用superType时,SubType.prototype会获得两个属性:name和colors,当调用SubType的构造函数时又会再调用一次SuperType构造函数,在对象实例上建立了实例属性name和colors屏蔽了SubType原型中的同名属性。
寄生组合式继承
寄生组合式是组合式继承的改进,思路是没必要为了指定子类的原型而调用超类的构造函数,仅复制超类的原型便可。

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType (name, age) 
    // 继承实例属性
    SuperType.call(this, name); 
    this.age = age;
}
//继承方法
SubType.prototype = Object.create(SuperType.prototype);
//Object.create(obj)至关于 function F(){}; F.prototype = obj;return new F();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
    console.log(this.age);
}

开发人员广泛认为寄生组合式继承是引用类型最理想的继承方法。

相关文章
相关标签/搜索