前言:
本文章帶有強烈的個人風格主義,我以自己的方式理解原型鏈,還請各位路過的大佬們,多多指點,有啥不懂直接提出來,大家多多交流。
建構函式:
什麼是建構函式?
var afunc = function (name) { // afunc就是建構函式!!!
this.name = name
this.get = function() {
return this.name;
};
}
var robotA = new afunc('html');
console.log( robotA.get() ); // "html"
var robotB = new afunc('css');
console.log( robotB.get() ); // "css"
var robotC = new afunc('javascript');
console.log( robotC.get() ); // "javascript"
複製程式碼
建構函式像一個克隆機器,它能夠源源不斷地克隆相同的東西,機器人A、機器人B..... ,都有相同的 屬性和方法 ;
判斷一個物件的建構函式:
就像上面例子一樣,我們怎麼找到,一個例項物件(robotA,robotB, robotC)的建構函式呢???
var afunc = function (name) { // afunc就是建構函式!!!
this.name = name
this.get = function() {
return this.name;
};
}
var robotA = new afunc('html');
console.log(robotA.constructor); // 函式 afunc
複製程式碼
辦法很簡單,通過 例項物件(robotA
)的 constructor
屬性,就可以找到此例項物件的建構函式啦!!!
那大家有沒有考慮過一個問題,我們宣告的 afunc建構函式 又是誰幫我們生成的呢???用上述辦法試試:
var afunc = function (name) { // afunc就是建構函式!!!
this.name = name
this.get = function() {
return this.name;
};
}
var robotA = new afunc('html');
console.log(afunc.constructor); // function Function() { }
複製程式碼
很顯然,我們宣告的建構函式 afun
是由另一個建構函式 Function
生成的!!!
讀到這裡,想必你肯定很疑惑,那麼 這個 Function
函式又是誰構造的呢 ???
我們先把這些疑惑先放放,我後面會解釋,在這之前,我們先看看,JS 內建的像 Function
一樣的函式有哪些?
JS 內建的建構函式:
聰明的讀者,想必都已經想到了,我們宣告一個數字、字串等等內建型別都 可能 有建構函式!!!
// 7種內建型別依次試試,不知道哪7種的同學可以看看我另一篇文章 《JS靈巧判斷7種型別的方式》
/*-----------------顯然兩個貨沒有建構函式---------------------*/
console.log(null) // null
console.log(undefined) // undefined
/*-----------------七種型別中的其他 5 種型別--------------------*/
console.log(Number); // ƒ Number() { }
console.log(String); // ƒ String() { }
console.log(Object); // ƒ Object() { }
console.log(Boolean); // ƒ Boolean() { }
console.log(Symbol); // ƒ Symbol() { }
--------------------7種型別之外的內建建構函式---------------------*/
console.log(Date); // ƒ Date() { }
console.log(RegExp); // ƒ RegExp() { }
/*--------------------注意:Math不是建構函式而是一個物件------------*/
console.log(Math); // [object Math] { ... }
複製程式碼
大概就列出來這麼些 內建函式 吧,那我們要怎樣使用這些建構函式呢???
當我們宣告一個字串、數字時,這些函式就會開始工作,證明這個真理:
var n = 123; // 當你寫的 123 會被瀏覽器用 Number() 內建函式構造出來;
console.log(n.constructor); // ƒ Number() { }
var s = 'javascript';
console.log(s.constructor); // f String() { }
// 懶,其他我就省略啦 ......
// 其實你也可以用 new 關鍵字宣告一個 數字、字元......,不過用這種方法的人該多蠢!!!
var n = new Number(123);
var s = new String('javascript');
console.log(typeof n); // "object"
console.log(typeof s); // "obejct"
console.log(Number(n)); // w3c 上是通過強制轉換來獲取 123 這個值的,
複製程式碼
你可以很清晰地看到,其實,我們的這裡個 內建函式 跟我們宣告的 afunc
沒有區別,都是同樣的操作!!!
現在,我們再來看一個有趣的現象,我們這些內建函式的建構函式又是誰???
Number.constructor // ƒ Function() { } 構造者是 Function 函式
String.constructor // ƒ Function() { } 構造者是 Function 函式
Object.constructor // ƒ Function() { } 構造者是 Function 函式
Boolean.constructor // ƒ Function() { } 構造者是 Function 函式
Symbol.constructor // ƒ Function() { } 構造者是 Function 函式
Date.constructor // ƒ Function() { } 構造者是 Function 函式
RegExp.constructor // ƒ Function() { } 構造者是 Function 函式
Math.constructor // ƒ Object() { } 注意:Math已經不是一個函式了,構造者是一個Object函式!!!
複製程式碼
現在,我們清楚了:
- 我們每宣告一個函式,瀏覽器解析到這個函式是會交給內建函式
Function
來構造!!! - 再看看
Math
物件,你也已經可能猜到,物件是由f Object()
函式所構造的!!!
相類似,我們用 var關鍵字宣告一個字串、數字、布林值等等,全部都是由瀏覽器解析然後交給內建函式處理!
函式物件和普通物件
估計看到標題你就傻了吧,函式物件是什麼鬼?其實,函式的本質也是一個物件,只不過函式的能力很強大罷了!
// 如何區分,函式物件和普通物件???
var o = {};
var f = function(){ };
console.log( o.constructor ); // ƒ Object() { }
console.log( f.constructor ); // ƒ Function() { }
複製程式碼
結論:普通物件的建構函式是 Object()
, 函式物件的建構函式是 Function()
;
原型的本質:
本篇文章是講原型,前面是基礎知識,現在終於到正餐啦!!!
函式物件與普通物件的區別:
函式物件和普通物件是有區別的,那我們怎麼去分辨這種區別呢???
var o = {}; // object(普通物件)
console.log( o.prototype ); // undefined
console.log( typeof o ); // "obejct"
/*----------------------我是你的分割線-------------------------*/
var f = function(){}; // function(函式物件)
console.log( f.prototype ); // {......}
console.log( typof f ); // "function"
console.log( typof f.prototype ); // "object" 原型是一個普通物件
複製程式碼
結論:
1. 函式物件的型別是 function
,普通物件的型別是 object
!!!
2. 函式物件 有 原型 ( prototype
),普通物件 沒有 原型 prototype
的!!!
每宣告一個函式,就會有一個產生一個原型,這個原型的 引用 就儲存在 函式物件的 func.prototype
上面;
為什麼要用原型?
回答這個問題前,還需要一點前置知識 。。。。。。
理解什麼是 引用:
引用的概念出自於 C++
,引用 的實質是 指標,不過用 引用 來思考 javascript 中的物件我想更為合適;
首先,我們所宣告的每一個變數都是會佔用計算機資源的,它們被儲存在計算機記憶體的某個位置;
引用也會佔據空間,小到一個小小的數字,大到一個巨大的物件:
如圖,所示,引用,就是 “外號” ,一個人可以有很多種外號,無論叫你哪一個外號,都是指你本身!!!
這是如何體現在程式碼裡面呢?
小明,有兩個外號,狗蛋 和 嚶嚶嚶;
小紅,有兩個外號,二狗 和 哈哈哈;
那麼,小明是不是小紅呢?小明和小紅大家都很熟悉了,不是一個人;
var xiaoming = {name: '小明'}; // 我們用小明代指這個物件
var goudan = xiaoming; // 小明 有一個外號叫做 狗蛋
var yingyingying = xiaoming; // 小明 有一個外號叫做 嚶嚶嚶
console.log( goudan === xiaoming ); // true 狗蛋 是 小明
console.log( yingyingying === xiaoming ); // true 嚶嚶嚶 是 小明
/*------------我是分割線-----------------*/
var xiaohong = {name: '小紅'}; // 我們用小紅代指這個物件
var ergou = xiaohong; // 小紅 有一個外號叫做 二狗
var hahaha = xiaohong; // 小紅 有一個外號叫做 哈哈哈
console.log( ergou === xiaohong ); // true 二狗 是 小紅
console.log( hahaha === xiaohong ); // true 哈哈哈 是 小紅
/*----------------我是分割線---------------------*/
console.log( xiaohong === xiaoming ); // false 小明 不是 小紅 <= 這個例子雖然無聊但是有用
複製程式碼
很顯然的,我們用 var
宣告的變數就叫做 引用(別名) 我們通過這個 引用 來找到這個物件,在計算機裡面的位置。
初次認識屬性 __proto__
:
在回答為 什麼要用原型 這個問題時,我們還需要明白一個屬性 __proto__
。
我這裡先不闡述 __proto__
屬性具體的定義和概念,以免大家思維混亂 。。。。。。
var afunc = function (name) { // afunc就是建構函式!!!
this.name = name
this.get = function() {
return this.name;
};
}
afunc.prototype = {
set: function(newName) {
this.name = newName;
}
}
var robotA = new afunc('html');
console.log( robotA.prototype ); // undefined 前面文章已經解釋了 普通物件沒有 原型
console.log( robotA.__proto__ ); // {set: function() {...}} 找到了原型。
robotA.set('css');
console.log(robotA.get()); // "css"
複製程式碼
現在你能夠理解,robotA.__proto__
和 afunc.prototype
都僅僅是 afunc
的原型的 引用 了嗎???
如果,不理解,再仔細想想,思考一下,在之後的內容裡,我會展示 __proto__
更為詳細的講解;
當我們呼叫 robotA.get()
方法時,JS 會先去 建構函式 裡面找是否有此方法,沒有的話就到 原型 裡面找!!!
回答為什麼要用原型:
我們明白了什麼是引用,就非常好解釋,為什麼需要用原型。
var afunc = function (name) { // afunc就是建構函式!!!
this.name = name
this.get = function() {
return this.name;
};
}
afunc.prototype = { // 呼叫原型物件
set: function(newName) {
this.name = newName;
}
}
var robotA = new afunc('html');
var robotB = new afunc('css');
var robotC = new afunc('javascript');
robotA === robotB // false 這兩在計算機中的物件 是不一樣
robotA === robotC // false
robotB === robotC // false
robotA.__proto__ === robotB.__proto__ // true 這兩個引用所指的物件 是相同的
robotA.__proto__ === robotC.__proto__ // true
robotB.__proto__ === robotC.__proto__ // true
複製程式碼
仔細想想,robotA
、robotB
、robotC
,這是分別是三個物件的 引用(別名);
計算機為這三個物件,分配了,三個儲存空間,我們通過引用,找到這個儲存空間的位置,然後執行!!!
而 robotA.__proto__
、robotB.__proto__
、robotC.__proto__
也是三個引用(別名);
但是,這上述三個引用指向的內容,都是 afunc
函式的 原型 ,計算機裡只儲存唯一的一個 afunc
函式的 原型 ;
這樣的原型有什麼好處???
如果,你需要 new
1000 個物件,這 1000 個物件就會分配 1000 個物件的儲存空間;
試想,每次 new
相同的 屬性和方法 ,這樣系統的開銷就會變得非常大!!!
我們把相同的屬性和方法放在一起,抽象為一個原型,這 1000 個物件都訪問原型上的 共有方法。
這樣豈不是,美滋滋!!!
談談原型鏈:
終於到這一步了,感覺手都寫酸了。。。。。
原型鏈,肯定跟原型有關啦,那這個關係到底是什麼?
理解 __proto__
的本質:
每一個物件,不管是函式物件或者普通物件,都會有 __proto__
屬性。
但是,這個屬性已經 不推薦使用 了,但是為了展示什麼是 原型鏈,我用了它。
如果想了解原因:請狠狠地戳這裡,瞭解ES6標準;
var o = { };
console.log( o.__proto__ ); // {......} 生成 o 物件的原型;
console.log( o.__proto__.constructor ); // 生成 o 物件的建構函式 f Object() { };
console.log( o.__proto__.__proto__ ); // null; 到頭啦,最初的起點 null !!!
複製程式碼
關於 {......}
代表的是 object 起點原型,它也是一個普通物件!!!;
仔細思考,這兩段程式碼,我不在這裡闡述過多的東西,其實這裡很繞的;
現在,來 函式物件 啦,仔細觀察,此過程,比單純的 普通物件 要複雜!!!
var f = function() { };
console.log( f.__proto__ ); // ƒ () { } 生成 f 函式物件的原型
console.log( f.__proto__.constructor ); // 生成 f 函式物件的建構函式 f Function() { }
console.log( f.__proto__.__proto__ ); // {......} 生成 f 函式物件的原型的原型
console.log( f.__proto__.__proto__.constructor ); // ƒ Object() { }
console.log( f.__proto__.__proto__.__proto__ ); // null 又到頭啦!!!
複製程式碼
如果,你理解了這段程式碼,也就理解了,為什麼說,函式物件也是物件這句話了。
現在,我們來模擬一下 ,從 new
一個物件,開始的原型鏈過程 !!!
var f = function() { };
f.prototype = {
name: "javascript"
}
var obj = new f();
console.log(obj.__proto__); // { name: "javascript" } 構造 obj 普通物件的 原型
console.log(obj.__proto__.__proto__); // { ...... } 構造 obj 普通物件原型的 原型
console.log(obj.__proto__.__proto__.__proto__); // null 到頭啦!!!
/*------------這是你需要區分的東西----------------*/
console.log(f.prototype); // f 函式物件的原型
console.log(obj.prototype); // undefined 普通物件沒有原型!!!
複製程式碼
其餘的內容留給讀者自己去思考。試著想一想,能否畫出整個內建函式物件的原型鏈關係圖
重要參考和感謝:
- www.cnblogs.com/shuiyi/p/53…。
- www.jianshu.com/p/dee9f8b14…。
- 《你不知道的JavaScript 上卷》。
感覺不錯,點個贊再走唄!!!
在此對上面連結或者書籍的作者表示感謝和支援,歡迎大家提出問題!!!