前端面试回顾(1)---javascript的面向对象 (2)

这个时候我们可以使用hasOwnProperty()方法来检测一个属性是存在于实例中,还是存在于原型中。如果实例中有这个属性,hasOwnProperty()会返回true,而hasOwnProperty()并不会感知到原型中的属性。所以可以用这个方法检测属性到底是存在于实例中还是原型中。

console.log(person1.hasOwnProperty("sex")); //原型中的属性,返回false console.log(person2.hasOwnProperty("sex")); //实例中的属性,返回true 二、继承

ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

2.1 原型链继承

如下代码:

function Super(){ this.val = true; this.arr = ["a"]; } function Sub(){ //... } Sub.prototype = new Super(); var sub = new Sub(); console.log(sub.val) //true

以上代码定义了Super和Sub两个类型,继承的核心就一句话:Sub.prototype = new Super() 将父类的一个实例赋给子类的原型。这样子类就能够使用父类实例所拥有的方法和父类原型中的方法。

这种情况要想给子类添加自己的方法或者是覆盖父类中某个方法的时候,一定要在放在替换原型语句后面。否则写在原型上的方法都会丢失。

而且在给子类添加新方法的时候,不能使用字面量的方式添加新方法,这样会导致继承无效。
如:

Sub.prototype = new Super(); Sub.prototype = { //错误的方式 getVal : function(){ //... } }

以上代码刚刚把Super的实例赋给原型,紧接着又将原信替换成一个对象字面量,导致现在原型包含的是一个Object的实例,并非Super的实例,因此原型链被切断了,Sub和Super已经没有关系了。

原型链的问题:
最主要的问题有两个:一是由于引用类型的原型属性会被所有实例所共享,所以通过原型链继承时,原型变成了另一个类型的实例,原先的实例属性也就变成了现在的原型属性,如下代码:

function Super(){ this.friends = ["peng","gao"]; } function Sub(){ //... } Sub.prototype = new Super(); var sub1 = new Sub(); var sub2 = new Sub(); sub1.friends.push("du"); console.log(sub2.friends); //["peng", "gao", "du"]

这个例子说明的就是上面的问题,子类的所有实例共享了父类中的引用类型属性。

原型链继承的另一个问题是在创建子类行的实例的时候,没法向父类的构造函数传递参数。

2.2 构造函数继承

具体实现:

function Super(){ this.val = true; this.arr = ["a"]; } function Sub(){ Super.call(this); } var sub = new Sub(); console.log(sub.val) //true

这种模式这是解决了原型链继承中出现的两个问题,它可以传递参数,也没有了子类共享父类引用属性的问题。
但这种模式也有他的问题,那就是在父类原型中定义的方法,其实是对子类不可见的。

2.3组合继承

既然上述的两种方式各有各自的局限性,将它俩整合到一起是不是会好一点呢,于是就有了组合继承。

function Super(){ this.val = true; this.arr = ["a"]; } function Sub(){ Super.call(this); //{2} } Sub.prototype = new Super(); //{1} Sub.prototype.constructor = Sub; //{3} var sub = new Sub(); console.log(sub.val) //true

组合继承还有一个要注意的地方:
在代码{3}处,将子类原型的constructor属性指向子类的构造函数。因为如果不这么做,子类的原型是父类的一个实例,所以子类原型的constructor属性就丢失了,他会顺着原型链继续往上找,于是就找到了父类的constructor所以它指向的其实是父类。

这种继承方式是使用最多的一种方式。
这种继承方式解决了上两种方式的缺点,不会出现共享引用类型的问题,同时父类原型中的方法也被继承了下来。

如果要说起有什么缺点我们发现,在执行代码{1}时,Sub.prototype会得到父类型的val和arr两个属性。他们是Super的实例属性,只不过现在在Sub的原型上。而代码{2}处,在创建Sub实例的时候,调用Super的构造函数,又会在新的对象上创建属性val和arr,于是,这两个属性就屏蔽了原型中两个同名属性。

2.4寄生组合式继承

对于上面的问题,我们也有解决办法,不是在子类原型中多了一份父类的属性和方法么,那我原型中就只要父类原型中的属性和方法,这里我们引入了一个方法:

function inheritObject(obj){ var F = function(){}; F.prototype = obj; return new F(); }

这个方法创建了一个对象临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygyyj.html