JS面向对象编程(三):原型

1、prototype(原型)是什么?

 在JavaScript中,咱们每建立一个函数,都会有一个prototype(原型)属性:这个属性是一个指针,指向一个对象,对象的用途是包含能够由特定类型的全部实例共享的属性和方法浏览器

图3-1

prototype 就是经过调用构造函数而建立的那个对象实例的原型对象。使用原型可让全部对象实例共享它所包含的属性和方法bash

function Person(){}

Person.prototype.name = "lilei"; 
Person.prototype.age = 26; 
Person.prototype.sayHello = function(){ 
    console.log(this.name+":Hello!"); 
}; 
var person1 = new Person(); 
person1.sayHello(); //"lilei:hello" 

var person2 = new Person(); 
person2.sayHello(); //"lilei:hello" 
console.log(person1.sayHello == person2.sayHello); //true 
复制代码

2、原型、构造函数、实例对象之间的关系:

图3-2

 全部原型对象都会自动得到一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。如图3-1:Person.prototype.constructor 指向 Person。函数

Person.prototype.constructor === Person // true
复制代码

 实例化的对象内部有一个看不见的__proto__指针,指向原型对象,__proto__只在浏览器内部使用,__对脚本则是彻底不可见的。虽然在全部实现中都没法访问到__proto__,但能够经过 isPrototypeOf()方法来肯定对象之 间是否存在这种关系:this

console.log(Person.prototype.isPrototypeOf(person1)); //true 
console.log(Person.prototype.isPrototypeOf(person2)); //true 
复制代码

 全部对象的__proto__都指向其构造函数的prototypespa

person1.__proto__  === Person.prototype //true
复制代码

3、__proto__与prototype的关系

  1. 全部构造函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)
Person.__proto__ === Function.prototype     // true
Number.__proto__ === Function.prototype     // true
Boolean.__proto__ === Function.prototype    // true
String.__proto__ === Function.prototype     // true
Object.__proto__ === Function.prototype     // true
Function.__proto__ === Function.prototype   // true
Array.__proto__ === Function.prototype      // true
RegExp.__proto__ === Function.prototype     // true
Error.__proto__ === Function.prototype      // true
Date.__proto__ === Function.prototype       // true
复制代码

因此全部构造函数都继承了Function.prototype的属性及方法prototype

  1. Function.prototype也是惟一一个typeof 为 “function”的prototype。其它的构造函数的prototype都是一个对象
console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object
复制代码
  1. 除Object以外全部构造函数的原型的__proto__都指向Object.prototype
Function.prototype.__proto__ === Object.prototype   // true
Person.prototype.__proto__ === Object.prototype     // true
Number.prototype.__proto__ === Object.prototype     // true
Boolean.prototype.__proto__ === Object.prototype    // true
String.prototype.__proto__ === Object.prototype     // true
Array.prototype.__proto__ === Object.prototype      // true
Function.prototype.__proto__ === Object.prototype   // true
RegExp.prototype.__proto__ === Object.prototype     // true
Error.prototype.__proto__ === Object.prototype      // true
Date.prototype.__proto__ === Object.prototype       // true
复制代码

 Object.prototype的__proto__?设计

Object.prototype.__proto__ === null  // true
复制代码

4、实例属性与原型属性

  1. 原型包含 constructor 属性,而该属性也是共享的,所以能够经过对象实例访问。
person1.constructor === Person.prototype.constructor    //true
person1.constructor === Person                          //true
person1.name === Person.prototype.name                  //true
person1.age === Person.prototype.age                    //true
person1.sayHello ===  Person.prototype.sayHello         //true
复制代码
  1. 虽然能够经过对象实例访问保存在原型中的值,但却不能经过对象实例重写原型中的值:重写的属性是保存在实例上的属性
function Person(){}

Person.prototype.name = "lilei"; 
Person.prototype.age = 26; 
Person.prototype.sayHello = function(){ 
    console.log(this.name+":Hello!"); 
}; 
var person1 = new Person(); 
var person2 = new Person(); 
person1.name = "Greg"; 
console.log(person1.name);    // "Greg" ——来自实例
console.log(person2.name);    // "lilei" ——来自原型
复制代码

访问person1.name时会在这个实例上搜索一个名为 name的属性。这个属性若是存在,就返回,不存在 就到原型上去找。当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;添加这 个属性只会阻止咱们访问原型中的那个属性,但不会修改那个属性指针

  1. 使用 delete 操做符则能够删除实例属性,从而让咱们可以从新访问原型中的属性
function Person(){}

Person.prototype.name = "lilei"; 
Person.prototype.age = 26; 
Person.prototype.sayHello = function(){ 
    console.log(this.name+":Hello!"); 
}; 
var person1 = new Person(); 
var person2 = new Person(); 
person1.name = "Greg"; 
console.log(person1.name);    // "Greg" ——来自实例
console.log(person2.name);    // "lilei" ——来自原型

delete person1.name; 
console.log(person1.name);    // "lilei" ——来自原型
复制代码
  1. 经过hasOwnProperty()方法,能够判断访问属性是否为 实例属性
person1.name = "Greg"; 
person1.hasOwnProperty("name")  //true
person2.hasOwnProperty("name")  //false
复制代码

 当 person1 重写name 属性后返回 true,由于这时候 name 是一个实例属性,而非原型属性code

  1. 经过in能够判断对象是否具备某个属性,包括对象实例及其原型的属性;
person1.name = "Greg"; 
person1.hasOwnProperty("name")  //true
person2.hasOwnProperty("name")  //false
console.log("name" in person1)  //true
console.log("name" in person2)  //true
复制代码
  1. 经过在实例上添加一个同名属性,能够隐藏原型中的对应属性。可是,对于引用类型来讲,在未重写的状况下,修改引用类型值的属性,就是在原型属性值的基础上修改 原型属性值的属性
function Person(){}

Person.prototype = { 
    constructor: Person, 
    name : "lilei", 
    age : 26, 
    friends : ["Jim", "Tom"]
};
var person1 = new Person(); 
var person2 = new Person(); 

person1.friends.push("hanmeimei"); 

console.log(person1.friends); //"Jim,Tom,hanmeimei" 
console.log(person2.friends); //"Jim,Tom,hanmeimei" 
console.log(person1.friends === person2.friends); //true 
复制代码

每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型 对象的内部指针。若是咱们让原型对象等于另外一个类型的实例,会发生什么?因此下一节《继承》cdn


文章参考:

《JavaScript 高级程序设计》中文译本 第三版

相关文章
相关标签/搜索