资讯专栏INFORMATION COLUMN

js知识梳理4.继承的模式探究

wenshi11019 / 2439人阅读

摘要:而且在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。在主要考虑对象而不是自定义类型和构造函数的情况下,这个模式也不错。

写在前面

注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者。有发现什么问题的,欢迎留言指出。

1.原型链

将原型链作为实现继承的方法,基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法:

function SuperType() {
    this.property = true;
}
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 instance = new SubType();
console.log(instance.getSuperValue());//true

原型链实现继承的问题:①问题来自包含引用类型值的原型,因为原来的实例属性变成现在的原型属性,会被共享,②在创建子类时,不能向超类型的构造函数中传递参数。

function SuperType() {
    this.colors = ["red","blue","green"];
}
function SubType() {}
//继承了SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//["red", "blue", "green", "black"]

var instance2 = new SubType();
console.log(instance2.colors);//["red", "blue", "green", "black"]
2.借用构造函数

即是在子类型构造函数的内部调用超类型构造函数(还可以传递参数):

function SuperType(name) {
    this.name = name;
    this.colors = ["red","blue","green"];
}
function SubType() {
    SuperType.call(this,"jaychou");
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//["red", "blue", "green", "black"]
console.log(instance1.name);//jaychou

var instance2 = new SubType();
console.log(instance2.colors);//["red", "blue", "green"]
console.log(instance2.name);//jaychou

借用构造函数的问题:方法都在构造函数中定义,没有进行函数复用。而且在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少多带带使用的。

3.组合继承

即使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性:

function SuperType(name) {
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function () {
    console.log(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 () {
    console.log(this.age);
}

var instance1 = new SubType("jaychou",34);
instance1.colors.push("black");
console.log(instance1.colors);//["red", "blue", "green", "black"]
instance1.sayName();//jaychou
instance1.sayAge();//34

var instance2 = new SubType("xiaoming",15);
console.log(instance2.colors);//["red", "blue", "green"]
instance2.sayName();//xiaoming
instance2.sayAge();//15
4.原型式继承

没有使用严格意义上的构造函数,借助原型可以基于已有的对象创建新对象的特点。同时还不必因此创建自定义类型:

function inherit(p) {
    if(p==null) throw TypeError();
    if(Object.create) return Object.create(p);
    var t = typeof p;
    if(t !== "object" && t !== "funtion") throw TypeError();
    function f() {};
    f.prototype = p;
    return new f();
}

好处:简单直接方便,只是简单地想一个对象与另一个对象保持类似的情况下,原型式继承是很不错的做法。注意点:包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样,如:

var person = {
    name:"jaychou",
    friends:[1,2]
}
var anotherP = inherit(person);
anotherP.friends.push(3);
console.log(person.friends);//[1, 2, 3]
anotherP.name = "xiaoming";
console.log(anotherP.name);//xiaoming
console.log(person.name);//jaychou

所以从本质上讲,原型式继承就是对传过来的对象执行了一次浅复制。

5.寄生式继承

即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象(接上):

function createAnother(original) {
    var clone = inherit(original);
    clone.sayHi = function () {
        console.log("hi");
    }
    return clone;
}
var person = {
    name:"jaychou",
    friends:[1,2]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();

在例子里给传进来的对象增加了方法sayHi,增强了对象。在主要考虑对象而不是自定义类型和构造函数的情况下,这个模式也不错。缺点:不能做到函数复用而降低效率了

6.寄生组合式继承

组合继承的问题就是调用了两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。解决方案就是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型:

//寄生组合式继承
function inheritPrototype(subType, superType) {
    var prototype = inherit(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}
function SuperType(name) {
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
}
function SubType(name,age) {
    //借用超类型的构造函数继承了超类型的实例属性
    SuperType.call(this,name);
    this.age = age;
}
//寄生式继承了超类型的原型方法
inheritPrototype(SubType,SuperType);

SubType.prototype.sayAge = function () {
    console.log(this.age);
}

var instance1 = new SubType("jaychou",34);
instance1.sayName();//jaychou
instance1.sayAge();//34

console.log(SuperType.prototype.isPrototypeOf(instance1));//true
console.log(instance1 instanceof SuperType);//true

好处:①只调用了一处SuperType构造函数,②避免了在SubType.prototype上面创建不必要的、多余的属性。综上,寄生组合式继承是继承的最理想方式。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108369.html

相关文章

  • js知识梳理3:创建对象模式探究

    摘要:起因构造函数对象字面量都可以用来创建单个对象,但有明显缺点使用同一个接口创建很多对象,会产生大量的重复代码。组合使用构造函数模式和原型模式创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。 写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者。有发现什么问题...

    MrZONT 评论0 收藏0
  • javasscript - 收藏集 - 掘金

    摘要:跨域请求详解从繁至简前端掘金什么是为什么要用是的一种使用模式,可用于解决主流浏览器的跨域数据访问的问题。异步编程入门道典型的面试题前端掘金在界中,开发人员的需求量一直居高不下。 jsonp 跨域请求详解——从繁至简 - 前端 - 掘金什么是jsonp?为什么要用jsonp?JSONP(JSON with Padding)是JSON的一种使用模式,可用于解决主流浏览器的跨域数据访问的问题...

    Rango 评论0 收藏0
  • JS核心知识梳理——原型、继承(下)

    摘要:引言上篇文章介绍原型,这篇文章接着讲继承,呕心沥血之作,大哥们点个赞呀明确一点并不是真正的面向对象语言,没有真正的类,所以我们也没有类继承实现继承有且仅有两种方式,和原型链在介绍继承前我们先介绍下其他概念函数的三种角色一个函数,有三种角色。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 上篇文章介绍原型,...

    joyqi 评论0 收藏0
  • JS核心知识梳理——原型、继承(上)

    摘要:同理,原型链也是实现继承的主要方式的只是语法糖。原型对象也可能拥有原型,并从中继承方法和属性,一层一层以此类推。利用构造函数小明张三张三小明缺点每次实例化都需要复制一遍函数到实例里面。寄生构造函数模式只有被类出来的才能用。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 最近又攀登了一下JS三座大山中的第二...

    villainhr 评论0 收藏0
  • 夯实基础-作用域与闭包

    摘要:作用域分类作用域共有两种主要的工作模型。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。词法作用域词法作用域中,又可分为全局作用域,函数作用域和块级作用域。 一篇巩固基础的文章,也可能是一系列的文章,梳理知识的遗漏点,同时也探究很多理所当然的事情背后的原理。 为什么探究基础?因为你不去面试你就不知道基础有多重要,或者是说当你的工作经历没有亮点的时候,基础就是检验你好坏的一项...

    daydream 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<