寫在前面的總結:
JS當中創建一個對象有好幾種方式,大體上就是以下幾種:
①通過var obj ={...}
這種方式一般稱為字面量方式,{}直接寫需要定義的字段
②var obj = new Object()
Object對象是JS的內建對象
③通過構造函數創建對象
例如:
function father(){ this.name = "I'm your father!" } var f = new father();
這裡通過構造函數的方式創建了一個對象實例f
④通過Object.create創建對象
Object.create(proto [, propertiesObject ]) 是E5中提出的一種新的對象創建方式,第一個參數是要繼承的原型,如果不是一個子函數,可以傳一個null,第二個參數是對象的屬性描述符,這個參數是可選的。
1.原型是什麼?
首先應該知道我們創建的每一個函數都有一個prototype屬性,這個屬性是一個指向原型對象的指針,這個原型對象的用途是包含由特定類型所有實例共享的屬性和方法。
也就是說,每創建一個函數,就會創建一個對應的原型對象。
一個對象的真正原型是被對象內部的[[Prototype]]屬性(property)所持有。ECMA引入了標准對象原型訪問器Object.getPrototype(object),到目前為止只有Firefox和chrome實現了此訪問器。除了IE,其他的浏覽器支持非標准的訪問器__proto__,如果這兩者都不起作用的,我們需要從對象的構造函數中找到的它原型屬性。
還是挺抽象,舉個例子吧:
function Person(){ this.name="大橙子"; this.age = 26; this.sex = "純爺們"; } var p = new Person(); console.log(p.name); console.log(p.age); console.log(p.sex); console.log(p.career); Person.prototype.career = "程序員"; console.log(p.career); 輸出結果: [Web浏覽器] "大橙子" [Web浏覽器] "26" [Web浏覽器] "純爺們" [Web浏覽器] "undefined" [Web浏覽器] "程序員"
上面Person.prototype.career = "程序員";就是通過原型來添加的屬性,所有繼承這個Person的對象,都具有career這個屬性。
這個過程是這樣的,當輸出p.career的時候,首先會在對象p找,因為p對象沒有career這個屬性,這就是為什麼會輸出undefined的原因,然後繼續向上查找,在Person.prototype中找到了career,然後就輸出了“程序員”。
要了解原型的概念,應該知道_proto_、prototype以及constructor他們之間是什麼聯系
那麼就來分析一下var p = new Person(); 這個過程
上面這個分析圖可以很好的展示,三者的關系,
首先要知道幾個地方,
第一點:就是Empty()函數,這個函數是所有函數的原型,並且他很特殊沒有prototype
第二點:__proto__是內部原型,prototype是構造器原型(構造器其實就是函數,所以上面才說這個是函數對象才有的,而_proto_是每個對象都有的。我的理解它才是構成原型鏈的原因)
①.從中間開始看,首先我創建了一個函數person(),同時JS幫我創建了一個原型對象person.prototype
並把我的person()的prototype指針指向了它,原型對象中有一個constructor屬性,指向了我的person()函數。
②.當我new一個person實例的時候,新創建的實例對象p的_proto_指向了person.prototype
這也是為什麼,我在person.prototype中添加屬性,p也能反映出來的根本原因。
③.person.prototype也是一個對象,那麼他的_proto_是誰呢,如上圖就是Object.prototype,這也不難理解,因為Object是Javascript中所有對象的父級對象,我們創建的所有對象都繼承於此,包括內建對象。同樣因為它也是對象,那麼它也擁有_proto_,圖上表示了它的原型就是null
我們可以打印每一屬性的值來進行確認:
//Person函數的原型對象 console.log(Person.prototype); //Person原型對象的Constructor console.log(Person.prototype.constructor); //判斷實例對象p的原型,是不是Person.prototype console.log(p.__proto__ === Person.prototype?true:false); //函數對象Person的_proto_,是不是Empty() console.log(Person.__proto__); //Empty()是不是沒有prototype console.log(Person.__proto__.prototype); //Empty()的__proto__是不是Object.prototype console.log(Person.__proto__.__proto__ === Object.prototype?true:false); //Person.prototype的_proto_是不是Object.prototype console.log(Person.prototype.__proto__ === Object.prototype?true:false); //Object.prototype的_proto_是不是null console.log(Object.prototype.__proto__); 輸出結果: [Web浏覽器] "[object Object]" [Web浏覽器] "function Person(){ this.name="大橙子"; this.age = 26; this.sex = "純爺們"; }" [Web浏覽器] "true" [Web浏覽器] "function Empty() {}" [Web浏覽器] "undefined" [Web浏覽器] "true" [Web浏覽器] "true" [Web浏覽器] "null"
2.原型鏈
上面的原型我們舉的例子沒有特別明顯的顯示這個鏈的過程,看不到具體的繼承關系,所以再分析一個例子
Object.prototype.dead = true; function animal(){} animal.prototype.eat=true; animal.prototype.sleep=true; function bird(){ this.fly = true; } bird.prototype = new animal(); var a = new animal(); var b = new bird(); console.log(a.fly); console.log(a.eat); console.log(a.sleep); console.log(a.dead); console.log(b.fly); console.log(b.eat); console.log(b.sleep); console.log(b.dead); 測試輸出: [Web浏覽器] "undefined" [Web浏覽器] "true" [Web浏覽器] "true" [Web浏覽器] "true" [Web浏覽器] "true" [Web浏覽器] "true" [Web浏覽器] "true" [Web浏覽器] "true"
下面我再畫圖分析一下這個
①下圖是在bird.prototype = new animal();之前,創建好函數之後的狀態
②繼續執行程序
從上圖就可以明顯看到
實例對象b->bird.prototype->animal.prototype->Object.prototype->null有一個鏈式的繼承關系
也就是為什麼
console.log(b.eat);
console.log(b.sleep);
console.log(b.dead);
會打印出來true的原因了。