资讯专栏INFORMATION COLUMN

js最常用和最有效的继承

xiangzhihong / 1133人阅读

摘要:中的继承概述原型链可以用来实现共享,构造函数可以让对象拥有自己的属性,两者结合的组合的组合继承便是最常用的继承方式。不易理解的地方的属性在实例中而不在构造函数中的属性在中而不在构造函数中。

1 前言

我试图用简单、清晰、专业的方式讲清楚js继承这么一个有点复杂的问题

2 继承的精髓 2.1 为什么要继承?

因为需要复用

2.2需要复用什么?该如何复用?

通过复用我们可以实现共享。

从对象的角度来说,我们不想重复写同一段逻辑,所以逻辑需要复用;但是我们不希望一个对象掌管的变量被其它对象修改。所以变量不能共享。也就是要共享函数,不共享属性。

3 js中的继承 3.1 概述

原型链可以用来实现共享,构造函数可以让对象拥有自己的属性,两者结合的组合的组合继承便是最常用的继承方式。

组合继承中有一个弊端,通过简单的改进,寄生组合式继承可以实现最有效的继承。接下来将展开讲解组合继承寄生组合式继承

3.2 预备知识

如果已经了解了这些知识,可以直接跳过

3.2.1 构造函数、实例和原型的关系
function Person () {
      
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job="Software Engineer";
Person.prototype.sayName=function () {
    console.log(this.name);
};
var person1=new Person();
person1.sayName();//Nicholas

构造函数(Person)可以用来创建实例(person1),创建的实例和构造函数共同指向一个原型(Person.prototype),原型的构造器又指向了构造函数。
如果没有指定原型,则默认的原型为Object.prototype

new操作符具体干了什么呢?

创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。var obj = {};

属性和方法被加入到 this 引用的对象中。obj.__proto__ = Base.prototype; (图示中用[[Prorotype]]代表__proto__)

新创建的对象由 this 所引用,并且最后隐式的返回 this 。Base.call(obj);

3.2.2 原型链的屏蔽和动态追加

屏蔽:

//...上面的代码,此处省略
var person2=new Person();
person1.name="Mike";
person1.sayName();//Mike
person2.sayName();//Nicholas

属性值沿着原型链,遵循就近原则。就近的属性值会屏蔽上层原型链的属性值

动态追加而非断开:

误区:javaScript高级程序设计第三版 中指出了 修改原型时 这样一种断开情景

function Person () {
    
}
var friend=new Person();
Person.prototype={
    constructor:Person,
    name:"Nicholas",
    age:29,
    job:"Software Engineer",
    sayName:function () {
        console.log(this.name);
    }
}
friend.sayName();//error

friend的原型丢失了,和它断开了联系。

其实现在已经有所改进,运行friend.sayName();,应该输出为:Nicholas,当修改原型时,语言的设计意图是什么?如果你是语言的设计者到底应该为何种输出?

我认为最好的效果是确保其稳妥性的同时,又不失灵活是最佳的,在这个例子中,创建完原型关系后又想追加原型中的属性,我希望他能够追加成功,因此,希望它输出为:Nicholas

但是,我不希望原型中的属性随意修改,先开始的时候我给他赋予了值,也定义了相应的方法使用这个值,如果这个值在后期随意修改的话,使用这个方法的时候会觉得不知所措。

所以我希望的涉及是原型属性值可以追加但定义后值不能随意修改,如下:

function Person () {
    
}
Person.prototype.name = "Greg";
Person.prototype.age = 24;
Person.prototype.job="Doctor";
var friend=new Person();
Person.prototype={
    constructor:Person,
    name:"Nicholas",
    age:29,
    job:"Software Engineer",
    sayName:function () {
        console.log(this.name);
    }
}
friend.sayName();//Greg

打印friend.__proto__

Object {name: "Greg", age: 24, job: "Doctor"}
age:24
job:"Doctor"
name:"Greg"
sayName:function()
3.2.3 总结

横向上,每个原型的属性 在原型本身被修改时 只是动态追加而不会修改原值;纵向上,原型链继承遵循就近原则。

3.3 组合继承
3.3.1 原型链继承

让一个构造函数A的原型为另一个构造函数B的实例,便形成了A继承B的原型链。

function SuperType() {
    this.property=true;
}
SuperType.prototype.getSuperValue=function () {
    return this.property;
};
function SubType() {
    this.subproperty=false;
};
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function () {
    return this.subproperty;
}
var instance=new SubType();

不易理解的地方:subproperty的属性在实例中而不在SubType构造函数中;property的属性在 SubType.prototype中而不在SuperType构造函数中。

讲解:subproperty和property都是实例属性,在哪里创建了实例就出现在哪里,

优势:函数共享
缺陷:原型链体现出了共享的特性,当一个实例改变了其从原型那里继承来的引用属性值时,其它继承自这个原型属性的值都将被改变。

function SuperType() {
    this.colors=["red","blue","green"];
}

function SubType() {
};
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"]
3.3.2 构造函数继承
function SuperType(name) {
    this.name=name;
}
function SubType() {
    SuperType.call(this,"Nicholas");
    this.age=29;
}
var instance=new SubType();
console.log(instance.name+" "+instance.age);//Nicholas 29

构造函数每个实例的属性都借助构造函数自己生成

优势:每个实例属性各自独立
缺陷:无法共享函数

3.3.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.sayAge=function () {
    console.log(this.age);
}
var instance1=new SubType("Nicholas",29);
instance1.colors.push("black");
console.log(instance1.colors);
instance1.sayName();
instance1.sayAge();
var instance2=new SubType("Greg",27);
console.log(instance2.colors);
instance2.sayName();
instance2.sayAge();

3.4 寄生组合式继承

组合继承虽然实现了需求:共享函数,但不共享属性,可是它是有不足之处:我们在独立属性时只是希望实例有各自的属性就好了,不需要原型(SubType.prototype)中也存在属性,这就多余了。

SubType.prototype存在属性是因为它对SuperType做了实例化继承,我们将实例话继承换成前拷贝继承便可以解决问题:

//将组合继承中的实例化继承:
//SubType.prototype=new SuperType();
//改为浅拷贝继承:
function inheritPrototype(subType,superType){
    var prototype=Object.create(superType.prototype);//创建对象
    prototype.constructor=subType;
    subType.prototype=prototype;
}
inheritPrototype(SubType,SuperType);

Object.create()代码说明:

function Object.create(o){
    function F() {};
    F.prototype=o;
    return new F();
}
参考

javascript高级程序设计 第三版

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

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

相关文章

  • 15个Python库,让你学习数据科学更轻松

    摘要:在本节中,我们将看到一些最流行和最常用的库,用于机器学习和深度学习是用于数据挖掘,分析和机器学习的最流行的库。愿码提示网址是一个基于的框架,用于使用多个或进行有效的机器学习和深度学习。 showImg(https://segmentfault.com/img/remote/1460000018961827?w=999&h=562); 来源 | 愿码(ChainDesk.CN)内容编辑...

    W4n9Hu1 评论0 收藏0
  • 阿里云前端周刊 - 第 17 期

    摘要:接下来本清单还列举了对于接入过滤与防攻击使用合适的方法并且对用户输入进行有效校验避免关键资源外泄设置合理的响应头等等内容。该论文的主要论点是,没有任何一项技术或方法可以能让软件工程的生产力在十年内提高十倍。 推荐 1. Styled-Components 实战 https://hackernoon.com/styled... Styled-Components 是由 Max Stoib...

    bingo 评论0 收藏0
  • 阿里云前端周刊 - 第 17 期

    摘要:接下来本清单还列举了对于接入过滤与防攻击使用合适的方法并且对用户输入进行有效校验避免关键资源外泄设置合理的响应头等等内容。该论文的主要论点是,没有任何一项技术或方法可以能让软件工程的生产力在十年内提高十倍。 推荐 1. Styled-Components 实战 https://hackernoon.com/styled... Styled-Components 是由 Max Stoib...

    gityuan 评论0 收藏0
  • 从零开始单排学设计模式「UML类图」定级赛

    摘要:从零开始单排学设计模式的国服排位之旅,今天正式开启目前段位定级赛这篇文章来总结下类图,本来不打算讲类图的,因为我在学习设计模式的时候,一遇到有关的就会自动忽略,一看感觉就很复杂。关联关系用实现箭头来表示。 阅读本文大概需要 3.5 分钟。 本篇是设计模式系列的开篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。 所以现在打算重写,加上距离现...

    Loong_T 评论0 收藏0

发表评论

0条评论

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