在 JavaScript 的世界中,萬物皆對象! 可是這各類各樣的對象其實具體來劃分的話就 2 種。 一種是 函數對象
,剩下的就是 普通對象
。其中 Function
和 Object
爲JS自帶的 函數對象
。(哎? 等等, Function 爲 函數對象 能夠理解,爲何 Object
也是函數對象呢?帶着疑問咱們繼續往下看。 )javascript
Function 和 Object 爲什麼都是 函數對象呢?css
var f1 = new Function('arg', 'console.log(arg)') var test1 = new Object var f3 = new Object() console.log(typeof f1) // function console.log(typeof test1) // object console.log(typeof f3) // object
通過 new 實例化事後的 test1 和 f3 均爲 Object 即爲 普通對象。 那若是咱們直接 打印出來 未實例化的對象 的類型 呢?
var test2 = Object var test3 = Object() console.log(typeof test2) // function console.log(typeof test3) // object
這裏咱們看到了一些區別,可是這又是爲何呢? 下面咱們看完普通對象再縱向的進行一下對比
咱們先想一想一下,建立對象的方式?html
var F1 = function() {} var o1 = {} var o2 = new F1() var o3 = new Object() console.log(typeof o1) // object console.log(typeof o2) // object console.log(typeof o3) // object
function f1() {} var f2 = function() {} var f3 = new Function('arg', 'console.log(arg)') var o1 = {} var o2 = new Object() var o3 = new f1() console.log(typeof f1) // function console.log(typeof f2) // function console.log(typeof f3) // function console.log(typeof o1) // object console.log(typeof o2) // object console.log(typeof o3) // object // 對比前文中的 Function 和 Object console.log(typeof Function) // function console.log(typeof Object) // function
在上面的例子中 o1 o2 o3 爲普通對象
,f1 f2 f3 爲函數對象
。怎麼區分,其實很簡單,凡是經過 new Function()
建立的對象都是函數對象
,其餘的都是普通對象
。f1,f2,歸根到底
都是經過 new Function()的方式進行建立的。Function Object 也都是經過 New Function()建立的。html5
f1,f2,歸根到底都是經過 new Function()的方式進行建立的。java
這麼來理解這句話呢?css3
function fns(a, b) { return a + b } // 等價於 var fns = new Function('a, b', 'return a + b')
上面的代碼作了一個同目標的不一樣實現實現方法,那麼爲何兩種方案的結果都是相同的呢?git
講到這裏就須要對 JavaScript 這門語言進行 分析了。github
JavaScript 是一門解釋型的語言
數組
什麼是 解釋型 語言? 在客戶端的瀏覽器中存在一個 能夠解釋 JS 的`引擎`。 這裏JavaScript的引擎 就是 谷歌的 V8 引擎 和 其餘瀏覽器引擎。 而咱們經常據說的 ES5 ES6 什麼的,每每指的是當前 JS 語言的版本。或者說當前的 JS 語言的標準。 而後所謂的瀏覽器兼容這些新的特性其實就是瀏覽器的 JS 引擎的升級,去適配這些新版本的 JS 的新特性。 不兼容,每每就是 引擎 不支持這個新特性。 (因此,咱們會發現寫一個 瀏覽器 應用 仍是很難的。 由於你須要去兼容這麼的東西,最新版本的JS,css3 最新的特性 , html5 的新標籤,等等) 那麼咱們回到 解釋型語言 上來,有了能解釋 JS 語句的引擎了,那麼上面就必定會有必定的規則了,否則的話,若是你亂寫都能被 引擎 讀懂的話,要這 引擎 何用。 好,上段 中介紹到了 規則的問題, 那麼 JS 語言自己確定也是隱藏了一些 咱們在 ES 系列上看不到的 規則。 那是什麼呢? 咱們一塊兒來看下, 這就是本小節將要介紹的函數對象(Function Object)。 函數對象與其它用戶所定義的對象有着本質的區別,這一類對象被稱之爲內部對象,例如日期對象(Date)、數組對象(Array)、字符串對象(String)都是屬於內部對象。換句話說,這些內置對象的構造器是由JavaScript自己所定義的:經過執行new Array()這樣的語句返回一個對象,JavaScript 內部有一套機制來初始化返回的對象,而不是由用戶來指定對象的構造方式。
這些內置對象的構造器是由JavaScript自己所定義的
瀏覽器
因此: new Fucntion('arg', console.log(arg)) new Array() new Date() new String() 都會返回對應的 對象。 因此,當咱們在用 字面量 去建立一個 函數的時候,JS 解釋器就會 用這些 內置的對象構造器 Function 去 實例化 並返回一個 函數對象。 那麼咱們能夠想象一下,是否是 咱們本身在寫函數的時候直接 new Function 的方法來寫 會不會執行效率更高。 一樣 問題就來了, var fn = new Function('a', 'return a') 這樣寫的話,參數還好,可是 函數體 若是很長 不少的話就很難受了,因此~ 面前咱們所用的建立 函數對象的方法 即爲 最方便的方法。
什麼是原型對象?
在 JavaScript 中,每當定義一個對象的時候,對象中都會包含一些預約義的屬性
。 其中 函數對象
的一個屬性就是 prototype
。 (上文介紹到的 普通對象沒有 prototype,可是有 __proto__
) (可是 函數對象也有 proto 這裏須要注重理解下,否則容易出錯。)
因此 通過上面的解釋 是否是就清楚了,原型對象
也是一種 普通對象。可是隻有 函數對象
擁有。
可是 有且僅有一個特殊的 案例 須要注意下。
eg:
function f1(){} console.log(f1.prototype) // {...} console.log( typeof f1.prototype) // object console.log(typeof Function.prototype) // Function console.log(typeof Object.prototype) // Object console.log(typeof Function.prototype.prototype) //undefined
原型對象其實就是普通對象(Function.prototype除外,它是函數對象,但它很特殊,他沒有prototype屬性(前面說道函數對象都有prototype屬性))
爲何?
從這句console.log(f1. prototype) //f1 {} 的輸出就結果能夠看出,f1.prototype 就是 f1 的一個實例對象(這裏結合上面講到了 實例話 函數對象的過程)。就是在f1 函數 在建立的時候,建立了一個它的實例對象 (var temp = new Function('','') ) 並賦值給它的prototype (f1.prototype = temp)
console.log(typeof Function.prototype.prototype) //undefined
惟一一個特殊的 函數對象沒有 prototype 屬性的。 爲何? 就是根據上面在 控制檯打印出來的結果。它是個特例,須要特殊記憶!
原型對象有什麼做用?
主要是用來繼承
eg:
var Person = function(name) { this.name = name this.getName = function() { return this.name } } Person.prototype.changeName = function(name) { this.name = name } Person.prototype.getFirstName = function(name) { return this.name } var zhang = new Person('zhang') var res0 = zhang.getName() var res1 = zhang.getFirstName() console.log(res0) // zhang console.log(res1,'xxx') // zhang xxx zhang.changeName('ge') var res2 = zhang.getName() console.log(res2) // ge
經過這個例子咱們能夠看出來, 咱們經過給 構造函數
的 prototype
屬性添加 方法(getName)。
那麼它 全部實例化出來的 函數對象都會帶有這個方法(getName),一樣添加屬性也是同樣。
那麼爲何 可以 實現繼承呢? 下面咱們就講到了 原型鏈
。
JS 在建立對象(不論普通對象仍是函數對象)的時候,都有一個叫作__proto__
對內置屬性,用於指向建立它對函數對象的原型對象 prototype
var Person = function(name) { this.name = name this.getName = function() { return this.name } } var zhang = new Person('zhang') zhang.__proto__ === zhang.prototype // true
一樣,zhang.prototype
對象也有 __proto__
屬性,它指向建立它的函數對象(Object)的prototype
zhang.prototype.__proto__ === Object.prototype // true
繼續,Object.prototype
對象也有 __proto__
屬性,但它比較特殊,爲null
console.log(Object.prototype.__proto__) //null
咱們把這個有__proto__
串起來的直到Object.prototype.__proto__
爲null
的鏈
叫作原型鏈
按照咱們上面說的例子來展現下這個原型鏈
var Person = function(name) { this.name = name this.getName = function() { return this.name } } var obj = new Person('zhang') // 其中 obj.__proto__ 指向了 Person.prototype(即爲 Person 的實例) // Person.prototype 的 __proto__ 指向了 Object.prototype // Object.prototype 的 __proto__ 指向了 null
經過 __proto__ 串起來的直到Object.prototype.__proto__爲null的鏈叫作原型鏈
原型對象prototype
中都有個預約義的constructor
屬性,用來引用它的函數對象。這是一種循環引用
一、Person.prototype.constructor
ƒ (name) { this.name = name this.getName = function() { return this.name } }
二、Function.prototype.constructor
ƒ Function() { [native code] }
三、Object.prototype.constructor
ƒ Object() { [native code] }
person.prototype. constructor === person // true
Function.prototype.constructor === Function // true
Object.prototype.constructor === Object // true
最後打個比喻,雖然不是很確切,但可能對原型
的理解有些幫助
父親(函數對象),先生了一個大兒子( prototype
),也就是你大哥,父親給你大哥買了好多的玩具,當你出生的時候,大家之間的親情紐帶(__proto__
)會讓你天然而然的擁有了你大哥的玩具。
一樣,你也先生個大兒子,又給他買了好多的玩具,當你再生兒子的時候,你的小兒子會天然擁有你大兒子的全部玩具。至於他們會不會打架,這不是咱們的事了。
因此說,你是從你大哥那繼承的,印證了那句「長兄如父」啊!
可以對上圖有所理解的話,原型 、原型鏈 等等都有一個很好的理解了,
固然也須要有大量的 OOP 相關的開發,才能對 JS 的 OOP 有一個 深入的理解。