本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-07-29
function SuperClass () {}
function SubClass() {
this.name = "wangcai";
this.sayHi = function(){
console.log("wangwang")
}
}
SubClass.prototype = new SuperClass();
var s = new SubClass();
// 需要注意的是,s的constructor属性是指向SuperClass的
类式继承的问题
使用类式继承,**子类的原型实际上变成了父类型的实例。原先父类的实例属性也就顺理成章地变成了子类的原型属性了。**此时,原型对象的缺点一样存在,即子类的一个实例对原型属性做出了改动,则会影响到其他所有实例。
第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
有鉴于这两个问题,实践中很少会单独使用原型链。
function SuperClass(id) {
this.books = ['JavaScript', 'Java', 'CSS']
this.id = id;
}
SuperClass.prototype.showBooks = function () {
console.log(this.books);
}
function SubClass(id) {
SuperClass.call(this, id)
}
测试代码如下:
var instance1 = new SubClass(10);
var instance2 = new SubClass(11);
instance1.books.push('html')
console.log(instance1.books); // [ 'JavaScript', 'Java', 'CSS', 'html' ]
console.log(instance2.books); // [ 'JavaScript', 'Java', 'CSS' ]
instance1.showBooks(); // Error
构造函数继承的问题:由于这种方法没有涉及到原型,所以父类的原型方法不会被子类继承。
组合继承避免了类式继承和构造函数继承的缺陷,融合了它们的优点,成为JavaScript
中最常用的继承模式。而且,instanceof
和isPrototypeOf()
也能够用于识别基于组合继承创建的对象。
function SuperClass(name){
this.colors = ["red", "blue", "green"];
this.name = name;
}
SuperClass.prototype.sayHello = function () {
console.log('hello')
}
function SubClass(){
//构造函数继承属性,同时还传递了参数
SuperClass.call(this, "Nicholas");
//实例属性
this.age = 29;
}
// 原型链继承方法 所有的实例将访问同一个方法 达到复用的效果
// 而且也可以在实例中重写这个方法,而不影响到其他实例
SubClass.prototype = new SuperClass();
var instance = new SubClass();
instance.sayHello() // hello
组合继承的问题:使用组合继承会将父类构造函数调用两次,一次是构造函数继承调用的SuperClass.call(this)
,一次是类式继承时调用 new SuperClass()
,所以这不是最完美的方式。
function inheritObj(o) {
function F() {};
F.prototype = o;
return new F();
}
var a = { age: 1 };
var b = inheritObj(a);
console.log(b.__proto__ === a); // true
这是对类式继承的一个封装,其中的过度对象就相当于是类式继承中的子类,只不过在原型式中作为一个过度对象出现的,目的是为了创建要返回的新的实例化对象。
好处是,由于过度类的构造函数无内容,所以开销比较小,使用也比较方便。当然如果你感觉有必要可以加F过度类缓存起来,不必每次创建一个新的过度类,可以直接使用 Object.create()
。
// Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
var a = { age: 1 };
var b = Object.create(a);
console.log(b.__proto__ === a); // true
缺点是,跟类式继承一样,父类对象中值类型的属性被复制,引用类型的属性被公用。
var book = {
name: 'js book',
alikeBook: ['css book', 'html book']
}
var b1 = inheritObj(book);
b1.name = 'java book';
b1.alikeBook.push("python book");
var b2 = inheritObj(book);
b2.name = 'flash book';
b2.alikeBook.push("as book");
console.log(b1.name); // java book
console.log(b1.alikeBook); // [ 'css book', 'html book', 'python book', 'as book' ]
console.log(b2.name); // flash book
console.log(b1.alikeBook); // [ 'css book', 'html book', 'python book', 'as book' ]
原型式继承的问题:依然拥有类式继承的弊病,共享引用类型的值。
寄生式继承是与原型式继承紧密相关的一种思路,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增加对象,在返回这个对象。
var book = {
name: 'js book',
alikeBook: ['css book', 'html book']
}
function createBook(obj) {
var o = Object.create(obj);
o.sayName = function () {
console.log(this.name);
}
return o;
}
测试代码如下:
var b1 = createBook(book);
b1.name = 'java book';
b1.alikeBook.push("python book");
var b2 = createBook(book);
b2.name = 'flash book';
b2.alikeBook.push("as book");
console.log(b1.name); // java book
console.log(b1.alikeBook); // [ 'css book', 'html book', 'python book', 'as book' ]
console.log(b2.name); // flash book
console.log(b1.alikeBook); // [ 'css book', 'html book', 'python book', 'as book' ]
b1.sayName(); // java book
b2.sayName(); // flash book
返回的新对象不仅具有book
的所有属性和方法,而且还有“自己”的sayName()
方法。
在主要考虑为增强对象(为对象添加属性和方法)时,寄生式继承也是一种有用的模式。
寄生式继承的问题
不能做到函数复用而降低效率。
本质上还是原型继承,虽然返回的是新对象,但是还是会有原型继承的弊病,在访问自身没有而原对象有的引用属性时,共享原对象的引用属性的值。
// 改造寄生式继承
function inheritPrototype(subClass, superClass){
var prototype = Object.create(superClass.prototype);
prototype.constructor = subClass;
subClass.prototype = prototype;
}
1.第一步是创建超类型原型的一个副本。
2.第二步是为创建的副本添加constructor
属性,从而弥补因重写原型而失去的默认的constructor
属性。
3.最后一步,将新创建的对象(即副本)赋值给子类型的原型。(解决函数复用的问题)
// 寄生组合式继承
function SuperClass(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperClass.prototype.sayName = function() {
console.log(this.name);
};
function SubClass(name, age){
SuperClass.call(this, name);
this.age = age;
}
inheritPrototype(SubClass, SuperClass);
SubClass.prototype.sayAge = function(){
console.log(this.age);
};
测试代码如下:
var instance1 = new SubClass("jack", 18)
var instance2 = new SubClass("andy", 20)
instance1.colors.push("black")
console.log(instance1.colors); // [ 'red', 'blue', 'green', 'black' ]
console.log(instance2.colors); // [ 'red', 'blue', 'green' ]
instance1.sayAge(); // 18 调用子类自己的方法
instance1.sayName(); // jack 调用继承过来的父类的方法
通过构造函数继承属性,通过寄生式继承继承父类的原型,因为我们要继承的仅仅是父类的原型,所以不需要再调用父类的构造函数,并且我们在构造函数继承中已经调用了父类的构造函数,也能做到向父类的构造函数中传递参数,而且寄生式继承还能增加这个被赋值的副本原型对象(修正constructor指向)。