理解js中的原型,原型对象,原型链
理解原型
我们创建的每一个函数都有一个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
理解原型对象
根据上面代码,看下图:

需要理解三点:
- 我们只要创建了一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,指向函数的原型对象。即Person(构造函数)有一个prototype指针,指向Person.prototype
- 默认情况下,每个原型对象上都会创建一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针
- 每个实例的内部都有一个指针(内部属性) ,指向构造函数的原型对象。即 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构造函数的的关系)

更简单的原型语法
我们不可能总像之前的例子一样,没添加一个属性和方法就要敲一遍,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
看下图分析:

调用构造函数时为实例添加了一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对线更久等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型,而不指向构造函数。
原型链
原型链的内容明天再写,今天有点晚了。
理解js中的原型,原型对象,原型链的更多相关文章
- [JS]如何理解JS中的类和对象
		-------------------------------------------------------------------------------------------- 变量:自由的 ... 
- 深入理解JS中的对象(一)
		目录 一切皆是对象吗? 对象 原型与原型链 构造函数 参考 1.一切皆是对象吗? 首先,"在 JavaScript 中,一切皆是对象"这种表述是不完全正确的. JavaScript ... 
- 深入理解JS中的对象(二):new 的工作原理
		目录 序言 不同返回值的构造函数 深入 new 调用函数原理 总结 参考 1.序言 在 深入理解JS中的对象(一):原型.原型链和构造函数 中,我们分析了JS中是否一切皆对象以及对象的原型.原型链和构 ... 
- 深入理解JS中的对象(三):class 的工作原理
		目录 序言 class 是一个特殊的函数 class 的工作原理 class 继承的原型链关系 参考 1.序言 ECMAScript 2015(ES6) 中引入的 JavaScript 类实质上是 J ... 
- JS中遍历数组、对象的方式
		1.标准的for循环遍历数组 //不打印自定义属性和继承属性 var array = [1,2,3]; for (var i = 0; i < array.length; i++) { cons ... 
- 怎么理解js中的事件委托
		怎么理解js中的事件委托 时间 2015-01-15 00:59:59 SegmentFault 原文 http://segmentfault.com/blog/sunchengli/119000 ... 
- js中内置有对象
		statpot:使用mongo+bootstrap+highcharts做统计报表 最近做了一个统计项目,这个统计项目大致的需求是统计接口的访问速度.客户端会调用一个接口来记录接口的访问情况,我的需求 ... 
- 如何更好的理解js中的this,分享2段有意思的代码
		关于js中this的浅析,大家可以点击[彻底理解js中this的指向,不必硬背]这篇博客了解. 今天遇到2段比较有意思的代码. ----------------第一段----------------- ... 
- 图文结合深入理解 JS 中的 this 值
		图文结合深入理解 JS 中的 this 值 在 JS 中最常见的莫过于函数了,在函数(方法)中 this 的出现频率特别高,那么 this 到底是什么呢,今天就和大家一起学习总结一下 JS 中的 th ... 
- 深度理解js中var let const 区别
		首先要理解js中作用域的概念 作用域:指的是一个变量的作用范围 1.全局作用域 直接写在script中的js代码,在js中,万物皆对象,都在全局作用域,全局作用域在页面打开时创建,在全局作用域中有一个 ... 
随机推荐
- Java 从入门到进阶之路(二十三)
			在之前的文章我们介绍了一下 Java 中的 集合框架中的Collection 的迭代器 Iterator,本章我们来看一下 Java 集合框架中的Collection 的泛型. 在讲泛型之前我们先来 ... 
- Linux nohup命令详解,终端关闭程序依然可以在执行!
			大家好,我是良许. 在工作中,我们很经常跑一个很重要的程序,有时候这个程序需要跑好几个小时,甚至需要几天,这个时候如果我们退出终端,或者网络不好连接中断,那么程序就会被中止.而这个情况肯定不是我们想看 ... 
- C#结构体struct -0029
			结构体 有时候我们仅需要一个小的数据结构,类提供的功能多于我们需要的功能:考虑到性能原因,最好使用结构体. 结构体是值类型,存储在栈中或存储为内联(如果结构体是存储在堆中的另一个对象的一部分). 例如 ... 
- Elasticsearch系列---生产集群的索引管理
			概要 索引是我们使用Elasticsearch里最频繁的部分日常的操作都与索引有关,本篇从运维人员的视角,来玩一玩Elasticsearch的索引操作. 基本操作 在运维童鞋的视角里,索引的日常操作除 ... 
- Windows安装C的编译环境
			对于java开发者来说安装C的编译环境不是非常熟悉,因此本文对C的安装环境进行介绍以及windows编译Redis和Zookeeper的过程.MinGW主要用于按照gcc.make等环境,cywin用 ... 
- Spring中的AOP(二)
			2.5 Spring的织入 在上一篇文章中,我们介绍了Pointcut.Advice.Advisor三个必要模块,剩下的工作就是把它们拼装起来,也就是织入过程.在Spring中,使用类org.spri ... 
- ElasticSearch--validate验证搜索语句是否合法或者存在语法错误
			GET /accounts/person/_validate/query?explain { "query":{ "match": { "user&q ... 
- JavaWeb网上图书商城完整项目--BaseServlet
			1.以前进行操作的时候,例如我们进行登陆操作我们使用LoginServlet进行处理,进行注册操作我们使用RegisterServlet,很多业务的操作的时候我们就要定义很多个Servlet 有了Ba ... 
- 梳理搭建SSM步骤
			以上全程手撕,如有不足或错误的,请指正! 
- 05[掌握]高可用、集群、持久化、docker 等前置知识点
			高可用 24小时对外提供服务 高并发 同一时间段能处理的请求数 1,中心化和去中心化 1.1,中心化 意思是所有的节点都要有一个主节点 缺点:中心挂了,服务就挂了 中心处理数据的能力有限,不能把节点性 ... 
