js原型中的方法小结

@一棵菜菜  March 27, 2019

说明

红皮书6.2.3章节里的内容。这部分讲述的方法颇多,发现自己很容易记混淆或者遗忘,所以简单整理了下,以便自己后续回顾和快速查阅。

1. 理解原型对象

可以通过两种方式来确定原型和实例之间的关系:

  1. 使用 instanceof 操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true。
  2. 使用 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 操作符:

  1. 单独使用
  2. 在 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自身所有属性(可枚举的+不可枚举的)

牢记理解js继承图

js继承


添加新评论