浅析js中的原型和原型链及其使用场景
Posted 铁锤妹妹@
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析js中的原型和原型链及其使用场景相关的知识,希望对你有一定的参考价值。
一、前言
最近浏览网站学习的时候,看到了这个话题,感觉面试里也经常会被问到;所以查阅了不少文章,想总结一下,方便以后翻看。理解的会比较浅显,希望能多多交流,以后理解更深层的也会持续在这里更新。
二、原型的使用场景
场景1:在vue项目中,我们通常会将对象公共属性放在vue原型上;或者使用插件,将其挂载到vue原型上;这都是利用原型来实现的。
下面案例是将公共属性添加到vue原型上,使用插件的话同理。
// main.js
import Vue from "vue";
//发现少了一个 imgBaseUrl 属性,可以使用原型对象进行扩展
Vue.prototype.imgBaseUrl = config.imgBaseUrl;
// 其他页面使用
<template>
<div>
<image :src="imgBaseUrl + 'supply/purchase_tag.png'"></image>
</div>
</template>
<script>
export default
data()
const _this = this;
return
imgBaseUrl: _this.imgBaseUrl,
;
;
</script>
场景2:js中判断数据类型Object.prototype.toString.call()方法, instanceof方法。
instanceof 是用来 判断数据是否是某个对象的实例,返回一个布尔值。
详细内容可以查看我之前的文章:js中判断数据类型的几种实用方法
三、原型和原型对象
在JS中,我们所说的原型通常是针对于函数
而言的,当然构造函数也是一个函数。
函数也是一个对象,是对象那么它就有属性,在JS中,我们所创建的每一个函数自带一个属性prototype
,我们就把prototype称为原型
,也有人把它称之为“显示原型
”,反正就一个意思。
这个prototype它指向了一个对象
,可以把prototype想象成一个指针,或者更简单的理解为prototype的属性值(键值对)。prototype指向的这个对象我们就称之为原型对象
,通常大家就直接将prototype理解为原型对象。
请注意,以下的代码是独立的(出于严谨,假定页面没有其他的 javascript 代码)。为了最佳的学习体验,强烈建议打开浏览器的控制台(在 Chrome 和火狐浏览器中,按 Ctrl+Shift+I 即可),进入“console”选项卡,然后把如下的 JavaScript 代码复制粘贴到窗口中,最后通过按下回车键运行代码。
function Person()
console.log(Person.prototype)
打印结果:
可以看到prototype它确实指向了一个对象,原型对象prototype里面有一个constructor属性,它指向了Person构造函数。
总结
1)每个函数都有一个prototype属性,被称作原型。
2)prototype原型指向一个对象,所以也被称原型对象。
四、prototype和__proto__不能混淆
很多人把 prototype和__proto__混为一潭,其实这是两个维度的东西。
prototype
的维度是函数,
而__proto__
的维度是对象
。
__proto__
是每个对象都有的属性,我们通常把它称为"隐式原型
",把prototype
称为"显式原型
"。
另外,函数也是一个对象,所以它既有prototype属性又有__proto__属性。
控制台验证一下:我们可以看到函数有prototype和__proto__
两个属性,而对象只有__proto__
属性。
Function:
object对象:
接下来我们再来看看__proto__属性,控制台打印看看。
发现了一个新的属性:[[Prototype]]
,官方对于这个属性其实有解释,这里通俗的解释一下:
[[prototype]]其实就是隐式原型__proto__,因为各大浏览器厂家不同,所以取了别名罢了,大家只需记住这个和__proto__一样即可。
总结
1)prototype和__proto__不太一样,一个是函数拥有的显式原型,一个是对象拥有的隐式原型。
2) __proto__通常被称作隐式原型,每个对象都拥有该属性。
3)[[prototype]] 其实就是__proto__。
五、原型链
接着我们可以给 Person 函数的原型对象添加新属性,如下:
function Person()
Person.prototype.name= "铁锤妹妹"; // 往函数的原型上添加变量和方法
Person.prototype.age = '18'
Person.prototype.getAge= function()
console.log('我18岁了')
var obj = new Person()
console.log( obj.name ) //"铁锤妹妹"
console.log( obj.age ) //"18"
上面的代码,我们声明了一个构造函数Person,其实就是一个函数。我们知道函数的prototype是一个对象,我们就可以往这个对象上添加东西,所以我们就直接往函数的原型上添加了变量和方法。
接着我们使用new关键词
创建一个Person构造函数的 实例对象
,分别打印name和age,会发现obj这个实例是可以调用构造函数Person原型属性和方法的。这个就叫做 继承
。
其实这就和我们的原型链有关了,我们把obj打印出来看看:
console.log(obj)
我们会发现obj对象上并没有name和age属性,但是在他的隐式原型[[Prototype]]上有name和age,而且我们会发现obj的[[prototype]]中的constructor指向的是它的构造函数Person。
然后我们再修改一下我们的代码,我们在obj对象上添加一个name属性,看看会输出什么。
function Person()
Person.prototype.name= "铁锤妹妹"; // 往函数的原型上添加变量和方法
Person.prototype.age = '18'
Person.prototype.getAge= function()
console.log('我18岁了')
var obj = new Person()
obj.name = '铁柱哥'
console.log( obj.name ) //"铁柱哥"
console.log( obj.age ) //"18"
如上打印所示, obj.__proto__ = Person.prototype
。但这是做什么的呢?当访问obj
中的一个属性,浏览器首先会查看obj
中是否存在这个属性。
如果 obj
不包含属性信息,那么浏览器会在 obj
的 __proto__
中进行查找 (同 Person.prototype
). 如属性在 obj
的 __proto__
中查找到,则使用 obj
中 __proto__
的属性。
否则,如果 obj
中 __proto__
不具有该属性,则检查 obj
的 __proto__
的 __proto__
是否具有该属性。默认情况下,任何函数的原型属性 __proto__
都是 window.Object.prototype
. 因此,通过 obj
的 __proto__
的 __proto__
( 同 Peron.prototype 的 __proto__
(同 Object.prototype
)) 来查找要搜索的属性。
如果属性不存在 obj
的 __proto__
的 __proto__
中,那么就会在obj
的 __proto__
的 __proto__
的 __proto__
中查找。然而,这里存在个问题:obj
的 __proto__
的 __proto__
的 __proto__
其实不存在。因此,只有这样,在 __proto__
的整个原型链被查看之后,这里没有更多的 __proto__
,浏览器断言该属性不存在,并给出属性值为 undefined
的结论。
上面的查找过程是不是很像链式查找!而这就是我们所说的 原型链
,而且我们发现查找的过程主要是通过 __proto__
原型来进行的,所以 __proto__
就是我们原型链中的 连接点
。
总结:
1)当访问某个对象的属性时,会先在这个对象本身属性上查找,如果没有找到,就去它的_proto_查找,即它的构造函数的prototype查找,如果没有找到,就去它的构造函数的prototype._proto_查找。这样一层一层的查找就会形成一个链式结构,这就是原型链。
2)一直往上层查找,直到找到null还没有找到,则返回undefined。
3)所有引用类型的 _proto_都指向它的构造函数的prototype。
想要理解原型链,我们还得理解__proto__指向哪儿,也就是说它指向那个构造函数,比如上面的obj对象的__proto__指向的就是Person构造函数,所以我们继续往Person上查找。
六、 性能
在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。
可参考:
一文搞懂原型和原型链!
浅谈原型和原型链以及其应用案例
【web前端面试必问2】js原型和原型链的理解(透彻)
以上是关于浅析js中的原型和原型链及其使用场景的主要内容,如果未能解决你的问题,请参考以下文章