一.建立对象
虽然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); }
开发人员广泛认为寄生组合式继承是引用类型最理想的继承方法。