说明
红皮书6.2.3章节里的内容。这部分讲述的方法颇多,发现自己很容易记混淆或者遗忘,所以简单整理了下,以便自己后续回顾和快速查阅。
1. 理解原型对象
可以通过两种方式来确定原型和实例之间的关系:
- 使用
instanceof
操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true。 - 使用
isPrototypeOf()
方法。同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
function Person(){};
Person.prototype.name = 'Nicholas';
var person1 = new Person();
console.log(person1 instanceof Person) // true
console.log(person1 instanceof Object) // true
.isPrototypeOf()
作用:虽然在所有实现中都无法访问到[[Prototype]], 但以通过 isPrototypeOf()
方法来确定对象之间是否存在这种关系。
从本质上讲,如果[[Prototype]]指向调用 isPrototypeOf()方法的对象 (Person.prototype),那么这个方法就返回 true, 如下:
function Person(){};
Person.prototype.name = 'Nicholas';
var person1 = new Person();
alert(Person.prototype.isPrototypeOf(person1)); //true
Object.getPrototypeOf() —— es5提供
作用:可以方便地取得一个对象的原型(这个方法返回[[Prototype]]
的值(即.__proto__
)),而这在利用原型实现继承是非常重要的。例如:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
为原型添加方法或属性
生产环境中,我们可以使用 Object.getPrototypeOf
方法来获取实例对象的原型,然后再来为原型添加方法/属性。
Object.getPrototypeOf(person1).addFun = function(){console.log('为原型添加了一个方法啦')}
person1.addFun(); // '为原型添加了一个方法啦'
es6中使用
Object.getPrototypeOf
方法可以用来从子类上获取父类,所以可以用来判断一个类是否继承了另一个类 【因为在es6中有两条继承链】
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color; // 正确
}
}
// es6中有两条继承链
ColorPoint.prototype.__proto__ === Point.prototype // true
ColorPoint.__proto__ === Point // true
Object.getPrototypeOf(ColorPoint) === Point // true
注意:这是ECMAScript 5新增的方法。
.hasOwnProperty([属性名])
作用:可以检测一个属性是存在于实例中,还是存在于原型中。
这个方法(不要忘了它是从 Object 继承来的)只在给定属性存在于对象实例中时,才会返回 true。
alert(person1.hasOwnProperty("name")); //false
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person1.hasOwnProperty("name")); //true
Object.getOwnPropertyDescriptor()
此方法只能用于实例属性,要取得原型属性的描述符,必须直接在原型对象上调用 Object.getOwnPropertyDescriptor()方法。
注意:这是ECMAScript 5新增的方法。
person1.age = "26";
Object.getOwnPropertyDescriptor(person1,'age'); // 实例属性
Object.getOwnPropertyDescriptor(Person.prototype,'name'); // 原型对象上的属性
Person.prototype的age结果为:
{
"value": 'Nicholas',
"writable": true,
"enumerable": true,
"configurable": true
}
2. 原型与 in 操作符
in
有两种方式使用 in 操作符:
- 单独使用
- 在 for-in 循环中使用
单独使用
此时in 操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中。
【牢记下面这个例子(in,for-in)】
function Person(name){
this.name = name;
}
Person.prototype.color = "red";
Person.prototype.getName = function(){return this.name}
var person1 = new Person('caicai');
console.log(person1) // {name: "caicai"}
console.log("color" in person1); //true
console.log("constructor" in person1); //true
// 默认的constructor是不可枚举的,所以for-in无法读取到
for(let i in person1){
console.log(i) // 'name' 'color' 'getName'
};
// 判断属性是否仅为原型中的属性:
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
在 for-in 循环中使用
在使用 for-in 循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。
屏蔽了 原型中不可枚举属性(即将 [[Enumerable]]标记为 false 的属性) 的实例属性 也会在 for-in 循环中返回,因为根据规定,所有开发人员定义的属性都是可枚举的——只有在 IE8 及更早版本中例外。
返回的内容中还会包含继承的属性。(但不会包含Object.prototype上的属性)
function Animal(name){
this.name = name
};
Animal.prototype.getName = function(){
return this.name
};
function Cat(name){
Animal.call(this,name)
};
// Cat继承Animal
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
var cat = new Cat('caicai');
for(let i in cat){
console.log(i) // name constructor getName
}
function Cat1(name){
Cat.call(this,name)
};
// Cat1继承Cat
Cat1.prototype = Object.create(Cat.prototype);
// 手动设置constructor,则constructor的[[Enumerable]]值会变为true
Cat1.prototype.constructor = Cat1;
var cat2 = new Cat1('shitou');
for(let i in cat2){
console.log(i)// name constructor getName
}
ECMAScript 5 也将 constructor 和 prototype 属性的[[Enumerable]]特性设置为 false
Object.keys()
作用:取得对象上所有可枚举的实例属性。
这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
如果取实例对象的,则只会返回实例上可枚举属性;如果取原型对象的,则只会返回原型对象上可枚举的属性。
注意:这是ECMAScript 5新增的方法。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var keys = Object.keys(Person.prototype);
console.log(keys); //["name", "age", "job", "sayName"]
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
console.log(p1keys); // ["name", "age"]
Object.getOwnPropertyNames()
作用:可得到所有实例属性,无论它是否可枚举。
下面例子包含了不可枚举的 constructor 属性:
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); //["constructor", "name", "age", "job", "sayName"]
var keys2 = Object.getOwnPropertyNames(p1);
console.log(keys); // [“name”,”age”]
Object.keys()和 Object.getOwnPropertyNames()方法都可以用来替代 for-in 循环.
小结
属性 in obj
: obj能够访问属性时返回true(不论属性在实例还是在原型中,不论属性是否可枚举)for-in
:遍历实例和原型上所有可枚举的属性Object.keys(obj)
:返回obj自身可枚举的属性Object.getOwnPropertyName(obj)
:返回obj自身所有属性(可枚举的+不可枚举的)