继承
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // 值类型(基本类型):字符串(string)、数值(number)、布尔值(boolean)、undefined、null // 引用类型:对象(Object)、数组(Array)、函数(Function) /* 原型链 继承 问题:1.包含引用类型值的原型属性会被所有实例共享 数组(引用类型值)。 2.在创建子类型的实例时,不能向超类型的构造函数中传递参数。 */ function SuperType() { this.property = true; // this.colors = ["red","black"]; } SuperType.prototype.colors = ["red", "black"]; SuperType.prototype.getSuperValue = function () { return this.property; }; function SubType() { this.subproperty = false; } //继承了SuperType 原型链继承 SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; var instance1 = new SubType(); var instance2 = new SubType(); instance1.colors.push("green"); console.log(instance1.getSuperValue()); //true console.log(instance1.colors);// ["red", "black", "green"] console.log(instance2.colors);// ["red", "black", "green"] console.log(instance1);// SubType {subproperty: false} console.log(instance1.__proto__);// SuperType {property: true, getSubValue: ƒ} /* 借用构造函数 继承 候也叫做 伪造对象 或 经典继承 问题:方法都在构造函数中定义,因此函数复用就无从谈起了 */ function SuperType2(name) { this.name = name; this.colors = ["red", "blue", "green"]; this.sayName = function () { console.log(this.name); } } function SubType2() { //继承了SuperType 构造继承 // this -> SubType // SuperType2.call(this); // 传递参数 SuperType2.call(this, "赵品霖"); } //继承了SuperType // SubType.prototype = new SuperType(); var instance3 = new SubType2(); var instance4 = new SubType2(); instance3.colors.push("black"); console.log(instance3.colors); // ["red", "blue", "green", "black"] console.log(instance4.colors); //["red", "blue", "green"] console.log(instance3.sayName == instance4.sayName); //false 函数无法复用 console.log(instance3);//SubType2 {name: "赵品霖", colors: Array(4), sayName: ƒ} console.log(instance3.__proto__);//{constructor: ƒ} /* 组合继承 思想:即在子类型构造函数的内部调用超类型构造函数 instanceof 和isPrototypeOf()也能够用于识别基于组合继承创建的对象。 问题:无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。 解决方法 ——寄生组合式继承。 */ function SuperType3(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType3.prototype.sayName = function () { console.log(this.name); } function SubType3(name, age) { //继承属性 构造继承 SuperType3.call(this, name);////第二次调用SuperType() this.age = age; } //继承方法 原链继承 SubType3.prototype = new SuperType3();////第一次调用SuperType() SubType3.prototype.constructor = SubType3; SubType3.prototype.sayAge = function () { console.log(this.age); }; // 实例既分别拥有自己属性——包括colors 属性,又可以使用相同的方法了。 var instance5 = new SubType3("aa", 20); var instance6 = new SubType3("bb", 21); instance5.colors.push("black"); console.log(instance5.colors); // ["red", "blue", "green", "black"] console.log(instance6.colors); //["red", "blue", "green"] instance5.sayName();//aa instance6.sayName();//bb console.log(instance5.sayName == instance6.sayName); //true console.log(instance5); //SubType3 {name: "aa", colors: Array(4), age: 20} console.log(instance5.__proto__); //SuperType3 {name: undefined, colors: Array(3), constructor: ƒ, sayAge: ƒ} /* 原型式继承 借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型 Object.create(person); 在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。 不过别忘了,包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。 */ var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; // ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。 // 这个方法接收两个参数:一个用作新对象原型的对象和(可选的); 一个为新对象定义额外属性的对象 var anotherPerson = Object.create(person); // 重写name anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); // var yetAnotherPerson = Object.create(person); // yetAnotherPerson.name = "Linda"; // Object.create() 接受第2个参数 var yetAnotherPerson = Object.create(person, { name: { value: "Linda" } }); yetAnotherPerson.friends.push("Barbie"); console.log(person.friends); //["Shelby", "Court", "Van", "Rob", "Barbie"] /* 寄生式继承: 寄生式继承的思路与寄生构造函数和工厂模式类似. 即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。 在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前 */ function createAnother(original) { var clone = Object(original); //通过调用函数创建一个新对象 clone.sayHi = function () { //以某种方式来增强这个对象 console.log("hi"); }; return clone; //返回这个对象 } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi" /* 寄生组合式继承 所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法. 基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。 本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。 它只调用了一次SuperType 构造函数,并且因此避免了在SubType.prototype 上面创建不必要的、多余的属性。 与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和isPrototypeOf()。 开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。 */ function inheritPrototype(subType, superType) { var prototype = Object(superType.prototype); //创建对象 寄生式继承来继承超类型的原型 // 弥补因重写原型而失去的默认的constructor 属性 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 将结果指定给子类型的原型。 } function SuperType4(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType4.prototype.sayName = function () { console.log(this.name); }; function SubType4(name, age) { SuperType4.call(this, name); this.age = age; } inheritPrototype(SubType4, SuperType4); SubType4.prototype.sayAge = function () { console.log(this.age); }; var instance7 = new SubType4("ee", 19); var instance8 = new SubType4("ff", 22); instance7.colors.push("black"); console.log(instance7.colors);// ["red", "blue", "green", "black"] console.log(instance8.colors);// ["red", "blue", "green"] console.log(instance7.sayName == instance8.sayName);// true console.log(instance7.sayAge == instance8.sayAge);// true </script> </body> </html>
寄生组合式继承 示例: