在工做中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,可是我经过查阅相关资料,决定作一下总结加深本身的理解,写得不对的地方还请各位大神指出。html
跟__proto__属性相关的两个方法vue
判断属性是存在实例对象中,仍是存在原型对象中的方法函数
获取或遍历对象中属性的几种方法this
一、prototypeprototype
每一个函数都有一个prototype属性,该属性是一个指针,指向一个对象。 而这个对象的用途是包含由特定类型的全部实例共享的属性和方法。使用这个对象的好处就是可让全部实例对象共享它所拥有的属性和方法指针
二、 __proto__htm
每一个实例对象都有一个__proto__属性,用于指向构造函数的原型对象。__proto__属性是在调用构造函数建立实例对象时产生的。对象
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); }; // 与声明函数在逻辑上是等价的 } var person1=new Person("Nicholas",29,"Software Engineer"); console.log(person1); console.log(Person); console.log(person1.prototype);//undefined console.log(person1.__proto__); console.log(Person.prototype); console.log(person1.__proto__===Person.prototype);//true
输出结果以下:继承
总结:element
一、调用构造函数建立的实例对象的prototype属性为"undefined",构造函数的prototype是一个对象。
二、__proto__属性是在调用构造函数建立实例对象时产生的。
三、调用构造函数建立的实例对象的__proto__属性指向构造函数的prototype。
四、在默认状况下,全部原型对象都会自动得到一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
下图展现了使用Person构造函数建立实例后各个对象之间的关系
上图展现了 Person 构造函数、 Person 的原型属性以及 Person现有的两个实例之间的关系。
三、 跟__proto__属性相关的两个方法
isPrototypeOf():虽然在全部实现中都没法访问到__proto__,但能够经过 isPrototypeOf()方法来肯定对象之间是否存在这种关系。
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true
Object.getPrototypeOf():在全部支持的实现中,这个方法返回__proto__的值。例如:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"
注意:虽然能够经过对象实例访问保存在原型中的值,但却不能经过对象实例重写原型中的值。若是咱们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那咱们就在实例中建立该属性,该属性将会屏蔽原型中的那个属性。请看下面的例子:
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg"―― 来自实例 alert(person2.name); //"Nicholas"―― 来自原型
四、 判断属性是存在实例对象中,仍是存在原型对象中,有如下方法
hasOwnProperty():能够检测一个属性是存在于实例中,仍是存在于原型中。返回值为true表示该属性存在实例对象中,其余状况都为false。
in 操做符:不管该属性存在于实例中仍是原型中。只要存在对象中,都会返回true。可是能够同时使用 hasOwnProperty()方法和 in 操做符,就能够肯定该属性究竟是存在于对象中,仍是存在于原型中。
var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true person1.name = "Greg"; alert(person1.name); //"Greg" ―― 来自实例 alert(person1.hasOwnProperty("name")); //true alert("name" in person1); //true alert(person2.name); //"Nicholas" ―― 来自原型 alert(person2.hasOwnProperty("name")); //false alert("name" in person2); //true delete person1.name; alert(person1.name); //"Nicholas" ―― 来自原型 alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true
五、 获取或遍历对象中属性的几种方法
for-in:经过for-in循环的返回的是可以被访问的、可枚举的属性,无论该属性是在实例中,仍是存在原型中。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; } Person.prototype={ sayName:function(){ return this.name; } } var p=new Person("李明",30,"诗人"); for(var prop in p){ console.log(prop);//name、age、job、sayName } console.log(Object.keys(p));//["name", "age", "job"] console.log(Object.keys(Person.prototype));//["sayName"] console.log(Object.getOwnPropertyNames(Person.prototype)) // ["constructor", "sayName"]
Object.keys():取得实例对象上全部可枚举的属性。 Object.getOwnPropertyNames(): 获取实例对象全部属性,不管它是否可枚举。
注意:使用对象字面量来重写整个原型对象时,本质上彻底重写了默认的 prototype 对象,所以 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),再也不指向 Person。可是能够经过在重写原型对象时指定constructor属性,使之仍是指向原来的constructor。此时,尽管 instanceof 操做符还能返回正确的结果,但经过 constructor 已经没法肯定对象的类型了。
object instanceof constructor:检测 constructor.prototype 是否存在于参数 object 的原型链上。
function Person() {} var friend2 = new Person(); Person.prototype = { //constructor : Person, name: "Nicholas", age: 29, job: "Software Engineer", sayName: function() { alert(this.name); } }; var friend = new Person(); console.log(friend2 instanceof Object); //true console.log(friend2 instanceof Person); //false, console.log(friend2.constructor == Person); //true console.log(friend2.constructor == Object); //false console.log(friend instanceof Object); //true console.log(friend instanceof Person); //true console.log(friend.constructor == Person); //false console.log(friend.constructor == Object); //true
因为原型的动态性,调用构造函数时会为实例添加一个指向最初原型的Prototype指针,而把原型修改成另一个对象就等于切断了构造函数与最初原型之间的联系。看下面的例子
function Person(){ } var friend = new Person(); Person.prototype = { constructor: Person, name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; var friend2=new Person(); friend.sayName(); //Uncaught TypeError: friend.sayName is not a function friend2.sayName();//Nicholas console.log(friend instanceof Person);//false console.log(friend instanceof Object);//true console.log(friend2 instanceof Person);//true
结果分析:这是由于friend1的prototype指向的是没重写Person.prototype以前的Person.prototype,也就是构造函数最初的原型对象。而friend2的prototype指向的是重写Person.prototype后的Person.prototype。以下图所示
六、 原型链
基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。最直观的表现就是让原型对象等于另外一个类型的实例。
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //继承了 SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
SubType.prototype = new SuperType();这句代码使得原来存在于 SuperType 的实例中的全部属性和方法,如今也存在于 SubType.prototype 中。使得instance的constructor指向了SuperType。
console.log(instance.constructor===SuperType);//true
总结: 访问一个实例属性时,首先会在实例中搜索该属性。若是没有找到该属性,则会继续搜索实例的原型。在经过原型链实现继承的状况下,搜索过程就得以沿着原型链继续向上。在找不到属性或方法的状况下,搜索过程老是要一环一环地前行到原型链末端才会停下来。
就拿上面的例子来讲,调用 instance.getSuperValue()会经历4个搜索步骤:
搜索instance实例;
搜索 SubType.prototype;
搜索SuperType的实例;
搜索 SuperType.prototype,最后一步才会找到该方法。