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 高級程序設計》中文譯本 第三版

相關文章
相關標籤/搜索