• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 知识库 知识库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

2022年02月14日发布javascript继承有哪两种形式

武飞扬头像
xhjyxxw
帮助0

知行礼动

大家好,今日小科来聊聊一篇关于2022年02月14日整理发布:javascript继承有哪两种形式的文章,现在让我们往下看看吧!

javascript继承有两种形式:对象模拟和原型模式。对象模拟的本质就是改变这个方向;原型的继承是指原型以某种方式被使用或覆盖,从而达到复制属性方法的目的。

Javascript本身是从Perl语言的语法演变而来的,本质上是一种脚本语言,是随着版本的更新而逐渐加入的面向对象的模拟。

我觉得Js的面向对象仿真总体做得还不错,因为我们不能盲目的跟着任何想法走,不能纯粹为了OOP而OOP。我们需要把握的是面向对象的好处。对于这些优势,去OOP是最明智的选择,所以Js做得很好。

Js的继承在很多书中分为很多类型和实现方式,大致有两种:对象模拟和原型方式。这两种方法各有利弊。这里我先列出来,然后从底层分析区别:

(1)对象模拟

函数A(名称){ 0

this.name=name

this . SayHello=function(){ alert(this . name)“说你好!”);};

}

函数B(名称,id){ 0

这个温度=;

this.temp(名称);//相当于新的A();

删除this.temp//防止超类A的属性和方法以后被temp引用覆盖。

this.id=id

this.checkId=函数(ID){ alert(this . ID==ID)};

}构造对象B时,调用temp相当于启动A的构造函数。注意这里上下文中的这个对象是B的一个实例,所以在执行A的构造函数脚本时,A的所有变量和方法都会赋给这个引用的对象,也就是B的实例,从而达到B继承A的属性方法的目的。

之后删除临时引用temp,防止维护B中对A的类对象(非实例对象)的引用改变,因为改变temp会直接导致A类(非A类对象)的结构改变。

我们看到,在Js版本更新的过程中,为了更方便地切换这个上下文这个达到继承或者更广义的目的,调用和应用函数都被加入了。它们有相同的原理,只是参数的版本不同(一个变量任意参数,另一个必须作为参数集传递到数组中)。这里以call为例说明调用实现的对象假装继承。

函数Rect(宽度、高度){ 0

this.width=宽度;

this.height=高度;

this . area=function(){ return this . width * this . height;};

}

函数myRect(宽度、高度、名称){ 0

Rect。调用(这个,宽度,高度);

this.name=name

this . show=function(){ 0

alert(this.name "带area : " this . area());

}

}关于Call方法,官方解释:调用一个对象的方法,用另一个对象替换当前对象。

调用(thisOb,arg1,arg2…,arg2.)这也是对象模拟的继承。事实上,调用方法时发生的事情也是上下文变量this的替换。在myRect函数体中,这必须指向类myRect对象的实例。然而,使用这个作为上下文变量来调用Rect方法,也就是类Rect的构造函数。

因此,当此时调用Rect时,它的赋值属性和方法实际上是针对myRect对象的。因此,尽管调用和应用并不是仅仅用于继承的新方法,但它们可以模拟继承。

对象模拟继承就是这么一回事,它可以实现多重继承,只要这个赋值过程是重复的。然而,目前大规模的应用并不多。为什么呢?

因为它有一个明显的性能缺陷,我们不得不谈谈OO的概念。我们说对象是成员方法的集合。构建对象实例时,这些实例只需要有自己的成员变量。成员方法只是一个对变量进行操作的可执行文本区域。不需要为每个实例复制该区域,所有实例都可以共享。

现在回到使用对象模拟的Js继承,所有成员方法都是为此创建的,也就是说,所有实例都将有一个成员方法的副本,这是对内存资源的极大浪费。

其他的缺陷,比如模仿变量的对象和不能继承原型域的方法,就不用提了。我觉得前一个致命缺陷就够了。但是,我们仍然需要理解它,尤其是父类的属性和方法是如何继承的。

原 理,对于理解Js继承很重要。

(二)原型方式

第二种继承方式是原型方式,所谓原型方式的继承,是指利用了prototype或者说以某种方式覆盖了prototype,从而达到属性方法复制的目的。 其实现方式有很多中,可能不同框架多少会有一点区别,但是我们把握住原理,就不会有任何不理解的地方了。看一个例子(某一种实现):

function Person(){this.name = “Mike”;this.sayGoodbye = function(){alert(“GoodBye!”);};}Person.prototype.sayHello = function(){alert(”Hello!”);};function Student(){}Student.prototype = new Person();

关键是对最后一句Student原型属性赋值为Person类构造的对象,这里笔者解释一下父类的属性和方法是如何copy到子类上的。

Js对象在读取某个对象属性的时候,总是先查看自身域的属性列表,如果有就返回否则去读取prototype域,如果找到就返回,由于prototype可以指向别的对象,所以Js解释器会递归的去查找prototype域指向对象的prototype域,直到prototype为本身就停止,此时还没找到就成undefined了。

这样看来,最后一句发生的效果就是将父类所有属性和方法连接到子类的prototype域上,这样子类就继承了父类所有的属性和方法,包括name、 sayGoodbye和sayHello。这里与其把最后一句看成一种赋值,不如理解成一种指向关系更好一点。

这种原型继承的缺陷也相当明显,就是继承时 父类的构造函数时不能带参数,因为对子类prototype域的修改是在声明子类对象之后才能进行,用子类构造函数的参数去初始化父类属性是无法实现的, 如下所示:

function Person(name){this.name = name;}function Student(name,id){this.id = id;}Student.prototype = new Person(this.name);

两种继承方式已经讲完了,如果我们理解了两种方式下子类如何把父类的属性和方法“抓取”下来,就可以自由组合各自的利弊,来实现真正合理的Js继承。下面是个人总结的一种综合方式:

function Person(name){this.name = name;}Person.prototype.sayHello = function(){alert(this.name “say Hello!”);};function Student(name,id){Person.call(this,name);this.id = id;}Student.prototype = new Person();Student.prototype.show = function(){alert(“Name is:” this.name ” and Id is:” this.id);

总结就是利用对象冒充机制的call方法把父类的属性给抓取下来,而成员方法尽量写进被所有对象实例共享的prototype域中,以防止方法副本重复创 建。然后子类继承父类prototype域来抓取下来所有的方法。

如想彻底理清这些调用链的关系,推荐大家多关注Js中prototype的 constructor和对象的constructor属性,这里就不多说了。

这篇好文章是转载于:知行礼动

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 知行礼动
  • 本文地址: /news/detail/tanhbkfgef