记录-对象有哪些继承方式

这篇具有很好参考价值的文章主要介绍了记录-对象有哪些继承方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录-对象有哪些继承方式

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;
}

let instance = new SubType()
console.log(instance.getSuperValue()); //true

 以上的代码定义了两个类型:SuperType和SubType。这两个类型分别定义了一个属性和方法。两个类型的主要区别是:SubType通过创建SuperType的实例并将其赋值给自己的原型SubType.prototype实现了对SuperType的继承。这个赋值重写了SubType最初的原型,将其替换成SuperType的实例。这意味着SuperType实例可以访问的所有属性和方法也会存在于SubType.prototype。这样实现继承后,紧接着又给SuperType的实例添加了一个新的方法。最后又创建了SubType的实例并调用了它继承的getSuperValue方法。

默认原型

  默认情况下,所有引用类型都继承自Object,这也是通过原型链实现的。任何函数默认的原型都是一个Object的实例,这意味着这个实例有一个内部指针指向Object.prototype。这也是为什么自定义类型能够继承包括toString()、valueOf在内部的所有默认方法的原因。
  SubType继承SuperType,而SuperType继承Object。在调用instance.toString时,实际上调用的是保存在Object.prototype上的方法。

原型与继承的关系

  原型与继承的关系可以通过两种方式来确定:

使用instanceof操作符:

如果一个实例的原型中出现过相应的构造函数,则instanceof返回true

    console.log(instance instanceof  Object); //true
    console.log(instance instanceof  SuperType); //true
    console.log(instance instanceof  SubType); //true

从技术上讲,instance是Object、SuperType、SubType的实例,因为instance的原型链中包含这些构造函数的原型。结果就是instanceof对所有这些构造函数都返回true。

使用isPrototypeOf()方法

原型链中的每个原型都可以调用这个方法,这要原型链中包含这个原型,这个方法就会返回true

    console.log(Object.prototype.isPrototypeOf(instance)); //true
    console.log(SuperType.prototype.isPrototypeOf(instance)); //true
    console.log(SubType.prototype.isPrototypeOf(instance)); //true

关于方法

子类有时需要覆盖父类的方法,或者增加父类没有的方法。为此,这些方法必须在原型赋值之后再添加到原型上。来看下面的例子:

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;
}

// 覆盖已有的方法
SubType.prototype.getSuperValue = function(){
    return false;
}

let instance = new SubType()
console.log(instance.getSuperValue()); //false

以上代码新增两个方法。第一个方法getSubValue()是SubType的新方法。而第二个方法getSuperValue()是原型链上已经存在但在这里被遮蔽的方法。;后面在SubType实例上调用getSuperValue()时调用的是这个方法。而SuperValue的实例仍然会调用最初的方法。重点在于上述两个方法都是在把原型赋值为SuperType的实例之后定义的。
  另一个要理解的重点,以对象字面量方式创建原型方法,会破环之前的原型链。因为这样相当于重写原型链。

   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(){
            return this.subproperty;
        },
        someOtherMethod(){
            return false;
        }
    }

    let instance = new SubType()
    console.log(instance.getSuperValue()); //instance.getSuperValue is not a function

子类的原型在被赋值为SuperType的实例后,又被一个对象字面量覆盖了。覆盖后的原型是一个Object的实例,而不再是SuperType的实例。因此,之前的原型链就断了。SubType和SuperType之间也没有关系了。

原型链的额问题

原先的实例属性变成了原型属性
  原型包含的引用值会在所有实例间共享,在使用原型实现继承时,原型实际上变成了另一个类型的实例。

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

    }
    // 继承SuperType
    SubType.prototype = new SuperType()

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

    let instance2 = new SubType()
    console.log(instance2.colors); //["red", "blue", "green", "black"]

SuperType构造函数定义了一个colors属性,其中包含一个数组(引用值)。每个SuperType的实例都会有自己的colors属性,包含自己的数组。但是当SubType通过原型继承SuperType后,SubType.prototype变成了SuperType的一个实例,因而也获得了自己的colors属性。这类似于创建了SubType.prototype.colors属性。最终结果是,SubType的所有实例都会共享这个colors属性。这一点通过instance1.colors上的属性也能反应到instance2.colors上就可以看出。

子类型在实例化时不能给夫类型的构造函数传参
  我们无法在不影响所有对象实例的情况下把参数传进父类的构造函数。再加上原型中包含引用值的问题,就导致原型链基本不会被单独使用。

2. 盗用构造函数

  盗用构造函数是用来解决原型包含引用值导致的继承问题。基本思路:在子类构造函数中调用父类构造函数。因为函数是在特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象上下文执行构造函数。

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

function SubType(){
    // 继承SuperType
    /* *********** */
    SuperType.call(this);
    /* *********** */
}

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

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

以上代码展示了盗用构造函数的调用。通过使用call()或者apply()方法,SuperType构造函数在为SubType的实例创建的新对象的上下文中执行了。这相当于新的SubType对象上运行了SuperType函数中的所有初始化代码。结果就是每个实例都会有自己的colors属性。

1. 优点

  可以在子类构造函数中向父类构造函数传参

function SuperType(name){
    this.name = name;
}

function SubType(){
    // 继承SuperType并传参
    SuperType.call(this,"Nicholas");
    // 实例属性
    this.age = 29
}

let instance = new SubType()
console.log(instance.name); //Nicholas
console.log(instance.age); //29

 SuperType构造函数接受一个参数name,然后将它赋值给一个属性。在SubType构造函数中调用SuperType构造函数时传入这个参数,实际上会在SubType的实例上定义name属性。为确保SuperType构造函数不会覆盖SubType定义的属性,可以在调用父类构造函数之后再给子类实例添加额外的属性。

2. 缺点

必须在构造函数中定义方法,因此函数不能重用。
子类不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。
由于存在以上问题,所以盗用构造函数基本上也不能单独使用。

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);
}

let instance1 = new SubType("Nicholas",29)
instance1.colors.push("blcak");
console.log(instance1.colors); //["red", "blue", "green", "blcak"]
instance1.sayName() //Nicholas
instance1.sayAge(); //29

let instance2 = new SubType("Greg",27)
console.log(instance2.colors); //["red", "blue", "green"]
instance2.sayName(); //Greg
instance2.sayAge(); //27

SuperType构造函数定义了两个属性name和colors,而它的原型上也定义了一个方法叫sayName()。SubType构造函数调用了SuperType构造函数,传入name参数,然后又定义了自己的属性age。此外,SubType.prototype也被赋值为SuperType的实例。原型赋值之后,又在这个原型上添加了新方法sayAge()。这样就可以创建两个SubType实例,让这两个实例都有自己的属性,包括colors,同时还共享相同的方法。
  组合继承弥补了原型链和盗用构造函数的不足,是JavaScript中使用最多的继承模式。而且组合继承也保留了instanceof操作符和isPrototypeOf()方法识别合成对象的能力。
缺点:
  父类构造函数会被调用两次,一次是在创建子类原型时调用,一次是在子类构造函数中调用。
  本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。

4. 原型式继承

  2006年Douglas Crockford的文章:《JavaScript中的原型式继承》中介绍了一种不涉及严格意义上构造函数的继承方法。作者的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。文章给出了一个函数:

function object(o){
    function F(){}
    F.prototype = o;
    return new F()
}

  这个object方法会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。本质上,object()是对传入对象执行了一次浅复制。

function object(o){
    function F(){}
    F.prototype = o;
    return new F()
}

let person = {
    name:"Nicholas",
    friends:["Shelby",'Court','Van']
};
let anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

let yetAnotherPerson = object(person);
yetAnotherPerson.name = 'Linda';
yetAnotherPerson.friends.push("Barbie")

console.log(person.friends); //["Shelby", "Court", "Van", "Rob", "Barbie"]

ECMAScript通过增加Object.create()方法将原型式继承的概念规范化。这个方法接受两个参数:作为新对象原型的对象,给新对象定义额外属性的对象(可选)。在只有一个参数时,Object.create()与这里的object()方法效果类似。

let person = {
    name:"Nicholas",
    friends:["Shelby",'Court','Van']
}
let anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("Barbie")

console.log(person.friends); //["Shelby", "Court", "Van", "Rob", "Barbie"]

Object.create()的第二个参数与Object.defineProperties()的第二个参数一样:每个新增参数属性都通过各自的描述符来描述。以这种方式添加的属性会遮蔽原型对象上的同名属性。

let person = {
    name:"Nicholas",
    friends:["Shelby",'Court','Van']
}
let anotherPerson = Object.create(person,{
    name:{
        value:'Greg'
    }
});
console.log(anotherPerson.name); //Greg

原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住,属性中包含的引用值始终会在相关对象间共享,跟使用原型模式时一样的。

5. 寄生式继承

  寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。基本的寄生继承模式如下:

function createAnother(original){
    let clone = object(original);   //通过调用函数创建一个新对象
    clone.sayHi = function(){ //以某种方式增强这个对象
        console.log("HI");
    };
    return clone;  //返沪这个对象
}

createAnother()函数接受一个参数,就是新对象的基准对象。这个对象original会被传给object()函数,然后将返回的新对象赋值给clone对象添加一个新方法sayHi()。最后返回这个对象。

function object(o){
    function F(){}
    F.prototype = o;
    return new F()
}

function createAnother(original){
    let clone = object(original);   //通过调用函数创建一个新对象
    clone.sayHi = function(){ //以某种方式增强这个对象
        console.log("Hi");
    };
    return clone;  //返沪这个对象
}

let person = {
    name:"Nicholas",
    friends:["Shelby",'Court','Van']
}

let anotherPerson = createAnother(person);
anotherPerson.sayHi(); //Hi
 

 这个例子基于person对象返回了一个新对象。新返回的anotherPerson对象具有person的所有属性和方法,还有一个新方法叫sayHi()
  寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。object()函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用。
==注意:==通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。

6. 寄生式组合继承

  通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而实取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。

function inheritPrototype(subType,superType){
    let prototype = object(superType.prototype); //创建对象
    prototype.constructor = subType; //增强对象
    subType.prototype = prototype; //赋值对象
}

这个inheritPrototype()函数实现了寄生式组合继承的核心逻辑。这个函数接受到两个参数:子类构造函数和父类构造函数。这个函数内部,第一步是创建一个父类原型的一个副本。然后,给返回的prototype对象设置constructor属性,解决由于重写原型导致默认constructor丢失的问题。最后将新创建的对象赋值给子类型的原型。调用inheritPrototype()就可以实现前面例子中的子类型原型赋值:

 function object(o){
    function F(){}
    F.prototype = o;
    return new F()
}

function inheritPrototype(subType,superType){
    let prototype = object(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);
}

这里只调用了一次SuperType构造函数,避免了SubType.prototype上不必要也用不到的属性,因此可以说这个例子效率更高。而且,原型链仍然保持不变,因此,instanceof操作符和isPrototypeOf()方法正常有效。寄生式组合继承可以说是引用类型继承的最佳方式。

本文转载于:

https://blog.csdn.net/qq_39200185/article/details/115134869

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录-对象有哪些继承方式文章来源地址https://www.toymoban.com/news/detail-433501.html

到了这里,关于记录-对象有哪些继承方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 进一步了解C++函数的各种参数以及重载,了解C++部分的内存模型,C++独特的引用方式,巧妙替换指针,初步了解类与对象。满满的知识,希望大家能多多支持

    C++的编程精华,走过路过千万不要错过啊!废话少说,我们直接进入正题!!!! 函数默认参数 在C++中,函数的形参列表中的形参是可以有默认值的。 语法 : 返回值类型 函数名 (参数 = 默认值){} 示例 : 函数占位参数 C++中函数的形参列表里可以有占位参教,用来做占位

    2023年04月17日
    浏览(56)
  • 每日一道面试题之在java中,有哪些方式可以实现对象的实例化?

    在 Java 中,实例化对象有以下几种方式: 使用 new : 使用 new 可以 直接调用类的构造方法 来实例化对象。 例如: 使用反射: 通过反射机制,可以 在运行时动态地实例化对象 。使用 Class 类的 newInstance() 方法 可以创建一个类的实例。 例如: 使用 clone() 方法

    2024年02月12日
    浏览(35)
  • 【Pytorch】学习记录分享2——Tensor基础,数据类型,及其多种创建方式

    pytorch 官方文档 1. 创建 Creating Tensor: 标量、向量、矩阵、tensor 2. 三种方法可以创建张量,一是通过列表(list),二是通过元组(tuple),三是通过Numpy的数组(array),基本创建代码如下: 张量相关属性查看的基本操作,后期遇到的张量结构都比较复杂,难以用肉眼直接看出,因此

    2024年02月04日
    浏览(52)
  • 大家知道ai绘画古风软件有哪些吗?

    现在,越来越多的人开始关注人工智能在艺术领域的应用,特别是在绘画领域。人工智能的应用不仅可以提高画作的效率,还能够提供高质量的创作体验。在古风绘画领域,ai绘画软件已经成为了越来越多艺术家和爱好者不可或缺的创作方式。这些软件不仅可以让我们快速、

    2024年02月11日
    浏览(53)
  • 分享一个403界面给大家

    先看效果图(说明:小鬼影会飘来飘去,长时间停留会有小惊喜,具体大家跑一下就知道): 代码如下: PS:发现我用文字写太生硬了,干的噎嗓子,干脆在代码里加注释了。

    2024年02月06日
    浏览(45)
  • 分享一个500页面给大家

    先看效果: 再看代码:

    2024年02月06日
    浏览(52)
  • 大家谈的视频体验指标,都有哪些?如何测定?

    在之前一篇的 DevforDev 专栏中,我们的工程师分享了声网的无参考视频评价体系(VQA)的建立与实践经验。建立一套 VQA 体系需要一个较长的过程,在那之前,大家仍然有很多方法可以来判断实时视频的质量。我们今天就来讲讲有哪些行业通用的视频体验指标。 我们一般通过

    2023年04月08日
    浏览(32)
  • 分享三个ai写作生成器给大家

    在当今数字化时代,随着人工智能的快速发展,ai写作软件已经成为越来越多人关注的热门话题。这些创新的软件利用机器学习和自然语言处理等技术,能够生成一些好的文章、博客、新闻稿甚至小说等内容,大大地提升了写作的效率和质量。然而,面对众多的ai写作软件选择

    2024年02月16日
    浏览(56)
  • 这几个ai写作生成器分享给大家

    大家知道吗,现在市场有许多ai写作软件,它们已经能够模仿人类的写作风格,帮大家轻松地生成文章。我对于非常感兴趣,于是就上网查了一下有可以ai写作的软件吗?结果真的找到几款不错的软件,今天借这个机会我将它们分享给你。 以下是今天分享的ai写作软件 一:A

    2024年02月15日
    浏览(57)
  • 必做的高收益自媒体平台,分享给大家

    现在市面上的自媒体平台是非常多的,大大小小加起来也有30好几个,但是要说到哪些平台收益比较高,那就不得不提到下面这个几个主流平台,今天就给大家分享一下收益比较高的几个主流平台。 第一个:百家号 百家号平台目前的收益单价在众多自媒体平台来说真的算是非

    2024年02月12日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包