第3集丨JavaScript 使用原型(prototype)实现继承——最佳实战1

这篇具有很好参考价值的文章主要介绍了第3集丨JavaScript 使用原型(prototype)实现继承——最佳实战1。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

JavaScript中, 继承机制的基础是原型,(包括内部原型__proto__prototype)。当访问一个对象的属性时,JavaScript 引擎是这么搜索的:如果在本对象中找不到一个属性时,就会去其原型对象中找,如果原型对象中还没找到的话,就去到原型对象中的原型中去找,直到顶级父类Object对象为止。

纸上得来终觉浅,绝知此事要躬行。所以,无论是什么知识,如果不在实践中去进行学习,最终必然是无法深入理解。

接下来,就进入本集的重点部分,用上集学习到的知识实现原型继承,姑且将其称之为最佳实战。

一、需求说明

  • 定义三个类ShapeTwoDShape和、Triangle
  • 实现 :TwoDShape 继承 ShapeTriangle继承 TwoDShape

二、普通实现

下面代码部分展示了一种我们最常见的写法。通过new 父类方式类实现原型继承。

function Shape(){
    this.name = "Shape";
    this.toString = function () {
        return this.name;
    }
}
function TwoDShape(){
    this.nam = "2D Shape";
}
TwoDShape.prototype = new Shape();
TwoDShape.prototype.constructor = TwoDShape;
function Triangle(side, height){
    this.name = "Triangle";
    this.side = side;
    this.height = height;
    this.getArea = function () {
        return this.side * this.height / 2;
    }
}
Triangle.prototype = new TwoDShape();
Triangle.prototype.constructor = Triangle;
var my = new Triangle(5, 10);
console.log(my.toString());  // Triangle
console.log(my.__proto__);	// TwoDShape {nam: '2D Shape', constructor: ƒ}
console.log(my.constructor);// Triangle(side,height)

2.1 代码分析

JavaScript 引擎在my.toString() 被调用究竟做了哪些事情?

在上面的示例中,过程大概是这样的:

  • 他首先会遍历my 对象中的所有属性,但是么有找到一个叫做toString()的方法
  • 接下来,再去查看my.__proto__所指向的对象,该对象是在继承关系构建的过程中由new TwoDShape() 所创建的实体,显然JavaScript 引擎在遍历TwoDShape实体的过程中依然不会找到toString()方法,然后它又会检查该实体__proto__属性,这个时候该__proto__属性所指向的实体是由new Shape()创建的。
  • 终于,在new Shape()所创建的实体中找到了toString()方法
  • 最后,该方法就会在my对象中被调用,并且this指向了my

所以本次的总共进行了3次搜索

  • 第1次是在本对象中找,
  • 第2次是在其原型对象即TwoDShape对象中找,
  • 第3次是在TwoDShape对象的原型对象Shape实例中找,最终找到了。

所以可以推测new关键字做的复制的工作,并不是真正的复制原型对象上的属性,而只是建立指针链接指向它。我们在查找的时候还是要一层一层往上找。

如果我们向my 对象询问:“您的构造器函数是哪一个?”,它应该是能够给出正确答案的,因为我们在构建继承关系的时候已经对相关的constructor属性进行了重置。如果不重置的话就会指向了TwoDShape()

2.2 如何回溯原型

  • 因为我们重置了constructor属性my.constructor.prototype(是my对象的原型)
    这样我们就么有办法通过my.constructor.prototype.constructor.prototype…
    回溯到原型。
  • 如果没有进行constructor属性的修正,只能通过对象内部的原型链进行回溯:my.__proto__.__proto__.__proto__....

三、将共享属性迁移到原型中

当我们用某一个构造器创建对象时,其属性就被添加到this中去,这会使某些不能通过实体改变的属性出现一些效率低下的情况。Shape()构造器是这样定义的

function Shape(){
	this.name = “shape”;
}

这种实现意味着当我们用new Shape()新建对象时,每个实例都会有一个全新的name属性,并在内存中拥有自己独立的存储空间。而事实上,我们也可以将name属性添加到所有实例所共享的的原型对象中去:

function Shape(){}
Shape.prototype.name = “shape”;

这样一来,每当我们在用new Shape()新建对象时,新对象中就不在含有自己的name属性了,而是被添加到了该对象的原型中。虽然这样做通常会更有效率,但这也是只针对对象实体中的不可变的属性而言,另外这种方式也同样适合对象中的共享性方法。

注意点:我们在对原型对象扩展之前,先要完成相关的继承关系构建。

3.1 代码分析

那么此时会使得my.toString()方法查找有什么不同么?

  • 首先,JavaScript引擎查看my对象中有么有toString()方法,自然是找不到,
  • 于是它就去搜索该对象的原型属性。此时该原型属性是TwoDShape的一个实例对象,也找不到,
  • 在去这个实例对象的原型中去找,就在TwoDShape.prototype中找,而TwoDShape.prototype这时是Shape的一个实例,自然也找不到,
  • 于是就去Shape的原型中找,最终找到了。

总计要4步的搜寻才能找到

  • 在本对象中找
  • 在TwoDShape实例对象中找
  • 在Shape实例中找
  • 在Shape原型中找,找到了

3.2 实现代码

function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {
    return this.name;
}
function TwoDShape(){}
TwoDShape.prototype = new Shape();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.prototype.name = "2D Shape";
function Triangle(side, height){
    this.side = side;
    this.height = height;
}
Triangle.prototype = new TwoDShape();
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {
    return this.side * this.height / 2;
}
var my = new Triangle(5, 10);
console.log(my.getArea());  //25
console.log(my.toString()); //Triangle

//用hasOwnProperty()方法来明确对象自身属性和原型链属性
console.log(my.hasOwnProperty('side')); //true
console.log(my.hasOwnProperty('name')); //false
//用isPrototypeOf()方法来判断一个原型对象是不是实例的原型
console.log(Shape.prototype.isPrototypeOf(my)); //true
console.log(TwoDShape.prototype.isPrototypeOf(my)); //true
console.log(Triangle.prototype.isPrototypeOf(my));  //true
//用instanceof()方法来,用来测试一个对象是不是由某个指定的构造器函数所创建的。
console.log(my instanceof Shape);   //true
console.log(my instanceof TwoDShape); //true
console.log(my instanceof Triangle); //true

四、prototype方式

正如上面所说,出于效率考虑,我们应该尽可能将一些可重用的属性和方法添加到原型中去,如果形成了一个好习惯,我们仅仅依靠原型就能完成继承关系构建了。而不需要使用new Shape()等实例对象。

由于原型中所有的代码都是可重用的,这意味着继承自Shape.prototype比继承自new Shape()所创建的实体要好的多。毕竟,new Shape() 方式会将Shape 属性设定为对象自身属性,这样的代码是不可重用的(因而要将其设置在原型中)。于是我们采取以下方式对效率做一些改进:

  • 不要单独为继承关系创建新对象
  • 尽量减少运行时方法搜索,例如toString()

4.1 代码分析

下面就是我们优化后的代码
这样做会令my.toString() 方法查找有什么不同么?

  • 首先,Js引擎同样会查看my对象中有么有toString()方法,自然是找不到,于是它就去搜索该对象的原型属性。此时该原型属性已经指向了TwoDShape的原型,而后者指向是Shape.prototype.更重要的是,由于这里所采用的都是引用传递而不是值传递,所以这里的方法查询步骤直接精简为两步。
  • 第一步:在本类中查找
  • 第二步:在其原型属性中查找,因为TwoDShape得原型定义成了Shape.prototype.所以直接就在Shape.prototpe中查询,一下就找到了。

4.2 这种方式的缺点

这样简单的拷贝原型从效率上来说固然会更好一些,但是由于子对象与父对象指向的是同一个对象,所以一旦子对象对其原型进行了修改,父对象也会被修改。文章来源地址https://www.toymoban.com/news/detail-551848.html

4.3 代码实现

function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {
    return this.name;
}
function TwoDShape(){}
TwoDShape.prototype = Shape.prototype;
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.prototype.name = "2D Shape";
function Triangle(side, height){
    this.side = side;
    this.height = height;
}
Triangle.prototype = TwoDShape.prototype;
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {
    return this.side * this.height / 2;
}
var my = new Triangle(5, 10);
console.log(my.getArea());  //25
console.log(my.toString()); //Triangle
var supers = new Shape();
console.log(supers.name);	//Triangle 一定要注意

到了这里,关于第3集丨JavaScript 使用原型(prototype)实现继承——最佳实战1的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • javascript中的prototype;javascript中的原型链

    在JavaScript中,每个对象都有一个原型(prototype),并且原型可以有自己的原型,形成了一个链式结构,被称为原型链。这个机制是JavaScript中实现继承和对象属性查找的基础。 原型链是由对象之间的连接构成的。每个对象都有一个指向它原型的链接,这个链接可以追溯到顶层

    2024年02月20日
    浏览(42)
  • 什么是原型链(Prototype Chain)?它在JavaScript中有什么作用?请解释一下JavaScript中的“this”关键字的含义和用法。

    原型链(Prototype Chain)是一种在JavaScript中创建对象的方式,它允许我们通过继承来扩展对象的属性和方法。 在原型链中,每个对象都有一个原型对象(也称为“原型”或“ proto ”),该对象继承了其父对象的属性和方法。当我们在一个对象上定义一个属性或方法时,如果该

    2024年02月11日
    浏览(58)
  • 什么是JavaScript中的类(Class)和继承(Inheritance)?它们与原型链继承相比有哪些优缺点?

    JavaScript中的类(Class)和继承(Inheritance)是面向对象编程的重要概念。类是一种抽象的概念,它定义了一组属性和方法,并且这些属性和方法可以在同一组对象中重复使用。继承是类之间的层次关系,其中一个类继承了另一个类的属性和方法,从而能够共享其代码和行为。 在Ja

    2024年02月12日
    浏览(58)
  • 设计模式之原型模式Prototype的C++实现

    1、原型模式提出 在软件功能设计中,经常面临着“某些结构复杂的对象”的创建工作,且创建的对象想拥有其他对象在某一刻的状态,则可以使用原型模型。原型模型是通过拷贝构造函数来创建对象,并且该对象拥有其他对象在某一刻的状态。 2、需求描述 设计产品A,B,这

    2024年02月12日
    浏览(41)
  • SAP Fiori开发中的JavaScript基础知识15 - 原型,object,constructor,class,继承

    本文将介绍JavaScript中的核心概念 - 原型,并会介绍基于原型的应用场景object,constructor,class,继承。 本文会将这几个核心概念汇总在一篇博客中,因为这些概念是触类旁通的,希望对你有帮助。 在JavaScript中,几乎所有的东西都是对象,每个对象都有一个 特殊的内部属性

    2024年04月23日
    浏览(121)
  • 【设计模式——学习笔记】23种设计模式——原型模式Prototype(原理讲解+应用场景介绍+案例介绍+Java代码实现)

    原型模式指用通过拷贝原型实例创建新的实例,新实例和原型实例的属性完全一致 原型模式是一种创建型设计模式 工作原理是通过调用原型实例的 clone() 方法来完成克隆,原型实例需要实现Cloneable接口,并重写 clone() 方法 需要为每个类开发一个克隆方法,这对全新的类来说

    2024年02月16日
    浏览(52)
  • JavaScript中手动实现Array.prototype.map方法

    在前端开发中,我们经常需要对数组进行操作和处理。在JavaScript中,数组是常用的数据类型之一。而数组的map方法可以将一个数组中的每个元素都进行某种操作,并返回一个新的数组。今天,我们就来手动实现JavaScript中数组原型的map方法。 首先,让我们回顾一下map方法的基

    2024年02月21日
    浏览(41)
  • 原型模式(Prototype)

    原型模式是一种创建型设计模式,使调用方能够复制已有对象,而又无需使代码依赖它们所属的类。当有一个类的实例(原型),并且想通过复制原型来创建新对象时,通常会使用原型模式。 原型模式包含如下角色: Prototype,原型类,用来声明克隆方法。在绝大多数情况下

    2024年02月14日
    浏览(44)
  • 原型模式(Prototype Pattern)

    原型模式(Prototype Pattern)结构较为简单,它是一种特殊的创建型模式, 当需要创建大量相同或者相似对象时 ,可以 通过对一个已有对象的复制获取更多对象 。Java语言提供了较为简单的原型模式解决方案,只需要创建一个原型对象,然后通过在类中定义的克隆方法复制自己

    2024年02月03日
    浏览(40)
  • 原型模式(Prototype Pattern)

    原型模式(Prototype Pattern)是一种创建型设计模式,它用于通过复制现有对象来创建新对象,而无需依赖显式的构造函数。 在原型模式中,我们创建一个原型对象,并通过复制该原型对象来创建新的对象。这样做的好处是,我们可以避免创建对象时的复杂实例化过程,而是通

    2024年02月15日
    浏览(46)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包