摘要:组合继承组合继承有时也叫伪经典继承,该继承模式将原型链和借用构造函数的技术结合在一起实现。寄生组合式继承通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
原文地址:JavaScript实现继承
众所周知,JavaScript 这门语言在 ES6 出来之前是没有类(class)这一概念的,所以 JavaScript 中的类都是通过原型链来实现的。同样,使用 JavaScript 也能实现面向对象的实现继承。以下是《高程》(第三版)的读书笔记。
原型链通过原型链实现继承很容易理解,也很简单。将子类的原型指向父类的实例即可。写个简单的例子:
// 父类 function SuperType() { this.property = "super"; } // 在父类的原型上定义方法 SuperType.prototype.getSuperVal = function () { console.log(this.property); }; // 子类 function SubType() { this.property = "sub"; } // 子类继承父类 SubType.prototype = new SuperType(); var instance = new SubType(); console.log(instance.getSuperVal()); // "sub"
对于子类来讲,其原型的指向应该是这样的:SubType -> new SuperType() -> SuperType.prototype -> Object.prototype -> null。
注意:
如果想要给子类添加原型上的方法,需要在子类继承了父类后添加,否则会被父类实例所覆盖。
也不要用对象字面量的方式给子类原型添加新方法,这会使得之前的继承失效。
原型链的问题:
父类的实例属性成为了子类的原型属性,即子类的实例共享了该父类实例的属性,如果该属性是引用类型,则子类的实例对该属性的修改会反映在所有的子类实例上。
在创建子类实例时,不能向父类的构造函数传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给父类的构造函数传递参数。
借用构造函数这个方法是为了解决原型链方式带来的问题,使用十分巧妙,利用了 call 方法。代码实现:
// 父类 function SuperType() { this.users = ["Jack", "Tom"]; } // 子类 function SubType() { // 继承 SuperType.call(this); } var instance1 = new SubType(); var instance2 = new SubType(); instance1.users.pop(); // "Tom" console.log(instance2.users); // ["Jack", "Tom"]
通过借用构造函数解决了共享原型属性导致的问题。同时也可以通过 call 方法给父类传递参数。
借用构造函数的问题:
方法都需要在构造函数(父类或子类)中定义,无法达到函数复用的功能。
组合继承组合继承有时也叫伪经典继承,该继承模式将原型链和借用构造函数的技术结合在一起实现。示例代码:
// 父类 function SuperType(company) { this.company = company; this.staffs = ["Jack", "Tom"]; } // 父类方法 SuperType.prototype.getCompany = function () { console.log(this.company); }; // 子类 function SubType(company, product) { // 继承属性 SuperType.call(this, company); this.product = product; } // 继承方法 SubType.prototype = new SuperType(); // 指向正确的constructor SubType.prototype.constructor = SubType; SubType.prototype.getProduct = function () { console.log(this.product); }; // SubType实例 var instance1 = new SubType("A", "tellphone"); instance1.getCompany(); // "A" instance1.getProduct(); // "tellphone" instance1.staffs.push("Amy"); // ["Jack", "Tom", "Amy"] var instance2 = new SubType("B", "toy"); instance2.getCompany(); // "B" instance2.getProduct(); // "toy" console.log(instance2.staffs); // ["Jack", "Tom"]
从代码的例子可以观察到,组合继承模式可以让子类的多个实例既能拥有自己的属性,又能使用相同的方法,融合了原型链和借用构造函数的优点。
原型式继承原型式继承是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。使用如下函数实现:
function object(obj) { function F() {} F.prototype = obj; return new F(); }
object 函数对传入的对象实现了浅复制。所以对所有由其创建的实例都共享了 obj 对象中的引用属性。
寄生式继承寄生式继承模式和原型式继承模式很相似,创建了一个仅用于封装继承过程的函数,在函数内部增强对象的功能:
function createAnother(obj) { var clone = object(obj); clone.saySomething = function () { alert("Hello world!"); }; return clone; } function object(obj) { function F() {} F.prototype = obj; return new F(); }
通过 createAnother 函数,对对象的功能进行增强,然而这种方式也没有达到函数复用的效果,这一点和构造函数模式一样。
寄生组合式继承通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。寄生组合模式使用寄生模式来实现对父类原型的继承,再将结果指定给子类的原型。其基本模式如下:
function inheritPrototype(subType, superType) { // 返回父类原型副本并赋值给子类原型 subType.prototype = object(superType.prototype); subType.prototype.constructor = subType; }
再来看一个例子:
// 父类 function SuperType(name) { this.name = name; } // 在父类的原型上定义方法 SuperType.prototype.getName = function () { console.log(this.name); }; // 子类 function SubType(name, age) { SuperType.call(this, name); this.age = age; } // 寄生组合继承 inheritPrototype(SubType, SuperType); // 添加子类方法 SubType.prototype.getAge = function () { console.log(this.age); };
和组合继承模式相比,寄生组合式继承模式只调用了一次 SuperType 构造函数,也避免了在 SubType.prototype 上创建多余的属性。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
参考书籍《JavaScript高级程序设计》(第三版)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/85114.html
摘要:首先,需要来理清一些基础的计算机编程概念编程哲学与设计模式计算机编程理念源自于对现实抽象的哲学思考,面向对象编程是其一种思维方式,与它并驾齐驱的是另外两种思路过程式和函数式编程。 JavaScript 中的原型机制一直以来都被众多开发者(包括本人)低估甚至忽视了,这是因为绝大多数人没有想要深刻理解这个机制的内涵,以及越来越多的开发者缺乏计算机编程相关的基础知识。对于这样的开发者来说 J...
摘要:使用构造函数的原型继承相比使用原型的原型继承更加复杂,我们先看看使用原型的原型继承上面的代码很容易理解。相反的,使用构造函数的原型继承像下面这样当然,构造函数的方式更简单。 五天之前我写了一个关于ES6标准中Class的文章。在里面我介绍了如何用现有的Javascript来模拟类并且介绍了ES6中类的用法,其实它只是一个语法糖。感谢Om Shakar以及Javascript Room中...
摘要:如下代码所示,可以使用构造函数来创建父对象,这样做的话,自身的属性和构造函数的原型的属性都将被继承。方法继承自对象这是中构造函数链的一个示例。 代码复用及其原则 代码复用,顾名思义就是对曾经编写过的代码的一部分甚至全部重新加以利用,从而构建新的程序。在谈及代码复用的时候,我们首先可以想到的是继承性。代码复用的原则是: 优先使用对象组合,而不是类继承 在js中,由于没有类的概念,因此实例...
摘要:组合继承前面两种模式的特点类式继承通过子类的原型对父类实例化实现的,构造函数式继承是通过在子类的构造函数作用环境中执行一次父类的构造函数来实现的。 类有三部分 构造函数内的,供实例化对象复制用的 构造函数外的,直接通过点语法添加的,供类使用,实例化对象访问不到 类的原型中的,实例化对象可以通过其原型链间接地访问到,也是供所有实例化对象所共用的。 类式继承 类的原型对象的作用就是为类...
摘要:抽象类是一种声明但是不能使用的类,当使用时就会报错。但是是灵活的,可以在类的方法中手动地抛出错误来模拟抽象类。 抽象类是一种声明但是不能使用的类,当使用时就会报错。在JavaScript中,abstract还是一个保留字,不能像传统的面向对象语言那样轻松创建。但是JavaScript是灵活的,可以在类的方法中手动地抛出错误来模拟抽象类。如下: var Car = function(){...
阅读 2033·2023-04-26 01:33
阅读 1662·2023-04-26 00:52
阅读 1043·2021-11-18 13:14
阅读 5427·2021-09-26 10:18
阅读 2904·2021-09-22 15:52
阅读 1491·2019-08-29 17:15
阅读 3019·2019-08-29 16:11
阅读 1041·2019-08-29 16:11