-
理解原型对象
无论什么时候,只要新建一个函数,就会根据特定规则为函数创建一个prototype
属性,这个属性指向这个函数的原型对象。所有原型对象都会自动获得一个constructor
属性,这个属性指向prototype
属性所在的函数。
创建来自定义的构造函数后,其原型对象默认只会取得constructor
属性,至于其它方法,都是从 Object 继承而来。
调用构造函数创建实例后,实例的内部会有一个指针指向该函数的原型对象。ECMA-262第5版中将这个指针叫做 [[ Prototype ]] ,虽然在脚本中没有标准的方式访问这个指针,但 Firefox 、Safari、chrome在每个对象上都支持一个属性 _proto_
。 很重要的一点就是,这个连接存在与实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
function Person() {}
Person.prototype.name = "张三";
Person.prototype.age = 24;
Person.prototype.job = "chinese";
Person.prototype.sayName = function () {
console.log(this.name);
}
let person1 = new Person();
let person2 = new Person();
console.log(Person.prototype.isPrototypeOf(person1)); // true
虽然在所有实现中都无法访问到 [[ Prototype ]] ,但可以通过 isPrototypeOf()
方法来确定对象之间是否有这种关系。如果 [[ Prototype ]] 指向调用 isPrototypeOf()
方法的对象,那么这个方法就返回 true。
ECMAscript 5 新增加了一个方法Object.getPrototypeOf()
。这个方法返回 [[ Prototype ]] 的值。
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
console.log(Object.getPrototypeOf(person1).name); // 张三
使用 Object.getPrototypeOf()
可以很方便的取得一个对象的原型,这在利用原型实现继承的情况下是非常重要的。
当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性,搜索首先从对象实例本身开始,如果在实例中找到了这个属性,就返回属性值,如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找这个属性,找到后就返回,找不到就报错。这就是多个对象实例共享原型所保存的属性和方法的基本原理。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果在实例中添加一个跟原型对象中的属性同名的属性,那么这个属性就会添加到实例上,并且该属性会屏蔽原型中的那个属性。
function Person() {}
Person.prototype.name = "张三";
Person.prototype.age = 24;
Person.prototype.job = "chinese";
Person.prototype.brother = ['李四', '王五'];
Person.prototype.sayName = function () {
console.log(this.name);
}
let person1 = new Person();
let person2 = new Person();
person2.brother = ['李四', '马六'];
console.log(person1.brother); // [ '李四', '王五' ]
console.log(person2.brother); //[ '李四', '马六' ]
上面的例子中,我们为 person2 添加了一个 brother 属性,而这个属性与原型对象的属性 brother 同名,这样当我们访问person2的 brother 属性时,就会返回添加在 person2 实例上的 brother 属性。而原型中的 brother 属性也并没有被重写。
使用 hasOwnProperty()
方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只在给定属性存在于实例中时,才会返回 true。
function Person() {}
Person.prototype.name = "张三";
Person.prototype.age = 24;
Person.prototype.job = "chinese";
Person.prototype.brother = ['李四', '王五'];
Person.prototype.sayName = function () {
console.log(this.name);
}
let person1 = new Person();
let person2 = new Person();
person1.name = '李斯特';
console.log(person1.hasOwnProperty('name')); // true
console.log(person2.hasOwnProperty('name')); // false
-
原型与 in 操作符
有两种方式使用 in 操作符,单独使用 和 在 for-in
中使用。单独使用时,in 操作符会在通过对象能够访问给定属性时返回 true;无论该属性是存在于实例中,还是存在于原型中。
利用 hasOwnProperty()
方法和 in 操作符就可以判断属性是存在于实例上,还是存在于原型中。
function testPropertyOf (object, name) {
if (!(name in object)) {
console.log('不存在这个属性');
} else if (!object.hasOwnProperty(name) && (name in object)) {
console.log('这个属性存在于原型上');
} else if (object.hasOwnProperty(name) && (name in object)){
console.log('这个属性存在于实例中');
}
}
在使用 for-in
循环时,返回的是所有能够通过对象访问的、可枚举的( 内部属性 enumerable 为 true)属性,既包括实例中的属性,也包括原型中的属性。
要取得对象上可枚举的实例属性,可以使用 Object.keys()
方法,这个方法接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
function Person() {}
Person.prototype.name = "张三";
Person.prototype.age = 24;
Person.prototype.job = "chinese";
Person.prototype.brother = ['李四', '王五'];
Person.prototype.sayName = function () {
console.log(this.name);
}
let person1 = new Person();
let person2 = new Person();
person1.name = '李斯特';
let tempArr = [];
for (var keyName in person1) {
tempArr.push(keyName);
}
let arr2 = Object.keys(person1);
console.log(tempArr);// [ 'name', 'age', 'job', 'brother', 'sayName' ]
console.log(arr2); // [ 'name' ]
如果想得到所有属性,不论是否可以枚举,可以使用Object.getOwnPropertyNames()
方法。