所以说尽管call和apply并不是仅仅为了继承而新增的方法,但用它们可以模拟继承。对象冒充继承就是这么一回事,它可以实现多重继承,只要重复做这一套赋值的流程就可以了。现在回到Js利用对象冒充模拟的继承里,所有的成员方法都是针对this而创建的,也就是所所有的实例都会拥有一份成员方法的副本,这是对内存资源的一种极度浪费。
javascript面向对象中继承实现的几种方式
< p>1。原型链继承核心将父类的实例作为子类的原型缺点。父类增加了原型方法/原型属性,可以被子类访问。2.构造继承核心使用父类的构造函数来增强子类的实例,相当于将父类的实例属性复制到子类中(不使用原型)。缺点方法在构造函数中定义,只能继承父类的实例属性和方法,不能继承原型属性/方法。无法实现函数重用,每个子类都有父类实例函数的副本,影响性能。3.组合继承组合继承(所有实例都可以有自己的属性,使用相同的方法。组合继承避免了原型链和借用构造函数的缺陷,结合了两者的优点,是最常用的继承方法。)核心通过调用父类来构造,继承了父类的属性,保留了传递参数的优点。然后,使用父类实例作为子类原型,实现功能重用缺点。父类的构造函数被调用两次,生成了两个实例(子类实例屏蔽了子类原型上的实例)。4.寄生组合继承核心通过寄生的方式砍掉父类的实例属性。这样,当父类被调用两次时,实例方法/属性就不会被初始化两次。避免组合继承的缺点是比较完美,但是实现起来比较复杂,这两种方法各有优缺点。在这里,我先给大家列举一下,再从底层分析区别。(1)该对象假装是(name){ this . name = name
}
}B(name,Id){ this . temp = A
< p > this . temp(name) < p >//它相当于newa() < p > this . temp < p >//防止超类A的属性和方法this.id将来被temp引用覆盖。}}构造对象B时,调用temp相当于启动A的构造函数,注意这里上下文中的这个对象是B的一个实例,所以在执行A的构造函数脚本时,A的所有变量和方法都会赋给这个所指向的对象,也就是B的实例,从而达到B继承A的属性方法的目的,删除临时引用temp后, 是为了防止维护B中A的类对象(注意不是实例对象)的引用改变,因为改变temp会直接导致A类的结构改变(注意不是A类的对象)。
我们可以看到,在Js版本更新的过程中,添加了call和apply函数,以便更方便地切换这个上下文来实现继承或更通用的目的。它们的原理是一样的,只是参数的版本不同(一个变量是任意参数,而一个参数集必须传入数组)。这里以call为例说明call实现的对象假装被继承。Rect(width),{this.width=width
this。=
< p > this . area =(){ this . width * this。}
}(width,name){Rect.call(this,width,)
< p > this . name = name < p > this . show =(){ alert(this . name+" With area "+this . area()) < p > }关于调用方法,官方的解释是调用一个对象的方法来使用另一个调用(,arg1,arg2…),这也是对象模拟的继承。实际上,调用call方法时发生的也是上下文环境变量this的替换。在函数体中,这必须指向类对象的实例。但是,使用它作为上下文环境变量会调用Rect方法,即Rect类的构造函数。所以当Rect被调用时,它的赋值属性和方法实际上是在一个新的对象上执行的。因此,尽管call和apply不仅仅是继承的新方法,它们也可以模拟继承。对象模仿继承就是这样的东西,它可以实现多重继承,只需要重复赋值的过程。然而目前并没有大规模使用。为什么?< br >因为它有一个明显的性能缺陷,所以有必要谈谈OO的概念。我们说对象是成员+成员方法的集合。构造对象实例时,这些实例只需要有自己的成员变量。成员方法只是一个对变量进行操作的可执行文本区域。这个区域不需要复制