面试问如何理解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。