面试问如何理解js原型?

第一,基于原型链的继承

1.继承属性

JavaScript对象是动态属性“包”(指自己的属性)。JavaScript对象有一个指向原型对象的链。当试图访问一个对象的属性时,它不仅会搜索该对象,还会搜索该对象的原型和该对象的原型的原型,并逐层向上搜索,直到找到一个具有匹配名称的属性或到达原型链的末端。下面的代码将演示访问对象的属性时发生的行为:

【javascript】?观平原?复制

//?假设有一个物体?o,?自身属性(自有?属性)是?答?然后呢。乙:

//?{a:?1,?b:?2}

//?o?原型?o .[[原型]]有属性?b?然后呢。丙:

//?{b:?3,?c:?4}

//?最后,?o .[[原型]]。[[原型]]?什么事?空。

//?这是原型链的末端,是吗?空,

//?根据定义,null?没有[[原型]]。

//?综上所述,整个原型链如下:

//?{a:1,?乙:2}?-& gt;?{b:3,?c:4}?-& gt;?空

console . log(o . a);?//?1

//?A是O的一个属性吗?是的,这个属性的值是1。

console . log(o . b);?//?2

//?b是o的属性吗?是的,这个属性的值是2。

//?o .在[[prototype]]上也有一个' b '属性,但是它不会被访问。这种情况称为“属性屏蔽”?(物业?阴影)”。

console . log(o . c);?//?四

//?C是O的属性吗?不,让我们看看它是否在o .[[原型]]上。

//?c是o.[[Prototype]]的属性吗?是的,这个属性的值是4。

console . log(o . d);?//?不明确的

//?d是o的属性吗?不,让我们看看它是否在o .[[原型]]上。

//?d是o.[[Prototype]]的属性吗?不,我们看看是不是在o上[[原型]][[原型]]。

//?o .[[原型]]。[[原型]]为空,停止搜索。

//?如果没有d属性,它将返回undefined。

创建对象自身属性的方法是设置该对象的属性。获取和设置行为规则的唯一例外是当存在。Getter还是setter?设置为继承的属性。

2.继承方法

JavaScript没有其他基于类的语言定义的“方法”。在JavaScript中,任何函数都可以作为对象的属性添加到对象中。函数继承和其他属性继承没什么区别,包括上面的“属性屏蔽”(这种情况相当于其他语言的方法重写)。

当调用继承的函数时。指向当前继承的对象,而不是继承的函数所在的原型对象。

【javascript】?观平原?复制

var?o?=?{

答:?2,

男:?函数(){

回归?这个. a?+?1;

}

};

console . log(o . m());?//?三

//?打电话的时候?o.m?当‘这个’指向o。

var?p?=?object . create(o);

//?p是一个对象。[[原型]]是o。

私人助理?=?12;?//?创造?p?自我属性a。

console . log(p . m());?//?13

//?打电话?下午?什么时候?这个‘指向’是什么?p?

//?又是因为?p?继承?o?什么事?m?功能

//?这个时候?即。也就是说。p?它自己的属性,”一个

第二,使用不同的方法创建对象和生成原型链编辑。

1.使用普通语法创建对象

【javascript】?观平原?复制

var?o?=?{a:?1};

//?o这个对象继承了Object.prototype上面的所有属性

//?所以可以这样用?o.hasOwnProperty('a ')。

//?hasOwnProperty?是Object.prototype的属性

//?Object.prototype的原型为空。

//?原型链如下:

//?o?-& gt;?对象.原型?-& gt;?空

var?答?=?【“哟”,?"哇"的一声。"?"];

//?数组都是从Array.prototype继承的?

//?(索引Of,?ForEach和其他方法都是从它继承的)。

//?原型链如下:

//?答?-& gt;?数组.原型?-& gt;?对象.原型?-& gt;?空

功能?f(){

回归?2;

}

//?函数都是从Function.prototype继承的。

//?(呼,?Bind和其他方法是从它继承而来的):

//?f?-& gt;?功能.原型?-& gt;?对象.原型?-& gt;?空

2.使用构造函数创建对象

在JavaScript中,构造函数实际上是一个普通的函数。使用时?新运营商?当使用这个函数时,它可以被称为构造函数。

【javascript】?观平原?复制

功能?Graph()?{

这个顶点?=?[];

这个.边缘?=?[];

}

图表.原型?=?{

addVertex:?功能(五){

this . vertexs . push(v);

}

};

var?g?=?新的?graph();

//?g是生成的物体,它本身的属性是‘顶点’和‘边’。

//?当g被实例化时,g.[[Prototype]]指向Graph.prototype

3.使用Object.create创建对象。

ECMAScript 5中引入了一个新方法Object.create()。您可以调用此方法来创建新的对象。新对象的原型是调用。创造?方法,传入的第一个参数:

【javascript】?观平原?复制

var?答?=?{a:?1};?

//?答?-& gt;?对象.原型?-& gt;?空

var?b?=?object . create(a);

//?b?-& gt;?答?-& gt;?对象.原型?-& gt;?空

console . log(b . a);?//?1?(继承)

var?c?=?object . create(b);

//?c?-& gt;?b?-& gt;?答?-& gt;?对象.原型?-& gt;?空

var?d?=?object . create(null);

//?d?-& gt;?空

console . log(d . hasownproperty);?//?未定义,?因为d不继承Object.prototype。

4.使用?班级?关键字

ECMAScript6引入了一组新的关键字来实现?班级.使用基于类的语言的开发人员将会熟悉这些结构,但它们是不同的。JavaScript仍然基于原型。这些新关键字包括?类,?构造函数,静态,?分机,还有?超级棒。

【javascript】?观平原?复制

“用吗?严格”;

班级?多边形?{

构造器(身高,?宽度)?{

这个.身高?=?身高;

这个.宽度?=?宽度;

}

}

班级?方形?延伸?多边形?{

构造函数(sideLength)?{

超级(边长,?边长);

}

得到?area()?{

回归?这个.身高?*?this .宽度;

}

设置?边长(newLength)?{

这个.身高?=?新长度;

这个.宽度?=?新长度;

}

}

var?方形?=?新的?正方形(2);

5.表演

在原型链上找属性很费时间,对性能有副作用,这在性能要求很高的情况下很重要。此外,试图访问一个不存在的属性会遍历整个原型链。当遍历对象的属性时,原型链上的每个可枚举属性都将被枚举。

有必要用检测对象的属性是定义在自身上还是原型链上吗?hasOwnProperty?方法,该方法包含在从Object.proptotype继承的所有对象中。

hasOwnProperty?它是JavaScript中唯一一个只涉及对象本身属性,不遍历原型链的方法。

注:仅通过判断值是否为?未定义?仅仅检测一个属性是否存在是不够的。一个属性可能存在,而它的值碰巧没有定义。

6.不好的做法:扩展本机对象的原型

一个常见的错误是扩张?对象.原型?或者其他内置对象的原型。这种技术叫做猴子打补丁,破坏了原型链的紧密性。尽管一些流行的框架(如Prototype.js)正在使用这种技术,但没有充分的理由将内置类型系统与其他非标准方法相混淆。我们扩展内置对象原型的唯一原因是为了引入新JavaScript引擎的一些新特性,比如Array.forEach。