定义
-
prototype
是函数(构造函数)的属性,指向原型对象。 -
constructor
是原型对象的属性,指向当前对象的构造函数。 -
__proto__
是对象的属性,指向它的原型对象。
先创建两个实例
function Person(name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person('张三', 20);
var person2 = new Person('李四', 24);
在这里,Person是构造函数,person1和person2是实例对象。
prototype 和 __proto__
首先Person.prototype
是一个对象,同时也是person1和person2的原型,所以Person.prototype
会有__proto__
和constructor
属性
根据定义,构造函数的
prototype
属性指向调用该构造函数创建的实例的原型,也就是person1和person2的原型,而且实例的__proto__
属性也指向它们的原型。
Person.prototype === person1.__proto__ //true
Person.prototype === person2.__proto__ //true
原型中的方法会被实例共享。
Person.prototype.getName = function() {
return this.name;
}
person1.getNmae(); //张三
person2.getName(); //李四
constructor
constructor始终指向创建当前对象的构造函数。
Person.prototype.constructor === Person //true
person1和person2本身是没有constructor属性的,但继承了原型的属性和方法,也就有了constructor属性
person1.constructor === Person //true
person2.constructor === Person //true
person1.constructor === Person.prototype.constructor //true
但是当我们重新定义函数的prototype时, constructor的行为就有点奇怪了,如下示例:
Person.prototype = {
getName: function() {
return this.name;
}
};
var person3 = new Person('王五', 30);
person3.constructor === Person // false
person3.constructor === Object // true
Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true
覆盖Person.prototype相当于如下操作,而constructor始终指向创建自身的构造函数:
Person.prototype = new Object({
getName: function() {
return this.name;
}
});
要修正这个问题,只要重新覆盖Person.prototype.constructor即可:
Person.prototype.constructor = Person;
person3.constructor === Person // true
person3.constructor === Object // false
Person.prototype.constructor === Person // true
Person.prototype.constructor === Object // false
也可以这么写:
Person.prototype = {
constructor: Person, //指定constructor
getName: function() {
return this.name;
}
};
补充
JavaScript中的Array, String, Date, Number, Object这些类型也是通过构造函数类实现的,因此,我们可以在这些类型的prototype中添加自己的方法。
Array.prototype.min = function() {
var min = this[0];
for (var i = 1; i < this.length; i++) {
if (this[i] < min) {
min = this[i];
}
}
return min;
};
console.log([11, 22, 33, 44].min()); // 11
注意:这里有一个陷阱,向Array的原型中添加扩展方法后,当使用for-in循环时,会枚举出其原型链上的属性,所以这个扩展方法也会被枚举出来。
var arr = [11, 22, 33, 44];
for(var i in arr) {
console.log(i); // 0, 1, 2, 3, min
}
解决办法一:
使用hasOwnProperty()方法,hasOwnProperty() 方法会返回一个布尔值,指示对象是否具有指定的属性作为自身(不继承)属性。
var arr = [11, 22, 33, 44];
for(var i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(i); // 0, 1, 2, 3
}
}
解决办法二:
使用Object.keys()方法,该 方法会返回一个由一个给定对象的自身可枚举属性组成的字符串数组
var arr = [11, 22, 33, 44];
Object.keys(arr); // ["0", "1", "2", "3"]