在javaScript建立对象一文中提到过:用构造函数建立对象存在一个问题即同一构造函数的不一样实例的相同方法是不同的,因此咱们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让全部实例共享的目的。javascript
接下来进一步介绍javaScript原型。html
js中建立一个函数,就会自动建立一个prototype属性,这个属性指向函数的原型对象,而且原型对象会自动得到一个constructor(构造函数)属性,指向该函数。java
举例:之前面的原型模式建立对象为例说明数组
<script type="text/javascript"> function Person(){ } Person.prototype.name="lxy"; Person.prototype.age=22; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); } var lxy=new Person(); lxy.sayName(); var personA=new Person(); personA.sayName(); alert(lxy.sayName()==personA.sayName());//true </script>
Person对象和Person对象的原型之间的关系以下图1。浏览器
图1函数、实例与原型的关系(图来着JS高程)ide
简单一句话就是:Person.prototype.constructor指向Person,能够在浏览器中测试一下。函数
经过构造函数建立一个实例,该实例内部将包含一个属性(指针),指向构造函数的原型对象。学习
举例:Person构造函数的实例Person1和Person2的[[Prototype]]属性都指向Person的原型。如图1所示。测试
Note:[[Prototype]]链接是存在于实例和构造函数的原型之间,而不是存在实例与构造函数之间。this
关于这个指针,ECMA-262中叫[[Prototype]],没有标准的方式访问[[Prototype]],但Firefox、Safari和Chrome在每一个对象上都支持一个属性__protp__,而在其余实现中,这个属性对脚本不可见。
实例有本身的属性和方法,而原型封装了全部实例共享的属性和方法,那这种共享是经过什么方式实现的呢?答案是原型链。
当要访问实例的属性时,解析器会执行一次搜索。首先从实例对象开始,若是在实例中找到了这个属性,则返回该属性的值;重点是若是没有找到的话,则继续搜索[[Prototype]]指针指向的原型对象,在原型对象中查找该属性,若是找到,则返回该属性的值。因此经过这种方式多个实例就能共享保存在原型中的属性和方法。这也是js的原型链。
Note:理解了原型链,很天然就能明白几个问题。
a、给实例添加一个与原型中同名的属性,就会将其屏蔽。由于搜索原型链时在实例中就能找到而后就返回了,根本到不了原型。
b、能够经过实例爬原型链访问原型中的值,但却不能经过实例重写原型中的值。同理。
c、原型的动态性,在原型上新增属性或方法能当即从实例反应出来。
虽然实例的[[Prototype]]属性没法访问的,咱们能够经过isPrototypeOf()方法来确认原型和实例之间的关系。这个方法呢是站在原型的角度来检测原型是否是某个实例的原型。A.isPrototypeOf(B),若是A的B的原型返回true,不然返回false。
举例:由于Person的两个实例lxy和personA内部都有一个[[Prototype]]属性指向Person.prototype。因此isPrototypeOf方法会返回true。
<script type="text/javascript"> function Person(){ } Person.prototype.name="lxy"; Person.prototype.age=22; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); } var lxy=new Person(); var personA=new Person(); console.log(Person.prototype.isPrototypeOf(lxy)); //true console.log(Person.prototype.isPrototypeOf(personA)); //true </script>
这个方法是ECMAScript 5中新增的,返回实例的[[Prototype]]值。
这个方法是很是有用的,由于它是在Object上实现的。因此把任何实例扔给Object,它都能得到实例的原型。
举例
<script type="text/javascript"> function Person(){ } Person.prototype.name="lxy"; Person.prototype.age=22; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); } var lxy=new Person(); console.log(Object.getPrototypeOf(lxy)); console.log(Object.getPrototypeOf(lxy)==Person.prototype); //true console.log(Object.getPrototypeOf(lxy).age);//22 </script>
结果:
这个方法用于检测某个属性是否真正存在于实例中。是返回ture,不然返回false。
就像咱们本身有一些资源和技能,可是也能够从爹妈那里获得一些资源和技能,好比看起来你有套别墅,可是你要知道哪些是你真正属于你本身的,哪些是爹妈给你的。
举例:好比我一出生父母就给我个名字lxy,这时候我用hasOwnPrototype()方法检测这个"name"属性是不真是个人,就会返回false。
后来我本身改了个名字starof,再用hasOwnPrototype()方法检测,这时就会返回true。
再后来我不想用这个名字了,我就把它delete掉了,用回了我父母给的名字。这时候再用hasOwnPrototype()方法检测这个"name"属性是否是我本身的,就会返回false。
这个一波三折的故事代码以下:
<script type="text/javascript"> function Person(){ } Person.prototype.name="lxy"; var lxy=new Person(); console.log(lxy.name); console.log(lxy.hasOwnProperty("name")); //false lxy.name="starof";//经过重写屏蔽原型属性name,因此这个name就变成了实例属性 console.log(lxy.name); console.log(lxy.hasOwnProperty("name")); //true delete lxy.name; console.log(lxy.name); console.log(lxy.hasOwnProperty("name")); //false </script>
in操做符在经过对象可以访问属性时返回true,不管该属性是实例属性仍是原型属性。
for-in循环,返回全部可以经过对象访问的,可枚举的属性。即包括实例属性也包括原型属性。
屏蔽了原型中不可枚举的属性的实例属性也会在for-in循环中返回,由于根据规定,全部开发人员定义的属性都是可枚举的——只有在IE8及更早版本中例外。
Object.keys()方法,取得实例上全部可枚举的实例属性。该方法接收一个实例做为参数,返回一个包含全部可枚举属性的字符串数组。
Object.getOwnPropertyNames()方法,得到全部实例属性,不管它是否可枚举。
<script type="text/javascript"> function Person(){ } Person.prototype.name="lxy"; Person.prototype.age=22; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); } var lxy=new Person(); //in console.log("name" in lxy);//in实例可访问的属性 //for-in for(var prop in lxy){ console.log(prop); }//for-in列出全部可访问的,可枚举的属性 //Object.keys var keys=Object.keys(Person.prototype); console.log(keys);//Person的原型的全部可枚举属性 var keys=Object.keys(lxy); console.log(keys);//lxy如今没有实例属性,因此keys为空 lxy.name="lxy"; var keys=Object.keys(lxy); console.log(keys); //Object.getOwnPrototypeNames console.log(Object.getOwnPropertyNames(lxy));//获得全部实例属性 </script>
<script type="text/javascript"> function Person(){ } Person.prototype.name="lxy"; Person.prototype.age=22; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); } var lxy=new Person(); </script>
<script> function Person(){ } Person.prototype={ name:"lxy", age: 22, job:"Software Engineer", sayName:function(){ alert(this.name); } }; var lxy=new Person(); </script>
第二种语法比较简单,少写几行代码,可是有一点要注意,字面量形式,彻底重写了prototype属性,因此constructor再也不指向Person,而是Object了。
<script> function Person(){ } Person.prototype={ name:"lxy", age: 22, job:"Software Engineer", sayName:function(){ alert(this.name); } }; var lxy=new Person(); console.log(lxy.constructor==Person);//false console.log(lxy.constructor==Object);//true </script>
若是constructor很重要,可手动设置为Person,以下。
<script> function Person(){ } Person.prototype={ constructor:Person, name:"lxy", age: 22, job:"Software Engineer", sayName:function(){ alert(this.name); } }; var lxy=new Person(); console.log(lxy.constructor==Person);//true console.log(lxy.constructor==Object);//false </script>
可是这样写会致使constructor的[[Enumerable]]特性被置为true。由于开发人员定义的属性都是可枚举的。
若是是兼容ECMAScript5的JS引擎可以使用Object.definePrototype。
<script> function Person(){ } Person.prototype={ name:"lxy", age: 22, job:"Software Engineer", sayName:function(){ alert(this.name); } }; //重设构造函数,只适用于ECMAScript5兼容的浏览器 Object.defineProperty(Person.prototype,"constructor",{ enumerable:false, value:Person }) var lxy=new Person(); console.log(lxy.constructor==Person);//true console.log(lxy.constructor==Object);//false </script>
本文做者starof,因知识自己在变化,做者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/4904929.html有问题欢迎与我讨论,共同进步。