多是以前開發和思考的層次比較淺,我的感受多數的開發工做與原型和原型鏈的關係不大,甚至不知道的狀況下也可以把任務完成。網上也有很多講解原型的文章,更有很多面經中提到這個考題,若是不是爲了炫技那必是開發中必需要掌握的一個知識點。最近也看了這方面的資料,就上面的三個問題總結一下本身的想法,其實學以至用纔是最棒的,目前開發經驗少不能體會原型的好處,只作記錄方便以後遇到問題可以知道該去查找哪方面的資料。 javascript
幾乎全部函數都有一個顯示的 prototype
屬性,除了下面的這種狀況java
let fun = Function.prototype.bind()
複製代碼
prototype
屬性是一個對象,在函數聲明的時候就會自動建立,並且默認狀況下只有一個屬性 constructor
用來指向函數自己。
git
每一個對象都會有的一個隱式原型屬性,指向了建立該對象的構造函數的原型。
es6
__proto__
是在對象建立的時候添加屬性,連接到構造調用的函數的原型上的。當訪問對象上不存在的屬性的時候能夠順着這條鏈查找屬性。
new
一個對象的過程能夠解析爲一下4步:
obj
obj
的 __proto__
連接到函數的 prototype
obj
// new 語法的簡單模擬
var creatObject = function(constructor,...args) {
var obj = Object.create(null)
// 連接原型
Object.setPrototypeOf(obj,constructor.prototype)
res = constructor.apply(obj,args)
return typeof res === 'object' ? res : obj
}
複製代碼
在JavaScript中沒有類這個概念,所謂的繼承也是經過原型鏈來實現的,包括加入的 class
語法糖也是爲了更好的理解JavaScript中的「繼承」並無改變JavaScript中對象的工做機制,底層仍是原型。那麼很天然的想到原型鏈中也有相似繼承的好處。
個人理解上原型有兩個主要的做用:github
在對象上查找屬性的時候,若對象自己不具有該屬性,則被查找的屬性會被委託在整個原型鏈上,只有當沒有更多的原型能夠查找的時候,才中止查找。
咱們定義一個對象 var o = {a:1,b:2}
咱們知道可使用 hasOwnProperty
來判斷某屬性是否對象自身擁有的屬性,可是在建立對象的時候並無建立 hasOwnProperty
方法,這個是如何經過 .
運算調用的呢?
app
o.__proto__===Object.prototype
能夠知道經過字面量的方式建立對象實例
o
的時候的函數是
Object
且如今
o
的原型鏈上有
Object.prototype
Object.prototype
發現這個對象中含有屬性
hasOwnProperty
o
中沒有找到
hasOwnProperty
,就順着原型鏈往上找,首先找
o.__proto__
也就是
Object.prototype
,很巧在這裏就找到了(若是沒有找到就會一直順着這條鏈往上找),因而拿過來使用。
前面略帶的提過這個屬性 constructor
,經過這個屬性能夠訪問建立該對象時所用的函數,經過這個咱們能夠判斷兩個實例是不是同一個函數建立而來的,我想這個屬性的做用差很少也應該是這樣了。
函數
obj instanceof Foo
應該被理解爲檢測函數 Foo
是否出如今 obj
的原型鏈中,並非單純的理解過對象實例是否由某個函數構造調用建立。只要經過 __proto__
一層一層的往上找可以找到這個函數的 prototype
就說明是這個函數建立的。
ui
dog.__proto__!==Dog.prototype
並且順着
__proto__
也找不到
Dog.prototype
因此這裏返回
false
當實例屬性和原型屬性中有同名屬性或者方法的時候優先使用實例屬性。下面實例屬性中和原型屬性中都有 say
函數,這裏使用實例屬性。順着原型鏈查找如今實例屬性中找到了就中止查找
this
對象與函數原型之間的引用關係是在對象建立的時候創建的,新建立的對象會根據當前的函數原型創建引用。
es5
上面例子中用一個對象字面量重寫了 Dog.prototype
此時 constructor
屬性不見了,這個在實習繼承效果的時候也須要注意,咱們在修改完原型以後須要補上 constructor
// es5
function Animal() {}
Animal.prototype.say = function() {
console.log('動物叫')
}
function Dog() {}
let dog = new Dog()
// 連接原型並加上constructor
Dog.prototype = Object.create(Animal.prototype, {
constructor: {
value: Dog, // 指向Dog
enumerable: false,
writable: true,
configurable: true
}
})
//es6 引入了class 和 extends 減小對原型覆蓋的反作用
class Animal{
constructor(name) {
this.name = name
}
say(){
console.log('動物叫')
}
}
class Dog extends Animal{
constructor(name,type){
super(name) // 調用父類構造函數
this.type = type
}
say(){
console.log('汪汪')
}
}
var dog = new Dog('道格','中華田園犬')
dog.say() // 汪汪
console.log(dog instanceof Dog) // true
console.log(dog instanceof Animal) // true
複製代碼
一些公共方法若是做爲實例方法建立在每建立一個實例的時候都會產生一個副本,不只佔內存並且修改起來麻煩。能夠在函數的原型對象上建立對象方法,這樣可使得一個方法被全部對象實例共享(能夠經過原型鏈訪問)。
// 翻轉字符串爲例
String.prototype.reversed = function(){
return Array.from(this).reverse().join('')
}
"abcdef".reversed() // "fedcba"
複製代碼