JS(6)——原型对象

1. prototype

函数定义的时候函数本身会默认为该函数创建一个prototype的属性,而如果用new 运算符来生成一个对象的时候就没有prototype属性prototype也是一个对象认情况下prototype包含了2个属性,一个是constructor,另外一个是[[prototype]](大多数浏览器下显示为__proto__)。constructor属性是一个指向prototype属性所在函数的指针。例如当我们定义如下函数:

function Person(){
  }


根据上图可以看出Person对象会自动获得prototyp属性,而prototype也是一个对象,会自动获得一个constructor属性,该属性正是指向Person对象。当调用构造函数创建一个实例的时候,实例内部将包含一个内部指针(很多浏览器这个指针名字为__proto__)指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间,而不是实例与构造函数之间。

function Person(name){
       this.name=name;
  }
 Person.prototype.printName=function(){
       alert(this.name);
}
var person1=new Person('Byron');
var person2=new Person('Frank');

Person的实例person1中包含了name属性,同时自动生成一个__proto__属性,该属性指向Personprototype,可以访问到prototype内定义的printName方法。如下图:


function Person(name){
      this.name=name;
  }

  Person.prototype.share=[];

  Person.prototype.printName=function(){
       alert(this.name);
  }

 var person1=new Person('Byron');
 var person2=new Person('Frank');

 person1.share.push(1);
 person2.share.push(2);
 console.log(person2.share); //[1,2]

搜索首先从对象实例开始,如果在实例中找到该属性则返回,如果没有则查找prototype,如果还是没有找到则继续递归prototypeprototype对象,直到找到为止,如果递归到object仍然没有则返回错误。同样道理如果在实例中定义如prototype同名的属性或函数,则会覆盖prototype的属性或函数。


2. constructor

constructor始终指向创建当前对象的构造函数我们知道每个函数都有一个默认的属性prototype,而这个prototypeconstructor默认指向这个函数。例如

function Person(name) {
     this.name = name;
 };

  Person.prototype.getName = function() {
       return this.name;
 };

 var p = new Person("haorooms");
 console.log(p.constructor === Person);  // true
 console.log(Person.prototype.constructor === Person); // true

// 将上两行代码合并就得到如下结果
 console.log(p.constructor.prototype.constructor === Person); // true

当我们重新定义函数的prototype时(注意和上例的区别,这里不是修改而是覆盖),

function Person(name) {
    this.name = name;
  };

 Person.prototype = {

    getName: function() {
        return this.name;
     }
 };

 var p = new Person("haorooms");

console.log(p.constructor === Person);  // false

console.log(Person.prototype.constructor === Person); // false

console.log(p.constructor.prototype.constructor === Person); // false

为什么呢? 原来是因为覆盖Person.prototype时,等价于进行如下代码操作:

Person.prototype = new Object({
     getName: function() {
          return this.name;
     }
 });

constructor始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:

function Person(name) {
    this.name = name;
 };

 Person.prototype = {
    getName: function() {
     return this.name;
   }
 };

var p = new Person("haorooms");

console.log(p.constructor === Object);  // true

console.log(Person.prototype.constructor === Object); // true

console.log(p.constructor.prototype.constructor === Object); // true

怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:

function Person(name) {
   this.name = name;
 };

 Person.prototype = {
     getName: function() {
      return this.name;
    }
 };

 Person.prototype.constructor = Person;

 var p = new Person("haorooms");

 console.log(p.constructor === Person);  // true

 console.log(Person.prototype.constructor === Person); // true

 console.log(p.constructor.prototype.constructor === Person); // true

3. [[prototype]]

几乎任何对象有一个[[prototype]]属性,在标准中,这是一个隐藏属性。该属性指向的是这个对象的原型。那么一个对象的[[prototype]]属性究竟怎么决定呢?这是由构造该对象的方法决定的。一般有三种构造一个对象的方法

1对象是通过对象字面量构造出来的。

var person1 = {
    name: 'cyl',
    sex: 'male'
};

形如这个形式的叫做对象字面量。这样子构造出的对象,其[[prototype]]指向Object.prototype

2这个对象是由构造函数构造出来的。

function Person(){}
var person1 = new Person();

通过new操作符调用的函数就是构造函数。由构造函数构造的对象,其[[prototype]]指向其构造函数的prototype属性指向的对象。每个函数都有一个prototype属性,其所指向的对象带有constructor属性,这一属性指向函数自身。在本例中,person1[[prototype]]指向Person.prototype

3对象是由函数Object.create构造的。

var person1 = {
    name: 'cyl',
    sex: 'male'};
var person2 = Object.create(person1);

本例中,对象person2[[prototype]]指向对象person1。在没有Object.create函数的日子里,人们是这样做的:

Object.create = function(p) {
    function f(){}
    f.prototype = p;
    return new f();     }

然而虽然说[[prototype]]是一个隐藏属性,但很多浏览器都给每一个对象提供

.__proto__这一属性,这个属性就是上文反复提到的该对象的[[prototype]]。由于这个属性不标准,因此一般不提倡使用。ES5中用Object.getPrototypeOf函数获得一个对象的[[prototype]]ES6中,使用Object.setPrototypeOf可以直接修改一个对象的[[prototype]]


4. 确定原型和实例的关系

可以通过两种方式来确定原型和实例之间的关系。第一种方式是使用instanceof

作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回

true。以下几行代码就说明了这一点。

alert(instance instanceof Object); //true

alert(instance instanceof SuperType); //true

alert(instance instanceof SubType); //true

由于原型链的关系,我们可以说instance ObjectSuperType SubType 中任何一个类型的实例。因此,测试这三个构造函数的结果都返回了true

第二种方式是使用isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型,因此isPrototypeOf()方法也会返回true,如下所示。

alert(Object.prototype.isPrototypeOf(instance)); //true

alert(SuperType.prototype.isPrototypeOf(instance)); //true

alert(SubType.prototype.isPrototypeOf(instance)); //true

创建了自定义的构造函数之后,其原型对象默认只会取得constructor 属性;至于其他方法,则都是从Object 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。


虽然在所有实现中都无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true

ECMAScript 5 增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。例如:

alert(Object.getPrototypeOf(person1) == Person.prototype); //true

alert(Object.getPrototypeOf(person1).name); //"Nicholas"

使用Object.getPrototypeOf()可以方便地取得一个对象的原型,而这在利用原型实现继承的情况下是非常重要的。

支持这个方法的浏览器有IE9+Firefox 3.5+Safari 5+Opera 12+Chrome


5. 获取可枚举属性Object.keys()、获取所有属性Object.getOwnPropertyNames()

要取得对象上所有可枚举的实例属性,可以使用ECMAScript 5 Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。例如:

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);

alert(keys); //"name,age,job,sayName"

var p1 = new Person();

p1.name = "Rob";

p1.age = 31;

var p1keys = Object.keys(p1);

alert(p1keys); //"name,age"

这里,变量keys 中将保存一个数组,数组中是字符串"name""age""job""sayName"。这个顺序也是它们在for-in 循环中出现的顺序。如果是通过Person 的实例调用,则Object.keys()返回的数组只包含"name""age"这两个实例属性。如果你想要得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法。

var keys = Object.getOwnPropertyNames(Person.prototype);

alert(keys); //"constructor,name,age,job,sayName"

注意结果中包含了不可枚举的constructor 属性。Object.keys()Object.getOwnPropertyNames()方法都可以用来替代for-in 循环支持这两个方法的浏览器有IE9+Firefox 4+Safari 5+Opera12+Chrome

猜你喜欢

转载自blog.csdn.net/u013789656/article/details/80942385
今日推荐