在工做中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,可是我经过查阅相关资料,决定作一下总结加深本身的理解,写得不对的地方还请各位大神指出。函数
每一个函数都有一个prototype属性,该属性是一个指针,指向一个对象。 而这个对象的用途是包含由特定类型的全部实例共享的属性和方法。使用这个对象的好处就是可让全部实例对象共享它所拥有的属性和方法。这个属性只用js中的类(或者说可以做为构造函数的对象)才会有this
每一个实例对象都有一个__proto__属性,用于指向构造函数的原型对象。__proto__属性是在调用构造函数建立实例对象时产生的。该属性存在于实例和构造函数的原型对象之间,而不是存在于实例于构造函数之间。spa
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
复制代码
输出结果以下:
总结:prototype
一、调用构造函数建立的实例对象的prototype属性为"undefined",构造函数的prototype是一个对象。
二、__proto__属性是在调用构造函数建立实例对象时产生的。
三、调用构造函数建立的实例对象的__proto__属性指向构造函数的prototype,本质上就是继承构造函数的原型属性。
四、在默认状况下,全部原型对象都会自动得到一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。3d
isPrototypeOf(): 虽然在全部实现中都没法访问到__proto__,但能够经过 isPrototypeOf()方法来肯定对象之间是否存在这种关系。指针
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
复制代码
Object.getPrototypeOf(): 在全部支持的实现中,这个方法返回__proto__的值。例如:code
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas" person1
是下面代码中的实例
复制代码
注意:虽然能够经过对象实例访问保存在原型中的值,但却不能经过对象实例重写原型中的值。若是咱们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那咱们就在实例中建立该属性,该属性将会屏蔽原型中的那个属性。请看下面的例子:cdn
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 操做符,就能够肯定该属性究竟是存在于对象中,仍是存在于原型中。blog
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个搜索步骤: