今天我們先來 聊聊 jquery 中的無 new 構造
寫過 js 物件導向的同學知道,一般我們是這麼來寫的
//建構函式
function
myjquery
() //方法掛載到原型上
myjquery.prototype.say = function
() var myjquery = new myjquery(); // 例項化
console.log( myjquery.age ); // 20
console.log( myjquery.say() ); // my age is 20
這是一種比較常見的寫法,然而 jquery 並不是這麼做的,回想一下,我們平時在用 jquery 獲取元素的時候,經常都是$('#myid') , $('.myclass')
這種方式去呼叫的,難道說 jquery 不用 new 去建立乙個例項?
其實不然,jquery 內部也是用 new 建立乙個 jquery 物件 , 只不過用的十分巧妙, 接下來我們來看看jquery是怎麼做到的!
在 jquery 原始碼中有這麼一段
jquery = function
( selector, context )
這個就是 jquery 的入口方法 , 它返回的 是乙個new jquery.fn.init( selector, context );
我們知道, 用 new 乙個物件 ,肯定是返回乙個物件的例項,那麼也就是返回的是jquery.fn.init( selector, context )
的例項,那麼這個jquery.fn
又是什麼? 我們檢視原始碼中有這麼一段:
jquery.fn = jquery.prototype =
也就是說,jquery.fn
就是jquery
建構函式的原型。
接著我們看看jquery.fn.init()
這個方法,原始碼中可以找到是這麼寫的:
init = jquery.fn.init = function( selector, context, root )
有的同學可能認為到這裡就結束了,是嗎?ok,我們來測試一下,我們把上面我們的**改造跟 jquery 一樣結構,
var myjquery = function
() myjquery.fn = myjquery.prototype= ,
age : 20,
say : function
() }
myjquery().age; //'undefiend'
myjquery().say();
// 'uncaught typeerror: myjquery(...).say is not a function'
什麼?出錯了,沒錯,得到的結果確實 令人意外 , 不過也是意料之中 , 我們稍加分析一下就知道了。
上面**我們使用了return new myjquery.fn.init();
返回的是 myjquery 原型上的 init() 方法的例項 , 我們在發現在 init() 方法中返回了this ; 此時的 this 指向的是myjquery.fn.init的例項,
呼叫myjquery() 得到的結果是myjquery.fn.init 的例項;
而在myjquery.prototype.init內部中根本就沒有 age 屬性 和 say 方法, 所以此時通過這個結果訪問不到 myjquery.prototype 的屬性 和方法的 , 因此上面得到的結果也就 不意外了。
可是我們用 jquery 為什麼沒有出現這樣的情況呢?原來我們還少了一句關鍵的**, jquery 中是這麼寫的:
init.prototype = jquery.fn
;
我們在上面就看到了init = jquery.fn.init
,所以說這句**相當於
jquery.fn
.init
.prototype = jquery.fn
;
這句話是關鍵,現在我們可以發現:
jquery.fn
.init
.prototype = jquery.fn = jquery.prototype
;
通過原型傳遞來解決上面的問題,把jquery的原型傳遞給
jquery.prototype.init.prototype
也就是說 jquery 的原型物件 覆蓋了內部 init 構造器的原型物件。
這樣new init 的出來的例項物件也就等價於new jquery的例項物件,所以也就可以訪問jquery的原型方法了。
我們把這句話加上去,把上面的函式改寫成:
var myjquery = function
() myjquery.fn = myjquery.prototype= ,
age : 20,
say : function
() }
myjquery.prototype.init.prototype = myjquery.prototype;
myjquery().age; //20
myjquery().say() //20
世界清靜多了,不是麼?
ok,這就是 jquery 實現無 new 操作的 原理 ~
上面還有一點點東西,我們補充一下:
1. 我們看到 jquery 物件的是通過原型中的 init 方法 return 回來的,如下:
return
new jquery.fn.init( selector, context );
也就是說,我們每次呼叫 jquery 的時候去 new 乙個新的物件 , 因此,我們在寫**的時候,如果頻繁使用乙個 jquery 物件的話,我們不妨把它用乙個變數存起來,可以避免每次去建立物件~
2. 上面 jquery 原型上,我單獨拿出來一句:
constructor: jquery
jquery 這麼做是為了保證 constructor 的指向,防止建構函式指向錯誤,引起呼叫的問題。那麼為什麼會產生這樣的問題呢?
//方式一
fn.prototype.say = function
(){}
//方式二
fn.prototype =
}
jquery 採用的是 第二種 方式,這種方式與第一種方式差別很大,第一種是給原型新增乙個方法,這種方式是不會影響fn原型內部 constructor 的指向問題,而第二種方式是重寫原型, 相當於原型覆蓋,原來的原型已不復存在,所以需要重新定義 constructor 的指向,小夥伴們以後寫**的時候多注意一點~ OkHttp原始碼初探
在之前的文章我中我們介紹了okhttp的基本使用方法並簡單說明了原始碼下各個module的功能作用,從這篇開始我們將要開始分析okhttp的原始碼。首先,我們先來回憶一下okhttp的使用過程 1.建立乙個okhttpclient物件 2.建立乙個request物件 3.呼叫okhttpclient...
RequireJS原始碼初探
前兩天跟著葉小釵的部落格,看了下requirejs的原始碼,大體了解了其中的執行過程。不過在何時進行依賴項的載入,以及具體的 在何處執行,還沒有搞透徹,奈何能力不夠,只能先記錄一下了。看原始碼從頭開始看,肯定是不切實際的。按照葉小釵的方法,是從data main開始的,所以我們也從那裡開始把!首先,...
RequireJS原始碼初探
前兩天跟著葉小釵的部落格,看了下requirejs的原始碼,大體了解了其中的執行過程。不過在何時進行依賴項的載入,以及具體的 在何處執行,還沒有搞透徹,奈何能力不夠,只能先記錄一下了。看原始碼從頭開始看,肯定是不切實際的。按照葉小釵的方法,是從data main開始的,所以我們也從那裡開始把!首先,...