原型是 JavaScript 巧妙的設計,它很是容易理解。都 2020 年了,看完這篇但願你之後不須要再重複學習 JavaScript 原型了。若有不當之處,懇請指點一二!函數
下面是相關單詞及其翻譯,緊緊記住它們就成功一半了。學習
function Drink() {} const a = new Drink() console.log(a.constructor) // ƒ Drink() {}
a 是由函數 Drink 構造而來的。測試
簡單寫點代碼,後面全是 console.log
。ui
function f() { this.a = 1 this.b = 2 } const o = new f() f.prototype.b = 3 f.prototype.c = 4
o 是 new f()
返回的結果,不妨回顧一下當執行 new f()
時, new 操做符對函數 f 作了些什麼事。this
var obj = Object.create(null)
。obj.__proto__ = f.prototype
。new Foo
等同於 new Foo()
,也就是 f 不帶任何參數調用的狀況; 將步驟 1 建立的對象做爲 this 的上下文(將 this 綁定到新建立的對象 | f 函數中的 this 的指針替換成 obj) ,f.call(obj)
。對於一個函數,若是不使用
new
操做它,它只是一個正常的函數;使用new
操做符僅僅改變了它的 this 指向且在函數內部隱式地建立了一個對象,而後再稱之爲 「構造函數」。僅此而已。prototype
若是你對第三步中的操做有困惑,看幾個簡單的例子:翻譯
function f() { this.a = 1 this.b = 2 } f() console.log(f.constructor) //ƒ Function() { [native code] }
function f() { this.a = 1 this.b = 2 } const o = new f() console.log(o.constructor) // ƒ f() { // this.a = 1 // this.b = 2 // }
function f() { console.log(this) // window this.a = 1 this.b = 2 } f()
function f() { console.log(this) // f {} this.a = 1 this.b = 2 console.log(this) // f {a: 1, b: 2} } new f()
const drink = { name: 'Coca Cola', color: 'black', price: '3.5', intro: function () { console.log(`名稱:${this.name},顏色:${this.color},價格:${this.price}`) }, } const computer = { name: 'Orange Juice', color: 'orange', price: '4', } drink.intro.call(computer) //名稱:Orange Juice,顏色:orange,價格:4
確保上面的內容你能十分清晰,不然不要進行下面的內容。設計
console.log(o.b) // 2
o 的值是經過 new f()
獲得的對象,this 指向這個對象,函數中給 this 添加了屬性 b 爲其賦值爲 2,並將他返回。因此 這裏打印出 2。f.prototype
是沒法被訪問到的,這種狀況還被稱之爲 property shadowing ---屬性遮蔽。指針
console.log(o.c) // 4 console.log(o.__proto__.c) // 4 console.log(o.__proto__ === f.prototype) // true
函數中並無給 this 添加 c 屬性併爲其賦值 4,可是打印 o.c 返回 4。經過上文你已經知道 constructor 是幹什麼的了:code
console.log(o.constructor.prototype.b) // 3
o 是由函數 f 構造的,o.constructor
返回函數 f,因此o.constructor.prototype === f.prototype
, f.prototype
返回什麼呢?上面初始代碼中直接寫好的,如今能夠翻上去看看 f.prototype
,因此 o.constructor.prototype.b
返回 3。查找對象上的屬性就是先找自身再經過 __proto__
一層一層往上找的:
prototype
上也有,屬性遮蔽不會忘了吧;__proto__
找但沒找到,會返回 undefined
;爲何呢?console.log({}.constructor) // ƒ Object() { [native code] } console.log({}.__proto__ === Object.prototype) // true console.log(Object.prototype.__proto__) // null
看到這裏,應該十分清晰了。這就是最終爲何會返回 undefind 的緣由:Object.prototype.__proto__
指向 null。
作一個簡單又不給你解釋的小練習吧!
console.log(o.b) console.log(o.__proto__.b) console.log(o.d)
// 2 // 3 // undefined
對於 Object.prototype.__proto__
:
若是看完仍是不太明白,動手試一試吧!我把本文用到的代碼片斷放到此處供你快速拷貝。
function f() { this.a = 1 this.b = 2 } const o = new f() f.prototype.b = 3 f.prototype.c = 4 console.log(o.b) // 2 console.log(o.c) // 4 console.log(o.__proto__.c) // 4 console.log(o.__proto__ === f.prototype) // true console.log(o.constructor.prototype.b) // 3 console.log(o.b) // 2 console.log(o.__proto__.b) // 3 console.log(o.d) // undefined console.log({}.constructor) // ƒ Object() { [native code] } console.log({}.__proto__ === Object.prototype) // true console.log(Object.prototype.__proto__) // null // --------- other -------- console.log(o.__proto__) // {b: 3, c: 4, constructor: ƒ} console.log(o.__proto__.__proto__ === Object.prototype) // true console.log(Object.prototype.__proto__) // null console.log(o.__proto__.__proto__.__proto__) // null console.log(f.prototype.__proto__ === Object.prototype) // true