Object-Oriented(二)原型对象

自用备忘笔记函数

 

1. 理解原型对象this

只要建立函数,函数上就会建立一个 prototype 属性指向函数的原型对象。spa

function Person() {}
Person.prototype  //指向该函数的原型对象

全部原型对象会自动得到一个 constructor 属性指向 prototype 属性所在的函数。prototype

Person.prototype.constructor === Person;  //true

每一个经过构造函数生成的实例对象,都包含一个 [[Prototype]] 属性指向其构造函数的原型对象,在 Firefox、Safari 和 Chrome 中能够经过 __proto__ 来访问。code

var Daryl = new Person();
Daryl.__proto__ === Person.prototype;  //true

虽然没法经过标准的方法访问 [[Prototype]] 方法,但能够经过 isPrototypeOf() 方法来确实关系。对象

Person.prototype.isPrototypeOf(Daryl);  //true

ES5新增了一个方法:Object.getPrototypeOf(),该方法能够返回 [[Prototype]] 的值。blog

Object.getPrototypeOf(Daryl) === Person.prototype;  //true

虽然经过实例对象能够访问原型中的属性,但没法经过实例对象重写原型中的属性。字符串

function Person() {}
Person.prototype.name = 'Daryl';

var person1 = new Person(),
    person2 = new Person();

person1.name = 'Nicholas';
person1.name;  //Nicholas
person2.name;  //Daryl

对对象属性的访问,是先检索实例上有无该属性,再检索原型对象上有无该属性。若删除实例上的属性,则会恢复对原型对象属性的访问。get

person1.name = 'Nicholas';
delete person1.name;
person1.name;  //Daryl

经过 hasOwnProperty 方法能够检测某个属性究竟来自实例仍是原型。原型

person1.name = 'Nicholas';
person1.hasOwnProperty('name');  //true
person2.hasOwnProperty('name');  //false

 

2. 原型与 in 操做符

in 操做符用来检测某个对象中是否有某个属性,能够单独使用,也能够搭配 for 使用。

person1.name = 'Nicholas';
'name' in person1;  //true
'name' in person2;  //true 

使用 for-in 遍历对象的属性时会将原型上的属性一块儿遍历,若是须要滤掉这部分,能够搭配 hasOwnProperty 方法一块儿使用。

function Person() {}
Person.prototype.name = 'Nicholas';
var person = new Person();
person.age = 29;
person.gender = 'male';

for (var o in person) {
    if (person.hasOwnProperty(o)) {
        console.log(o);  //age, gender
    }
}

ES5新增了 Object.keys 方法,用来返回一个全部可枚举的实例属性的集合。

Object.keys(person);  //['age', 'gender']

若是想要获得全部的属性,不管是否可枚举,能够使用 Object.getOwnPropertyNames

Object.getOwnPropertyNames(Person.prototype);  //['constructor', 'name'] 

以上两种方法均可以用来代替 for-in 循环。

 

3. 更简单的原型语法

为了不重复书写,能够经过对象字面量的方式重写原型对象。 

function Person() {}
Person.prototype = {
    name: 'Nicholas',
    age: 29,
    sayName: function() {
        alert(this.name);
    }
};

这种方式会致使原型对象上的 constructor 属性丢失,能够手动指定 constructor 属性。

function Person() {}
Person.prototype = {
    constructor: Person,
    name: 'Nicholas',
    age: 29,
    sayName: function() {
        alert(this.name);
    }
};

但 constructor 自己是不可枚举的,能够经过 ES5 新增的 Object.defineProperty() 方法设置。

Object.defineProperty(Person.prototype, 'constructor', {
    enumerable: false,
    value: Person
});

 

4. 原型的动态性

因为在原型中查找值的过程是一次搜索,所以在任什么时候候对原型对象的修改都会当即反映在实例上。

var Daryl = new Person();
Person.prototype.sayHi = function() {
    alert('Hi');
};
Daryl.sayHi();  //Hi

可是重写原型对象会致使构造函数与原型之间的联系被切断。

var Daryl = new Person();
Person.prototype = {
    sayHi: function() {
        alert('Hi');
    }
};
Daryl.sayHi();  //出错

 

5. 原生对象的原型

原型模式不只应用在自定义类型方面,就连全部原生的引用类型,都是采用这种模式建立的。它们都在其构造函数的原型上定义了方法。

Array.prototype.sort;  //function
String.prototype.substring;  //function

还能够在原生对象上定义新方法,以下,定义一个反转字符串的方法。

String.prototype.reverseString = function() {
    return this.split('').reverse().join('');
};

'Hello World!'.reverseString();  //!dlroW olleH

 

6. 原型的问题

因为原型上全部属性均是共享的,若某个实例对原型中的引用类型属性进行修改,则这一个修改会反映在全部实例上。

function Person() {}
Person.prototype.friends = ['Shelby', 'Court'];

var person1 = new Person(),
    person2 = new Person();

person1.friends.push('Van');
person2.friends;  //['Shelby', 'Court', 'Van'];
相关文章
相关标签/搜索