详解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 相关文章推荐
Aptana调试javascript图解教程
Nov 30 Javascript
IE浏览器打印的页眉页脚设置解决方法
Dec 08 Javascript
Javascript 完美运动框架(逐行分析代码,让你轻松了运动的原理)
Jan 23 Javascript
javascript上下方向键控制表格行选中并高亮显示的方法
Feb 13 Javascript
JSONP之我见
Mar 24 Javascript
jQuery插件Easyui设置datagrid的pageNumber导致两次请求问题的解决方法
Aug 06 Javascript
基于Vue实现图书管理功能
Oct 17 Javascript
JS实现小球的弹性碰撞效果
Nov 11 Javascript
layui监听下拉选框选中值变化的方法(包含监听普通下拉选框)
Sep 24 Javascript
在layui中对table中的数据进行判断(0、1)转换为提示信息的方法
Sep 28 Javascript
微信sdk实现禁止微信分享(使用原生php实现)
Nov 15 Javascript
vue v-model的用法解析
Oct 19 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 array_intersect()函数使用代码
2009/01/14 PHP
PHP 加密/解密函数 dencrypt(动态密文,带压缩功能,支持中文)
2009/01/30 PHP
PHP中将数组转成XML格式的实现代码
2011/08/08 PHP
header跳转和include包含问题详解
2012/09/08 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(八)
2014/06/23 PHP
php安装ssh2扩展的方法【Linux平台】
2016/07/20 PHP
详解PHP使用日期时间处理器Carbon人性化显示时间
2017/08/10 PHP
PHPExcel实现表格导出功能示例【带有多个工作sheet】
2018/06/13 PHP
Laravel 模型使用软删除-左连接查询-表起别名示例
2019/10/24 PHP
extJs 文本框后面加上说明文字+下拉列表选中值后触发事件
2009/11/27 Javascript
Jsonp 跨域的原理以及Jquery的解决方案
2011/06/27 Javascript
EASYUI TREEGRID异步加载数据实现方法
2012/08/22 Javascript
js弹出层包含flash 不能关闭隐藏的2种处理方法
2013/06/17 Javascript
JavaScript也谈内存优化
2014/06/06 Javascript
javascript面向对象之共享成员属性与方法及prototype关键字用法
2015/01/13 Javascript
js实现点击文本框显示日期选择器特效代码分享
2020/05/21 Javascript
jQuery中的通配符选择器使用总结
2016/05/30 Javascript
Vue.js组件tree实现省市多级联动
2016/12/02 Javascript
js编写三级联动简单案例
2016/12/21 Javascript
jsTree事件和交互以及插件plugins详解
2017/08/29 Javascript
微信小程序左滑删除功能开发案例详解
2018/11/12 Javascript
layer.js之回调销毁对话框的例子
2019/09/11 Javascript
[01:17]Ti4 循环赛第一日回顾
2014/07/11 DOTA
python抓取并保存html页面时乱码问题的解决方法
2016/07/01 Python
Python 中的with关键字使用详解
2016/09/11 Python
浅谈python可视化包Bokeh
2018/02/07 Python
python flask中动态URL规则详解
2019/11/22 Python
Python中filter与lambda的结合使用详解
2019/12/24 Python
Pandas替换及部分替换(replace)实现流程详解
2020/10/12 Python
HomeAway的巴西品牌:Alugue Temporada
2018/04/10 全球购物
2013年办公室秘书的个人自我鉴定
2013/10/24 职场文书
党的群众教育实践活动实施方案
2014/06/12 职场文书
2014年信访维稳工作总结
2014/12/08 职场文书
2015年母亲节活动策划方案
2015/05/04 职场文书
2016教师暑期培训学习心得体会
2016/01/09 职场文书
高一化学教学反思
2016/02/22 职场文书