详解js中的原型,原型对象,原型链


Posted in Javascript onJuly 16, 2020

理解原型

我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。看如下例子:

function Person(){
}
Person.prototype.name = 'ccc'
Person.prototype.age = 18
Person.prototype.sayName = function (){
 console.log(this.name);
}

var person1 = new Person()
person1.sayName()  // --> ccc

var person2 = new Person()
person2.sayName()  // --> ccc

console.log(person1.sayName === person2.sayName)  // --> true

理解原型对象

根据上面代码,看下图:

详解js中的原型,原型对象,原型链

需要理解三点:

  1. 我们只要创建了一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,指向函数的原型对象。即Person(构造函数)有一个prototype指针,指向Person.prototype
  2. 默认情况下,每个原型对象上都会创建一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针
  3. 每个实例的内部都有一个指针(内部属性) ,指向构造函数的原型对象。即 person1 和person2 身上都有一个内部属性__proto__(在ECMAscript中管这个指针叫[[prototype]],虽然在脚本中没有标准的方式访问[[prototype]],但是firefox,ie,chrome都支持一个属性叫__proto__) 指向Person.prototype

注意:person1 和person2 实例与构造函数之间没有直接的关系。

在之前我们提到,所有实现中无法访问到[[prototype]],那我们如何知道实例和原型对象之间是否存在关系呢?这里可以通过两个方法来判断:

  • 原型对线上的方法:isPrototypeOf(),如:console.log(Person.prototype.isPrototypeOf(person1)) // --> true
  • ECMAscript5中新增的一个方法:Object.getPrototypeOf(),这个方法返回[[prototype]]的值。如:console.log(Object.getPrototypeOf(person1) === Person.prototype) // --> true

实例属性与原型属性的关系

前面我们提到过,原型最初只包含constructor属性,而该属性也是共享的,因此可以通过对象实例访问。虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而改属性与实例原型中的一个属性同名,那就会在实例上创建该属性并屏蔽原型中的那个属性。如下:

function Person() {}
Person.prototype.name = "ccc";
Person.prototype.age = 18;
Person.prototype.sayName = function() {
 console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

person1.name = 'www'  // 在person1中添加一个name属性
person1.sayName()  // --> 'www'————'来自实例'
person2.sayName()  // --> 'ccc'————'来自原型'

console.log(person1.hasOwnProperty('name'))  // --> true
console.log(person2.hasOwnProperty('name'))  // --> false

delete person1.name  // --> 删除person1中新添加的name属性
person1.sayName()  // -->'ccc'————'来自原型'

 我们如何判断一个属性,到底是实例上的属性还是原型上的属性?这里可以通过hasOwnProperty()方法来检测一个属性是存在于实例中还是存在于原型中。(此方法继承于Object)

下图详细分析了上面例子在不同情况下的实现与原型的关系:(省略了Person构造函数的的关系)

详解js中的原型,原型对象,原型链

更简单的原型语法

我们不可能总像之前的例子一样,没添加一个属性和方法就要敲一遍,Person.prototype。为了减少不必要的输入,更常见的方法是像下面这样:

function Person(){}
Person.prototype ={
 name: 'ccc',
 age: 18,
 sayName: function () {
 console.log(this.name)
 }
}

在上面代码中,我们将Person.prototype设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外,constructor属性不再指向Person了。前面我们介绍过,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。但是在我们使用的新语法中,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数了。此时,尽管instanceof操作符还能返回正确的结果,但通过constructor已经无法确定对象的类型了。如下:

var person1 = new Person()
console.log(person1 instanceof Object)  // --> true
console.log(person1 instanceof Person)  // --> true
console.log(person1.constructor === Person)  // --> false
console.log(person1.constructor === Object)  // --> true

这里用instanceof操作符测试Object和Person仍然返回true,constructor属性则等于Object,不等于Person了,如果constructor真的很重要可以像下面这样写:

function Person(){}
Person.prototype ={
 constructor: Person,  // --> 重设
 name: 'ccc',
 age: 18,
 sayName: function () {
 console.log(this.name)
 }
}

但是这会引起一个新问题,用上述方式重置constructor属性会导致它的[[Enumerable]]特性被设置为true。而默认情况下,原生的constructor属性是不可枚举的。因此如果你要使用兼容ECMAscript5的JavaScript引擎,可以试一试Object.defineProperty()。

function Person(){}
Person.constructor = {
 name: 'ccc', 
 age: 18,
 sayName: function(){
 console.log(this.name)
 }
}
// 重设构造函数,只适用于ECMAscript5兼容的浏览器
Object.defineProperty(Person.constructor, "constructor", {
 enumerable: false, 
 value: Person
})

原型的动态性

由于原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能立即从实例上反映出来。比如:

function Person(){}
var person1 = new Person()
Person.prototype.sayHi= function(){
 console.log('hi')
}
person1.sayHi()

上述代码我们先创建了一个Person实例,并将其保存在person1中,然后在Person.prototype中添加了sayHi()方法。即使person1是添加新方法之前创建的,但它仍然可以访问这个方法。原因是实例与原型之间的松散的连接关系。
尽管可以随时为原型添加属性和方法,并立即能够在实例中反映出来。但是如果重写整个原型对象,那么情况就不一样了。看如下代码:

function Person(){}
var person1 = new Person()

Person.prototype = {
 name: 'ccc',
 age: 18,
 sayName: function(){
 console.log(this.name)
 }
}

person1.sayName()  // --> error

看下图分析:

详解js中的原型,原型对象,原型链

调用构造函数时为实例添加了一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对线更久等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。

原型链

简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。

详解js中的原型,原型对象,原型链

图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。

以上就是详解js中的原型,原型对象,原型链的详细内容,更多关于js中的原型,原型对象,原型链的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
关于IFRAME 自适应高度的研究
Jul 20 Javascript
javascript实现的动态文字变换
Jul 28 Javascript
jQuery学习笔记之 Ajax操作篇(三) - 过程处理
Jun 23 Javascript
推荐8款jQuery轻量级树形Tree插件
Nov 12 Javascript
jQuery插件ajaxFileUpload实现异步上传文件效果
Apr 14 Javascript
jquery trigger实现联动的方法
Feb 29 Javascript
详解JavaScript正则表达式之分组匹配及反向引用
Mar 09 Javascript
JavaScript_object基础入门(必看篇)
Jun 13 Javascript
分分钟学会vue中vuex的应用(入门教程)
Sep 14 Javascript
jQuery实现仿京东防抖动菜单效果示例
Jul 06 jQuery
Vue.js实现备忘录功能
Jun 26 Javascript
JS前端可视化canvas动画原理及其推导实现
Aug 05 Javascript
详解Webpack4多页应用打包方案
Jul 16 #Javascript
快速了解Vue父子组件传值以及父调子方法、子调父方法
Jul 15 #Javascript
微信小程序12行js代码自己写个滑块功能(推荐)
Jul 15 #Javascript
TypeScript 引用资源文件后提示找不到的异常处理技巧
Jul 15 #Javascript
微信小程序实现列表的横向滑动方式
Jul 15 #Javascript
JavaScript之scrollTop、scrollHeight、offsetTop、offsetHeight等属性学习笔记
Jul 15 #Javascript
JavaScript实时更新当前的时间的示例代码
Jul 15 #Javascript
You might like
php中处理mysql_fetch_assoc返回来的数组 不用foreach----echo
2011/05/04 PHP
PHP对接微信公众平台消息接口开发流程教程
2014/03/25 PHP
PHP防止注入攻击实例分析
2014/11/03 PHP
php创建、获取cookie及基础要点分析
2015/01/26 PHP
javascript eval和JSON之间的联系
2009/12/31 Javascript
jquery与js函数冲突的两种解决方法
2013/09/09 Javascript
调试JavaScript中正则表达式中遇到的问题
2015/01/27 Javascript
javascript字符串与数组转换汇总
2015/05/26 Javascript
原生js实现移动端瀑布流式代码示例
2015/12/18 Javascript
JavaScript的String字符串对象常用操作总结
2016/05/26 Javascript
jqPlot jQuery绘图插件的使用
2016/06/18 Javascript
探索Vue.js component内容实现
2016/11/03 Javascript
Javascript 判断两个IP是否在同一网段实例代码
2016/11/28 Javascript
javascript 的变量、作用域和内存问题
2017/04/19 Javascript
在vue-cli搭建的项目中增加后台mock接口的方法
2018/04/26 Javascript
js实现一个简易计算器
2020/03/30 Javascript
python遍历序列enumerate函数浅析
2017/10/17 Python
Python2和3字符编码的区别知识点整理
2019/08/08 Python
Django的CVB实例详解
2020/02/10 Python
python 绘制场景热力图的示例
2020/09/23 Python
Python调用飞书发送消息的示例
2020/11/10 Python
雅诗兰黛美国官网:Estee Lauder美国
2016/07/21 全球购物
伦敦一家领先的精品零售商:IRIS Fashion
2019/05/24 全球购物
微软巴西官方网站:Microsoft Brasil
2019/09/26 全球购物
潘多拉珠宝俄罗斯官方网上商店:PANDORA俄罗斯
2020/09/22 全球购物
创业计划书六个要素
2013/12/26 职场文书
文明风采获奖感言
2014/02/18 职场文书
销售人员求职的自我评价分享
2014/03/15 职场文书
请假条格式范文
2014/04/10 职场文书
中学生演讲稿
2014/04/26 职场文书
就业意向书
2014/07/29 职场文书
2015学校年度工作总结
2015/05/11 职场文书
党员电教片《信仰》心得体会
2016/01/15 职场文书
2020年个人安全保证书参考模板
2020/01/08 职场文书
Python Pandas知识点之缺失值处理详解
2021/05/11 Python
Ruby使用Mysql2连接操作MySQL
2022/04/19 Ruby