1. 程式人生 > >通過物件、建構函式深入理解js原型

通過物件、建構函式深入理解js原型

說一些題外話

在最近的實習工作中因大量涉及到原生js的編寫,因此在工作之餘重新拿起紅寶書閱讀,每重新看一遍都受益匪淺,最近又讀到了js中難點原型、原型鏈,這裡僅是本人對js原型的理解和觀點,若有槽點錯誤之處,希望不吝賜教。

接下來我們就直奔主題

怎樣建立物件?建立物件方法有幾種?

使用建構函式建立物件

var M = function (name) {
 		 this.name = name;
 	 }

這是比較傳統建立物件的方法,這裡 M 為建構函式,當建立一個建構函式的時候,就為這個建構函式添加了一個 prototype 原型屬性表示M的原型物件,而在這個prototype 中自動生成了一個 constructor

構造器指向擁有 prototype 指標的建構函式,而這裡就是 M

用如下的UML圖便容易理解

M建構函式原型物件constructorprototype指標指向擁有指向擁有 prototype指標的建構函式M建構函式原型物件constructor
console.log(M.prototype.constructor===M)//true

ok,理解了建構函式與prototype、constructor之間的關係,那麼例項與他們之間的關係是如何呢?

var m=new M("Bob");//例項化一個物件 m

當新建一個例項時就為 m 建立了__proto__ 或([[Prototype]]、< prototype >)屬性指向建構函式M的原型物件 prototype

例項物件m原型物件(prototype)M建構函式constructor通過 __proto__指標指向用 prototype 表示擁有指向擁有 prototype指標的建構函式例項物件m原型物件(prototype)M建構函式constructor

注意:原型是m與M的prototype之間的聯絡,不是m與M之間的聯絡,換句話說建構函式與例項物件沒有直接關係 因此我們可以得出 例項物件的__proto__嚴格相等於 建構函式的 prototype物件

console.log(m.__proto__===M.prototype) // true
console.log(m.constructor===M) // true

現在你理解了原型的概念了嗎? 如果還不太理解就繼續往下看…

函式M實際上也是Function型別的一個例項

這裡函式M是一個建構函式只是因為是大寫的M,用來區分普通的函式,但實際上他們沒什麼實質性的區別。 所以函式M實際上也是Function型別的一個例項

//Function引數列表所有引數都為字串型別,最後一個字串內容是要執行的函式程式碼,其餘字串為數量
// var functionname = new Function( [argname1, [... argnameN,]] body );
var M=new Function("name","this.name=name");

既然M也是一個例項物件,那麼它也有__proto__ 指向 它的建構函式的prototype 因此我們可以得出:

console.log(M.__proto__===Function.prototype); //true
console.log(M.constructor===Function); //true
console.log(Function.prototype.constructor===Function); //true

那麼 Function 有__proto__ 屬性嗎?

顯然是有的, 我們可別忘了函式也是一個物件,函式有自己的屬性和方法,因此你能說它不滿足物件的概念嗎? 所以Function 也是 Object 的一個 例項物件嗎? 答案是錯誤的。 前面我們說過誰是誰的例項物件就有__proto__ 指向它的建構函式的prototype

console.log(Function.constructor === Object) // false
console.log(Function.__proto__===Object.prototype); // fasle
console.log(Function.constructor === Function) // true
console.log(Function.__proto__===Function.prototype); // true

為什麼會這樣? 首先Function 不是 Object 的一個 例項物件, Function 還是Function 的例項物件 (Array、Function、Object 都是 Function 例項物件) 因此都有

console.log(Array.__proto__===Function.prototype) //true
console.log(Function.__proto__===Function.prototype) //true
console.log(Object.__proto__===Function.prototype) //true

Array、Function的 prototype 原型物件 才是 Object的 例項物件 既然 Function.prototype 是Object的例項物件 必然有__proto__ 指向建構函式的prototype

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

這就是我下一篇要講的原型鏈、繼承的概念 接下來還有一個疑問,Object有沒有__proto__ (原因前面已經講過) 噢不是 是

Object.prototype 有沒有__proto__?

答案是沒有的

console.log(Object.prototype.__proto__) //null

這就是原型鏈的終點,也是每一個例項物件原型的終點。

好現在我們已經瞭解了原型是怎樣產生的,並且具有什麼的特點,它與建構函式和例項物件之間的聯絡

m的建構函式是M

m.proto===M.prototype

M的建構函式是Function

M.proto===Function.prototype

Function的建構函式是Function

Function.proto===Function.prototype

Function.prototype的建構函式是Object

Function.prototype.proto===Object.prototype

Object的建構函式是Function

Object.proto===Function.prototype

Object的prototype沒有建構函式沒有__proto__

Object.prototype.proto===null

我們得出結論: 只有例項物件才有__proto__ 屬性,只有建構函式才有ptototype物件 且例項物件的__proto__指向建構函式的prototype 如果既是建構函式又是另一個建構函式的例項物件,那麼同時擁有__proto__ 和 prototype

使用物件字面量建立物件

  var o = {
  	name: "ojbk",
  	age: 22
  }

使用物件字面量建立物件是一種最常用的建立物件的方法, 前面我們說過當建立物件的時候就為新增__proto__ 指向建構函式的prototype

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

其實這裡本身就是對Object的一個例項物件

 o2 = new Object({
	ok: 'ok',
	no: 'no'
  })
console.log(o2.__proto__===Object.prototype); //true
console.log(Object.prototype.constructor===Object); //true

說到這兒相信同學們都理解了原型的基本概念了 其實還有一種建立物件的方法,

使用Object.create()建立物件

這種方法其實用在繼承方面較多,不過也是建立物件的一種簡便方法

 // 通過Object.create()方法建立物件
        (function () {
            var OP = {
                pname: "pname",
                page: '22'
            }
            
	// 使用此方法強制把p.__proto__屬性指向OP物件 注意是OP物件 而不是OP.prototype  
	// OP物件沒有prototype屬性 只有建構函式才有prototype物件
	// 因為Object是OP物件的建構函式,OP是Object的例項  因此Object才有prototype物件
            var p = Object.create(OP);
            console.log(p.constructor === OP); //false
            console.log(p.__proto__ === OP); //true

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

 	// OP是Object的例項 只有例項物件才有__proto__屬性 
 	//且指向建構函式的prototype 原型物件
            console.log(OP.__proto__ === Object.prototype);
        })();

這裡其實是重寫原型,重寫原型是在原型鏈繼承方面的知識, 重寫原型後還會有一些其他的影響,有興趣的同學可以去了解了解。

總結

  1. 原型是怎樣產生的?
  2. 例項物件、建構函式、proto、prototype互相的聯絡
  3. 建立物件的幾種方式
  4. 原型鏈的終點是Object.prototype
  5. Array、Function、Object、都是Function 的例項物件
  6. Array.prototype、Function.prototype是Object的例項物件(原型鏈相關知識)