當查找一個對象的屬性時,JS會向上遍歷原型鏈,直到找到給定的屬性名稱爲止,若是沒找到就是undefined。
前端
大多數的JS的實現用_proto_屬性來表示一個對象的原型鏈,如下代碼展現了JS引擎如何查找屬性。
函數
function getProperty(obj, prop) { if (obj.hasOwnProperty(prop)) return obj[prop] else if (obj.__proto__ !== null) return getProperty(obj.__proto__, prop) else return undefined}
讓咱們舉一個比較簡單的例子,假設有個三維點,座標x、y、z,同時有print打印方法。測試
如今咱們建立一個對象point,具備x、y、z和print屬性,爲了能建立一個新的三維座標點,咱們須要建立一個新的對象,使得它的_proto_指向point,繼承point。相似C++中的OOP,例如point爲基類,建立新的point對象爲原先point的子類。this
var Point = { x: 0, y: 0, z: 0, print: function () { console.log(this.x, this.y, this.z); }}; var p = {x: 10, y: 20, z: 30,__proto__: Point};p.print(); // 10 20 30
可是js工程師通常都不會這樣寫原型繼承,他們以下寫出:spa
function Point(x, y) { this.x = x; this.y = y; this.z = z;} Point.prototype = { print: function () { console.log(this.x, this.y, this.z); }}; var p = new Point(10, 20, 30); p.print(); // 10 20 30
這裏涉及到了new運算符的工做原理prototype
new後面跟的不是類,而是構造函數 ,用new構造函數生成實例對象,有一個很明顯的缺點,就是每一個實例沒法共享同一個屬性和方法。在C++中,若是類中定義了一個static成員,那麼全部該類的實例都共享該成員。而JS中每個實例的對象都有本身屬性的副本,這樣比較浪費空間,並且沒法實現數據的共享。code
基於以上new構造函數的缺陷,JS創始人爲構造函數添加了prototype屬性對象
JS規定每個構造函數都有prototype屬性,該屬性指向另外一個對象,另外一個對象中全部的屬性和方法都會被構造函數的實例引用。繼承
這個屬性包含一個對象,全部須要共享的屬性和方法放入這個對象中,而不須要共享的屬性和方法放入構造函數中。實例對象一旦建立成功,就會自動引用prototype對象中的方法和屬性,即實例對象的屬性和方法分爲兩種,一種是本地的,即放入構造函數中的屬性和方法,一種是引用的,即放入prototype對象。例如如下代碼:
ci
function DOG(name){ this.name = name; } DOG.prototype = { species : '犬科' }; var dogA = new DOG('大毛'); var dogB = new DOG('二毛'); alert(dogA.species); // 犬科 alert(dogB.species); // 犬科
在這個例子中,species屬性放入prototype對象中,那麼實例dogA和dogB共用species屬性。只要其中一個實例的species發生改變,則會影響全部實例的species
這個方法用來判斷prototype對象和某個實例之間的關係,例如
alert(DOG.prototype.isPrototypeOf(dogA)); //true
每個實例對象都有一個該方法,用來判斷該實例中的某個屬性是來自本地屬性,仍是繼承自原型對象屬性。例如
alert(dogA.hasOwnProperty(name))//true
in運算符能夠判斷某個屬性是否屬於實例對象,無論是本地屬性仍是繼承自原型對象屬性。如
alert("name" in dogA); //true alert("species" in dogA); //true
in運算符還能夠遍歷某個對象的全部屬性,如
for (var prop in dogA) { alert(prop); }
有一道阿里的在線前端筆試題,題目以下:
如今有以下的代碼:
var foo = 1; function main(){ console.log(foo); var foo = 2; console.log(this.foo) this.foo = 3;}
1.請給出如下兩種方式調用函數時,輸出的結果,並說明緣由
var m1 = main(); var m2 = new main();
2.若是須要var m1= main()產生的結果與前面m2產生結果同樣,應該如何改造main函數
第一題解答:首先根據JS的變量提高規則,能夠知道,全局的foo被main函數屏蔽了,main函數在內部定義了一個foo同名的變量,該變量在第一個console以前只定義而未賦值,故爲undefined(undefined有兩種狀況會出現,已定義未賦值,未定義)。而在第二個console的時候,this指向的是window,故輸出爲1