JS--面向对象的程序设计

来源:互联网 发布:windows电脑如何截屏 编辑:程序博客网 时间:2024/06/03 05:41

面向对象的程序设计

  • 理解对象属性
  • 理解并创建对象
  • 理解继承

理解对象

属性类型

ECMAScript中有两个属性:数据属性和访问器属性
  • 数据属性:数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问属性。默认值为true
[[Enumerable]]:表示能否通过for-in循环返回属性。默认值为true
[[Writable]]:表示能否修改属性的值。默认值为true
[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为undefined。
要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法。这个方法接受三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是:
configurable、enumerable、writable和value。设置其中的一或多个值,可以修改对应的特性值。
var person = {};Object.defineProperty(person, "name", {     writable: false,  //不能修改属性的值     value: "Nicholas"});alert(person.name);     //"Nicholas"person.name = "Greg";  alert(person.name);     //"Nicholas"
writable:false为不能修改属性的值。严格模式下赋值操作会报错。其他特性一样。
注意:configurable特性设置为false后就不可以配置了,后面修改configurable无效。
  • 访问器属性:访问器属性不包含数据值;它们包含一对儿getter和setter函数。在读取访问器属性时,会调用getter函数;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问属性。默认值为true
[[Enumerable]]:表示能否通过for-in循环返回属性。默认值为true
[[Get]]:在读取属性时调用的函数。默认值为undefined
[[Set]]:在写入属性时调用的函数。默认值为undefined
访问器属性不能直接定义,必须使用Object.defineProperty()来定义

var book = {_year: 2004,edition: 1};Object.defineProperty(book, "year", {get: function(){return this._year;},set: function(){if (newValue){this._year = newValue;this.edition +=newValue - 2004;}}});book._year = 2005;alert(book.edition);  //2
以上创建了一个book对象,并定义了两个默认属性:_year和edition。_year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。
在不支持Object.defineProperty()方法的浏览器中不能修改[[Configurable]]、[[Enumerable]]

定义多个属性

ECMAScript5定义了Object.defineProperties()方法。这个方法接受两个参数。第一个对象是要添加和修改其属性的对象。第二个对象的属性与第一个对象中要添加或修改的属性一一对应。
例如:
var book = {};Object.defineProperties(book, {_year: {value:2004},edition: {value: 1},year: {get: function(){return this._year;},set: function(newValue) {if(newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}}});
以上代码在book对外上定义了两个数据属性(_year和edition)和一个访问器属性(year)。唯一的区别是这里的属性都是在同一时间创建的。

读取属性的特性

使用ECMAScript5的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。这个方法接受两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个
对象,如果是访问器属性,这个对象的属性有configurable、enumrable、get和set;如果是数据属性,这个对象的属性有configurable、enumrable、writable和value。
var book = {};Object.defineProperties(book, {_year: {value:2004},edition: {value: 1},year: {get: function(){return this._year;},set: function(newValue) {if(newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}}});var descriptor = Object.getOwnPropertyDescriptor(book, "_year");   //访问器属性alert(descriptor.value);           //2004alert(descriptor.configurable);    //falsealert(descriptor.get);             //"undefined"var descriptor = Object.getOwnPropertyDescriptor(book, "year");    //数据属性alert(descriptor.value);           //undefinedalert(descriptor.configurable);    //falsealert(typeof descriptor.get);      //"function"

创建对象

工厂模式


用函数来封装以特定接口创建对象的细节
function createPerson(name, age, job){var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){alert(this.name);}return o;}var person1 = createPerson("Nicholas", 29, "Software Engineer");var person2 = createPerson("Greg", 27, "Doctor");
工厂模式虽然解决了创建多个相似对象的问题,但是没有解决对象识别的问题。

构造函数模式

构造函数可用来创建特定类型的对象。(像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中。),可以使用构造函数模式将前面的例子重写如下:
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = function(){alert(this.name);};}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");
与工厂模式不同的是:
  • 没有显式地创建对象
  • 直接将属性和方法赋给了this对象
  • 没有return语句
构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头
创建Person的新实例,必须使用new操作符。以这种方式调用构造函是实际上会经历以下4个步骤:
  • 创建一个新对象
  • 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  • 执行构造函数中的代码(为这个新对象添加属性)
  • 返回新对象
对象的constructor属性最初是用来标识对象类型的
alert(person1.constructor == Person);  //truealert(person2.constructor == Person);  //true
也可以通过instanceof操作符可以得到验证:
alert(person1 instanceof Object);  //truealert(person2 instanceof Person);  //truealert(person1 instanceof Object);  //truealert(person2 instanceof Person);  //true
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方
  1. 将构造函数当作函数
构造函数与其他函数的唯一区别,就在于调用它们的方式不同。任何函数,只要通过new操作符来调用,那它就可以作为构造函数
// 当作构造函数使用
function Person(name, age, job){     this.name = name;     this.age = age;     this.job = job;     this.sayName = sayName //与声明函数在逻辑上是等价的}function sayName(){     alert(this.name);}

var person = new Person("Nicholas", 29, "Software Engineer");person.sayName(); //"Nicholas";// 作为普通函数调用Person("Greg", 27 ,"Doctor"); //添加到windowwindow.sayName(); //"Greg"// 在另一个对象的作用域中调用var o = new Object();Person.call(o, "Kristen", 25, "Nurse");o.sayName(); //"Kristen"
在使用call()(或者apply())在某个特殊对象的作用域调用Person()函数。这里是在对象o的作用域中调用
2.构造函数的问题
使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。在前面的例子中,person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个
Function的实例。ECMAScript中的函数是对象。
function Person(name, age, job){     this.name = name;     this.age = age;     this.job = job;     this.sayName = new Function("alert(this.name)"); //与声明函数在逻辑上是等价的}

以上面的方式创建函数,会导致不同的作用域链和标识符解析,但创建Function新实例的机制仍然是相同的。因此,不同实例上的同名函数是不相等的。
以下函数可以解决,通过把函数定义转移到构造函数外部来解决这个问题:
function Person(name, age, job){     this.name = name;     this.age = age;     this.job = job;     this.sayName = sayName }function sayName(){     alert(this.name);}
新问题:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而且,如果对象需要定义很多方法,那么就要定义很多个全局函数。

原型模式

创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处就是可以让所以对象实例共享它所包含的属性和方法。
function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Sofyware Engineer";Person.prototype.sayName = function(){alert(this.name);};var person1 = new Person();person1 .sayName();  //"Nicholas"var Person = new Person();person1.sayName();  //"Nicholas"alert(person1.sayName == person2.sayName);  //true
将sayName()方法和所以属性直接添加到Person的prototype属性,构造函数就变成了空函数。与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。也就是
说person1和person2访问的都是同一组属性和同一个sayName()函数。
1. 理解原型对象
在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。Person.prototype.constructor指向Person。
创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;其他方法都是从object继承而来的。这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
  • Person.prototype指向了原型对象。而Person.prototype.constructor又指回了Person构造函数。而实例与构造函数没有直接的对象。实例的[[prototype]]属性指向原型对。可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。
    alert(Person.prototype.isPrototypeOf(person1));  //truealert(Person.prototype.isPrototypeOf(person2));  //true

  • ECMAScript5新增了一个新方法,叫Object.getPrototyOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值
    alert(Object.getPrototypeOf(person1) == Persong.Prototype);  //truealert(Object.getPrototypeOf(person1).name);  //"Nicholas"
注意:
  • 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性
  • 虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那么我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。
使用hasOwnPrototype()方法可以检测一个属性存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例时,才会返回true
function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Sofyware Engineer";Person.prototype.sayName = function(){alert(this.name);};var person1 = new Person();var Person = new Person();alert(person1.hasOwnPrototype("name"));  //falseperson1.name = "Greg";alert(person1.name);      //"Greg"---来自实例alert(person1.hasOwnPrototype("name"));  //truealert(person2.name);      //"Nicholas"---来自原型alert(person1.hasOwnPrototype("name"));  //false
2.原型与in操作符
有两种方式使用in操作符
  • 单独使用
  • for-in循环中使用
  1. 单独使用时,in操作符会在通过对象能够给定属性时返回true,无论该属性存在于实例中还是原型中。同时使用hasOwnPrototype()方法和in操作符,就可以确定该属性到底是存在对象中,还是存在于原型中。
  2. 在使用for-in循环时,返回的时所有能够通过对象访问的、可枚举的(enumerated)属性。
var o = {toString : function(){return "My Object";}};for(var prop in o){if(prop == "toString"){alert("Found toString");    //在IE中不会显示}}
这个的对象o定义了一个名为toString()的方法,该方法屏蔽了原型中(不可枚举)的toString()方法。在IE中,由于其实现认为原型的toString()方法被打上了false的[[enuerable]]标记。该bug会影响默认不可枚举的所有属性和方法。
  1. 要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法,这个方法接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
    function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Sofyware Engineer";Person.prototype.sayName = function(){alert(this.name);};var keys = Object.keys(Person.prototype);alert(keys);               //"name,age,job,sayname"var p1 = new Person();p1.name = "Rob";p1.age = 31;var p1keys = Object.keys(p1);alert(p1keys);   //"name,age"
    如果想要取得所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法。
    var keys = Object.getOwnPropertyNames(Person.prototype);alert(keys);   //"constructor,name,age,job,sayName"
3. 更简单的原型语法
更常见的做法就是用一个包含所有属性和方法的对象字面量来重写整个原型对象
function Person(){}Person.prototype ={name : "Nicholas",age : 29,job : "Sofyware Engineer",sayName : function(){alert(this.name);}};
在上面代码中,将Person.prototype设置为等于一个以对象字面量形式创建的新对象。但是,constructor属性不再指向Person。这里的constructor属性指向Object函数可以这样添
加一行代码
function Person(){}Person.prototype ={        constructor :Person,name : "Nicholas",age : 29,job : "Sofyware Engineer",sayName : function(){alert(this.name);}};
以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true。可以通过下面代码修改:
//重设构造函数,只适用于ECMAScript5兼容的浏览器Object.defineProperty(Person.prototype, "constructor", {enumerable : false,value : Person});
4.原型的动态性
由于在原型中查找值的过程是一次搜索,因此对原型对象所做的任何修改都能够立即从实例中反映出来--即使是先创建了实例后修改原型也照样如此。
var friend = new Person();Person.prototype.sayHi = function (){    alert("hi");};friend.sayHi();   //"hi"
这归结于实例与原型之间的松散连接关系。因为实例与原型之间的连接只不过是一个指针,而非一个副本。
注意:实例中的指针仅指向原型,而不指向构造函数。
5. 原生对象的原型
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。
可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。
6.原型对象的问题
对于包含引用类型值的属性来说,就是创建的实例通过在实例中创建相同名的属性来修改原型对象中的引用类型的值。其他的实例也有这个属性就会同时改变。它们指向同样的引
用类型的值

组合使用构造函数模式和原型模式

创建自定义类型的最常见的方式,就是组合使用构造函数模式与原型模式,这样每个实例都会有自己的一份实例属性的副本。这种混成模式还支持向构造函数传递参数

function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.friends = ["Shelay", "Count"];}Person.prototype = {constructor : Person,sayName : function(){alert (this.name);}}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.friends.push("Van");alert(persoon1.friends);    //"Shelay,Count,Van"alert(persoon2.friends);    //"Shelay,Count"alert(person1.friends == person2.friends);  //falsealert(person1.sayName == person2.sayName);  //true

动态原型模式

function Person(name, age, job){//属性this.name = name;this.age = age;this.job = job;//方法if (typeof this.sayName != "function"){Person.prototype.sayName =function(){alert(this.name);};}}var friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName();
注意这里加粗的部分,这里只在sayName()方法不存在的情况下,才会把它添加到原型中。

寄生构造函数模式

function Person(name, age, job){var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function(){alert(this.name);};return o;}var friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName();    //"Nicholas"
注意:返回的对象与构造函数或者与构造函数的原型属性之间没有关系。也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

稳妥构造函数模式

与寄生构造函数的不同:
  1. 新创建对象的实例方法不引用this
  2. 不使用new操作符调用构造函数
function Person(name, age, job){    //创建要返回的对象var o = new Object();//可在这里定义私有变量和方法//添加方法o.sayName = function(){alert(this.name);};//返回对象return o;}var friend = Person("Nicholas", 29, "Software Engineer");  //不使用new操作符调用构造函数friend.sayName();    //"Nicholas"
变量friend中保存的是一个稳妥对象,而除了调用sayName()方法外,没有别的方法可以访问其数据成员

继承

ECMAScript只支持实现继承,而且其实现继承主要的依靠原型链来实现的。

原型链

ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的的属性和方法。
实现原型链有一种基本模式,其代码大致如下:
function SuperType(){this.prototype = true;}superType.prototype.getSuperValue = function(){return this.property;};function.SubType(){this.subproperty = false;}//继承了SuperTypeSubType.prototype = new SuperType()SubType.prototype.getSubValue = function (){return this.subproperty;};var instance = new SubType();alert(instance.getSuperValue());   //true
定义两个类型:SuperType和SubType。每个类型分别有一个属性的方法。它们主要区别是SubType继承SuperType,而继承是通过创建SuperType的实例,并将该实例赋给
SubType.prototype。实现的本质是重写原型对象,代之以一个新类型的实例。
别忘记默认的原型
所有引用类型都继承了Object,而这个继承也是通过原型链实现的。
所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也就是所有自定义类型都会继承toString()、valueof()等默认方法的
根本原因
确定原型和实例的关系
  • 使用instanceof操作符,只要用这个操作符来测试实例与原型链中出现过的函数,结果就会返回true
alert(instance instanceof Object);  //truealert(instance instanceof SuperType);  //truealert(instance instanceof SubType);  //true
  • isPrototypeOf()方法
alert(Object.prototype.isPrototype(instance));  //truealert(SuperType.prototype.isPrototype(instance));  //truealert(SubType.prototype.isPrototype(instance));  //true
谨慎的定义方法
子类型有时候需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎样,给原型添加方法的代码一定要放在替换原型的语句之后。
function SuperType(){this.prototype = true;}superType.prototype.getSuperValue = function(){return this.property;};function.SubType(){this.subproperty = false;}//继承了SuperTypeSubType.prototype = new SuperType()//添加新方法SubType.prototype.getSubValue = function(){return this.subproperty;};//重写超类型中的方法SubType.prototype.getSuperValue = function (){return false;};var instance = new SubType();alert(instance.getSuperValue());   //false
第一个方法getSubValue()被添加到了SubType中。第二个方法ge'SuperValue()是原型链中已经存在的一个方法,但重写这个方法将会屏蔽原来的方法。
换句话说,当通过SuperType的实例调用gtSuperValue()时,调用的就是这个重新定义的方法;但通过SuperType的实例调用getSuperValue()时,还会继续调用原来的那个方法。
必须在用SuperType的实例替换原型之后,再定义这两个方法。

还有在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做就会重写原型链。
function SuperType(){this.prototype = true;}superType.prototype.getSuperValue = function(){return this.property;};function.SubType(){this.subproperty = false;}//继承了SuperTypeSubType.prototype = new SuperType()//使用字面量添加新方法,会导致上一行代码无效SubType。prototype = {getSubValue : fucntion(){return this.subproperty;},someOtherMethod : function(){return false;}};var instance = new SubType();alert(instance.getSuperValue());   //false
上面的原型链已经被切除了---SubType和SuperType之间已经没有关系了
原型链的问题
  • 最主要的问题就是包含引用类型的原型。包含引用类型值的原型属性会被所有实例共享;而这也正是为什么要在构造函数中,而不是在原型对象定义属性的原因。
function SuperType(){this.color = ["red", "blue", "green"];}function SubType(){}//继承了SuperTypeSubType.prototype = new SuperType();   //都继承了color属性var instance1 = new SubType();instance1.colors.push("black");alert(instance1.colors);  //"red", "blue", "green", "black"var instance2 = new SubType();alert(instance2.colors);    //"red", "blue", "green", "black"
SubType.prototype就变成了SuperType的一个实例,结果就是SubType的所有实例都会共享这个color属性。修改instance1,就会从instance2中反映出来
  • 在创建子类型的实例时,不能向超类型的构造函数中传递参数。

借用构造函数

借用构造函数(伪造对象或经典对象):即在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法也可以在新创建的对象上执行构造函数:
function SuperType(){this.color = ["red", "blue", "green"];}function SubType(){//继承了SuperTypeSuperType.call(this);}var instance1 = new SubType();instance1.colors.push("black");alert(instance1.colors);  //"red", "blue", "green", "black"var instance2 = new SubType();alert(instance2.colors);    //"red", "blue", "green"
在新创建的SubType实例的环境下调用了SuperType构造函数。
  • 传递参数
借用构造函数可以在子类型构造函数中向超类型构造函数传递参数  
function SuperType(){this.name = name;}function SubType(){//继承了SuperType,还同时传递了参数SuperType.call(this, "Nicholas");this.age = 29;}var instance= new SubType();alert(instance2.name);     //"Nicholas"    alert(instance2.age);    //29
  • 借用构造函数的问题
方法都在构造函数中定义,因此函数复用就无从谈起

组合继承(伪经典继承)

也就是指将原型链和借用构造函数技术组合到一起。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function SuperType(name){this.name = name;this.color = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){alert(this.name);};function SubType(name, age){//继承属性SuperType.call(this, name);this.age = age;}//继承方法SubType.prototype = new SuperType();SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function(){alert(this.age);};var instance1 = new SubType("Nicholas", 29);instance1.color.push("black");alert(instance1.colors);    //"red, blue, green", black"instance1.sayName();   //"Nicholas"instance1.sayAge();  //29var instance2 = new SubType("Greg", 27);alert(instance2.colors);   //"red, blue, green"instance2.sayName();       //"Greg"instance2.sayAge(); //27
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点。而且,instnceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。

原型式继承

借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型

function object(o){   function F(){}   F.prototype = o;   return new F();}
在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。
ECMAScrpit5通过新增Object.create()方法规范了原型继承。这个方法接受两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。
var person = {name: "Nicholas",friends: ["Shelay", "Count", "Van"]};var anotherPerson = Object.create(person);anotherPerson.name = "Greg";anotherPerson.friends.push("Rob");var yetAnotherperson = Object.create(person);yetAnotherperson.name = "Linda";yetAnotherperson.friends.push("Barbie");alert(person.friends);    //"Shelay,Count,Van,Rob,Barbie"var person = {name: "Nicholas",friends: ["Shelay", "Count", "Van"]};var anotherPerson = Object.create(person);anotherPerson.name = "Greg";anotherPerson.friends.push("Rob");var yetAnotherperson = Object.create(person);yetAnotherperson.name = "Linda";yetAnotherperson.friends.push("Barbie");alert(person.friends);    //"Shelay,Count,Van,Rob,Barbie"
Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种方式增强对象,最后再像真地是它做了所有工作一样返回对象
function createAnother(original){var clone = object(original);     //通过调用函数创建一个新对象clone.sayHi = function(){         //以某种方式来增强这个对象alert("hi");};return clone;            //返回这个对象}var person = {name: "Nicholas",friends: ["Shelay", "Count", "Van"]};var anotherPerson = createAnother(person);anotherPerson.sayHi();  //"hi"
这个例子中的代码是基于person返回了新对象---anotherPerson。新对象不仅具有person的所有属相和方法,而且还有自己的sayHi().

寄生组合式继承

function SuperType(name){this.name = name;this.color = ["red", "blue", "green"];}SuperType.pertotype.sayName = function(){alert(this.name);};function SubType(name, age){SuperType.call(this, name);      //第二次调用SuperType()this.age = age;}SubType.prototype = new SuperType();     //第一次调用SuperTypeSubtype.prototype.conStructor = SubType;SubType.prototype.sayAge = function(){alert(this.age);};
当调用SubType构造函数时,又会调用一次SuperType构造函数。

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方式。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(subType, supertype){var prototype = object(superType.prototype);    //创建对象prototype.constructor = subType;          //增强对象subType.prototype = prototype;//指定对象}
这个示例中的inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接受两个参数:子类构造函数和超类构造函数。


原创粉丝点击