這個系列的教程我一開始是寫在github上的,
可是以爲放到掘金來可讓更多須要的人看到,
就搬到掘金專欄上啦,
若是以爲本教程對你有幫助,請點這裏去github上給我一顆Star~
教程目錄也在github上哈~javascript
下面開始第三篇:java
啥是原型,啥是原型鏈?
原型:一個屬性,屬性名叫prototype,只有構造函數有,好比Foo.prototype;
原型鏈:一個屬性,屬性名叫__proto__,萬物皆有,鏈狀相連,最後歸宗到Object.prototype上,Object.prototype__proto__值爲null;
git
訪問一個實例的屬性,JS會先在實例內部尋找,找不到的話,沿着原型鏈繼續找下去。github
function Foo(){this.name='我是構造函數Foo'} //一個構造函數Foo
var foo = new Foo(); //經過new Foo(),獲得實例化的foo
console.log(foo.bar); //訪問foo的bar屬性,控制檯打印一下,獲得undefined
Foo.prototype.bar = '我是bar屬性,在Foo.prototype(Foo的原型)裏'; //給Foo.prototype添加bar屬性
console.log(foo.bar); //再次訪問foo的bar屬性,控制檯打印一下看看。結果驗證了"沿着原型鏈找下去"這句話。
Object.prototype.examAttr = '我是examAttr屬性,在Object.prototype(Object的原型)裏'; //給Object.prototype添加examAttr屬性
console.log(foo.examAttr); //訪問foo的examAttr屬性,控制檯打印一下看看。結果驗證了"一直找到Object.prototype"這句話。複製代碼
配合以前寫的紙面意思,回味一下上面的代碼。app
每一個構造函數都有一個原型,這個原型的constructor屬性就是這個構造函數。函數
function Foo(){this.name='我是構造函數Foo'} //一個構造函數Foo
console.log(Foo.prototype); //打印結果能夠看到一個Object對象,即Foo的原型,裏面有一個constructor屬性,屬性值即爲Foo函數。
var foo = new Foo(); //實例化
console.log(foo.constructor); //foo中沒有constructor屬性,沿着原型鏈找到Foo的原型(即上面打印的結果),獲得Foo原型的constructor屬性值,即Foo函數。複製代碼
以上代碼解釋了爲何經過查看實例的constructor屬性能夠獲得實例的構造函數。重點在於「沿着原型鏈找」。
學習
//寫一個構造函數,定義其prototype
function Foo(name) {
this.name = name;
}
Foo.prototype = {
constructor:Foo, //因爲從新定義了prototype,我們把constructor屬性補上。
say: function () {
console.log('My name is ' + this.name);
}
};
//JS實現new的方法generate
function generate(Fun,arguments) {
var foo = {}; //新建一個空對象
Fun.apply(foo, arguments); //利用apply改變this指向,如今運行Fun時,內部this指向foo空對象,那麼給this.name賦值就變成了給foo.name賦值。
foo.__proto__ = Fun.prototype; //把foo的__proto__屬性指向Fun.prototype。
return foo;
}
//執行generate,模擬new
var foo = generate(Foo,["Terry"]); //至關於 var foo = new Foo("Terry")
//驗證明例方法
foo.say();
//查看foo的構造函數
console.log(foo.constructor);複製代碼
下面這張圖想必不少人都很熟悉,我截取了小上半部分,
不要考慮看不見的導線,只關心f一、Foo、Foo.prototype三者的關係就夠了。
配合下面這張圖,再回頭看一下JS實現new的過程:ui
圖中表示,f1由new Foo而來,而f1的__proto__鏈接着Foo.prototype,
這說明Foo的實例f1的__proto__(原型鏈)是指向Foo.prototype(原型)的,
你再回頭去看咱們用JS實現new所封裝的方法generate,有這麼一句:
foo.__proto__ = Fun.prototype;
那不就是手動把foo(圖中的f1)的原型鏈指向Fun(圖中的Foo)的原型嗎!複製代碼
在封裝代碼/插件的時候,使用面向對象的混合模式來寫,代碼結構是這樣的:複製代碼
function MyPlugin(name){
this.name = name; //每一個實例都不同的屬性,寫在構造函數裏。
}
MyPlugin.prototype = {
publicFun:function(){
console.log('我是公共方法,全部實例共用'); //每一個實例都調用同樣邏輯的代碼,封裝成方法寫進構造函數的原型裏
console.log(this.name+'使用了插件');
}
}
//使用插件
var myPlugin = new MyPlugin('小明');
myPlugin.publicFun(); //實例並無publicFun方法,可是JS從myPlugun.__proto__中找到了public。複製代碼
經過找「點」大法(前面教程有說過),能夠發現publicFun內部的this指向myPlugin,
也就驗證了爲何一、理解this指向的小技巧中小結裏的第三點:
「明確區分函數是[構造函數]仍是[普通函數],[構造函數]內的this指向實例化後的對象;」this
PS:
歡迎轉載,須要註明原址。
教程之間緊密聯繫,不懂的地方,請好好看下全系列教程目錄,
有沒有你不懂的那個關鍵字在裏面。
若是幫到你,別忘了給我一顆Star~
es5