//*-------------------- 继承 ----------------------*/ //为什么需要继承 //减少重复性的代码 //类式继承 //简单的类声明 /* Class Person */ function Person(name) { this.name = name; } Person.prototype.getName = function () { return this.name; }; var reader = new Person('John'); reader.getName(); //原型链 //创建继承Person的类则要复杂一些 /* Class Author */ function Author(name, books) { Person.call(this, name); //call the superclass's constructor in the scope of this this.books = books; // add an attribute to Author } Author.prototype = new Person(); //set up the prototype chain Author.prototype.constructor = Author; //set the constructor attribute to Author Author.prototype.getBooks = function () { //add a method to Author return this.books; }; //extend函数 //为了简化类的声明,可以把派生子类的整个过程包装在一个名为extend的函数中 function extend(subClass, superClass) { var F = function () { }; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; } /* 添加了一个空函数F,并将用它创建的一个对象实例插入原型链中。这样做可以避免创建超类的新实例,因为它可能会比较庞大,而且有时超类的构造函数有一些副作用,或者会执行一些需要进行大量计算的任务 */ //usage function Person(name) { this.name = name; } Person.prototype.getName = function () { return this.name; }; function Author(name, books) { Person.call(this, name); //Person被固化在了Author类的声明之中 this.books = books; } extend(Author, Person); Author.prototype.getBooks = function () { return this.books; }; /*--------------------------------------------------*/ //更好的extend函数,弱化subClass和superClass之间的耦合 function extend(subClass, superClass) { var F = function () { }; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; //提供了superclass属性,用来存放subClass.superclass.constructor subClass.superclass = superClass.prototype; //确保超类的prototype的constructor属性被正确设置 if (superClass.prototype.constructor === Object.prototype.constructor) { superClass.prototype.constructor = superClass; } } /*--------------------------------------------------*/ //usage function Author(name, books) { Author.superclass.constructor.call(this, name); this.books = books; } extend(Author, Person); Author.prototype.getBooks = function () { return this.books; }; //有了superclass属性,就可以直接调用超类中的方法 Author.prototype.getName = function () { var name = Author.superclass.getName.call(this); return name + ', Author of ' + this.getBooks().join(', '); }; /*----------------- 原型式继承 --------------------*/ /* 使用原型式继承时,并不需要用类来定义对象的结构,只需直接创建一个对象即可。 这个对象随后可以被新的对象重用,这得益于原型链查找的工作机制。 */ var Person = { name:'default name', getName:function () { return this.name; } }; var reader = clone(Person); alert(reader.getName()); //default name reader.name = 'John Smith'; alert(reader.getName()); //John Smith /*--------------------------------------------------*/ function clone(object) { function F() { } F.prototype = object; return new F(); } /*--------------------------------------------------*/ //克隆Person var Author = clone(Person); Author.books = []; Author.getBooks = function () { return this.books; }; //克隆Author var author = []; author[0] = clone(Author); author[0].name = 'Dustin Diaz'; author[0].books = ['JS Design Patterns']; author[0].books.push('author0Book'); author[1] = clone(Author); author[1].name = 'Ross'; author[1].books = ['asdasd']; console.log(author[0].name); //Dustin Diaz console.log(author[0].getName()); //Dustin Diaz console.log(author[0].getBooks()); //JS Design Patterns console.log(author[0].books); //JS Design Patterns console.log(author[1].name); //Ross console.log(author[1].getName()); //Ross console.log(author[1].getBooks()); //asdasd console.log(author[1].books); //asdasd //对继承而来的成员的读和写的不对等性 /* 然而需要注意的是:对于从原型对象继承而来的成员,其读和写具有内在的不对等性。比如对于读取Author.name的时候,如果在没有设置Author.name属性的前,得到的值是Person.name。对于写Author.name的时候,写的却是Author.name属性。就是说读和写分别对不同的对象进行了操作。 这和内存的分配有关。当我们clone(Person)得到一个对象赋值给Author对象时,实际上一个克隆并非其原型对象的一份完全独立的副本,而只是一个以那个对象为原型的空对象而已。var Author = clone(Person); 后,Author是一个空对象,其属性prototype指向Person原型。所以在读取name属性时是读取Person.name。而在设置name时,实际上是个Author添加了一个name属性。我们可以通过hasOwnProperty()来查看成员是否在本对象中。(hasOwnProperty不会查找原型链,只在本对象中查找) */ var Person = { name:"default name", books:['first', 'second'], getName:function () { return this.name; } } var Chinese = clone(Person); Chinese.books.push('new book'); console.log(Chinese.books);//show first,second,new book Chinese.books = [];//需要重新弄多个books数组,否者就是对Person.books操作 Chinese.books.push('other book'); console.log(Chinese.books);//show other book //对于这种类似的问题我们可以在父类中用工厂方法来实现特许成员的创建。 Person.createChildObject = function () { return []; }; Chinese.books = Person.createChildObject(); Chinese.books.push('otherbook'); console.log(Chinese.books);//show other book //如果想覆盖原型对象的子对象中的一个属性值。 //可以通过该子对象设置为一个空对象字面量, //然后对其进行重塑而办到 //但为了弱化对象之间的耦合,任何复杂的子对象都应该使用方法来创建 var CompoundObject = { string:'default value', childObject:{ bool:true, num:10 } }; var compoundObjectClone = clone(CompoundObject); //糟糕的写法,会改变CompoundObject.childObject.num的值 compoundObjectClone.childObject.num = 5; console.log(CompoundObject.childObject.num); //5 //稍好的写法,重写对象,但耦合性高 compoundObjectClone.childObject = { bool:true, num:5 }; //最好的写法 var CompoundObject = {}; compoundOject.string1 = 'default value'; CompoundObject.createChildObject = function () { return { bool:true, num:10 }; }; CompoundObject.childObject = CompoundObject.createChildObject(); var compoundObjectClone = clone(CompoundObject); compoundObjectClone.childObject = CompoundObject.createChildObject(); compoundObjectClone.childObject.num = 5; /** * 继承与封装 * 从现有的类派生出一个子类时,只有公用和特权成员会被承袭下来。 * 门户大开型类是最适合于派生子类的。 * 可以在子类中通过存在父类的特权方法访问父类的私用属性,但子类自身的新实例方法都不能直接访问这些私用属性 */ /** * 掺元类 * 如果想把一个函数用到多个类中,可以通过扩充(augmentation)的方式让这些类共享该函数。 * 先创建一个包含各种通用方法的类,然后再用它扩充其他类 */ // Mixin class var Mixin = function () { }; Mixin.prototype = { serialize:function () { var output = []; for (key in this) { if (this[key] != arguments.callee) { output.push(key + ':' + this[key]); } } return output.join(', '); } }; //把方法添加到每一个需要它的类中 //实现了多继承的效果 augment(Author, Mixin); var author = new Author('Ross', ['js DP']); var serializedString = author.serialize(); /** * augment函数用一个for..in循环遍历予类(giving class)的prototype中的每一个成员,并将其添加到受类(receiving class)的prototype中。如果存在同名成员,则跳过,处理下一个 * @param {Class} receivingClass * @param {Class} givingClass */ //augment function function augment(receivingClass, givingClass) { for (methodName in givingClass.prototype) { if (!receivingClass.prototype[methodName]) { receivingClass.prototype[methodName] = givingClass.prototype[methodName]; } } } /*--------------------------------------------------*/ //augment函数的改进,可继承指定的方法 function augment(receivingClass, givingClass) { if (arguments[2]) { // only give certain methods for (var i = 2, len = arguments.length; i < len; i++) { receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]; } } else { //give all methods for (methodName in givingClass.prototype) { if (!receivingClass.prototype[methodName]) { receivingClass.prototype[methodName] = givingClass.prototype[methodName]; } } } } /*--------------------------------------------------*/ //适用于各个类的差异大的场合。要是那些类彼此的差异不是那么大,那么普通的继承往往更适合