国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 網站 > WEB開發 > 正文

jQuery源碼中的“newjQuery.fn.init()”什么意思?

2024-04-27 15:01:04
字體:
來源:轉載
供稿:網友

所有文章搬運自我的個人主頁:sheilasun.me

引子

最近打算試試看看jQuery的源碼,剛開個頭就卡住了。無論如何都理解不了jQuery源碼入口部分中的

return new jQuery.fn.init( selector, context )

看了好多帖子都沒看懂,覺得自己很蠢,心里很苦,吃宵夜都不香了。昨晚去游泳,游完8*100后靠在池壁上喘氣,有人從我旁邊出發,水花濺起的瞬間,我突然,想通了!這大概就是回光返照 (劃掉)福至心靈吧!
下面一點點地說下我對jQuery入口源碼的理解。

自執行的匿名函數

jQuery源碼最外層的結構如下:

(function(window,undefined){    ...})(window);

任何庫的引入都得做到不污染全局變量,得有自己的命名空間。上面的自執行匿名函數就可以做到這點,把所有庫私有的變量和方法,都包到一個私有的空間內,允許外界訪問的屬性或方法可以掛載到window上。

例如下面這段代碼:

(function(){  var count=0;  var addOne=function(){    alert(count++);  };  window.outerAddOne=addOne; //掛到window上外界方可訪問})();outerAddOne();//alert "0"console.log(count);//errorconsole.log(addOne);//error

內部定義的count變量以及addOne方法,外部環境下是無法訪問到的,但是在window上掛載一個方法outerAddOne,指向addOne,外界就可以訪問到了。

OK,了解了這個自執行匿名函數的作用,這里還有兩個問題。

第一,為什么要傳入window?

看了上面的outerAddOne這個例子,就會發現,不傳入window也沒什么嘛,照樣可以把方法掛到window身上啊。
兩個原因:

首先,從代碼壓縮混淆的角度考慮。

我們用線上工具來壓縮混淆下面這段示例代碼:

function say(){  var name="naima";  window.description="hi "+name;}

壓完混完后瘦了一點:

function say(){var a="naima";window.description="hi "+a}

看到沒有,用a代替了name,但是window既不是聲明的局部變量也不是參數,是不會被壓縮混淆的,所以將window作為參數傳入可解決這個問題。

其次,傳入window參數,就可以不用沿著作用域鏈一層層向上查找直到頂層作用域去獲取window對象了,訪問更快了。

第二,為什么要傳入undefined?

undefined并不是JS中的關鍵字,在IE8及以下中是可以對其重新賦值的。

var undefined="new value";alert(undefined);//alert “new value"

在參數列表中給出undefined參數,但是不傳入值,那么這個參數值就是undefined值了。

jQuery對象的構建

先看jQuery源碼中如何對jQuery賦值的:

jQuery = function( selector, context ) {        // The jQuery object is actually just the init constructor 'enhanced'        return new jQuery.fn.init( selector, context, rootjQuery );    }    

我就是被new jQuery.fn.init()這里弄暈了,先在這里暫停,回想一下平常我是如何使用jQuery的($即對應‘jQuery'):

$('body').CSS('background','red');$.parseJSON('{}');

要實現這兩種調用,$('body')應該是一個實例對象,css是每個實例共享的方法,是原型上的方法。而$則是一個類,parseJSON則是類的靜態方法。
接下來,我們試著往這個結果上靠。

如何不用new關鍵字得到jQuery對象

回想一下平常我都是怎么構建實例對象的,通常我會這樣寫一個PRince類:

function Prince(name){  this.name=name;  this.body="human";}Prince.prototype.change=function(){  this.body="frog";};

然后我會這樣去獲取一個Prince實例對象:

var prince=new Prince("Harry");prince.change();

如果我年紀大了忘記用new關鍵字了,程序就報錯了:

var a=Prince('harry');a.change();//error,"Cannot read property 'change' of undefined"

除了調用方法會出錯之外,window還被掛載了兩個變量上去,何其無辜。

但是獲取jQuery對象(以下簡稱JQ對象)用new和不用new都可以,返回的是一樣樣的。

console.log($('*').length);//14console.log(new $('*').length);//14

為了做到這點,我們很容易想到需要在構造函數內部返回對象。引用下我在另一篇博文javaScript中的普通函數與構造函數里寫的:

構造函數有return值怎么辦?
構造函數里沒有顯式調用return時,默認是返回this對象,也就是新創建的實例對象。
當構造函數里調用return時,分兩種情況:
1.return的是五種簡單數據類型:String,Number,Boolean,Null,Undefined。
這種情況下,忽視return值,依然返回this對象。
2.return的是Object
這種情況下,不再返回this對象,而是返回return語句的返回值。

所以我們應該在jQuery構造函數內部去返回一個對象,這樣就可以不用new的方式去創建JQ對象了,其實這時候,構造函數就相當于一個工廠函數了。
那么核心問題來了。

該返回什么樣的對象?對于這個對象有何要求?

這個對象必須可以調用jQuery.prototype上的方法。

我們使用或自己寫jQuery插件的時候會經常遇到$.fn這個對象,很多插件都是通過擴展這個對象來實現的。
$.fn其實對應著jQuery.prototype,$和fn分別是jQuery和prototype的簡寫方式,只要我們把方法擴展到這個原型對象身上,通過$()獲取的JQ對象都是可以訪問到方法的。
例如:

$.fn.greeting=function(){alert('hi')};$('body').greeting();//alert 'hi'

所以,工廠函數內部返回的對象一定要可以調用jQuery.prototype上的方法。

是時候看John Resig到底是怎么做的啦。

jQuery源碼

jQuery = function( selector, context ) {    return new jQuery.fn.init( selector, context, rootjQuery );},jQuery.fn = jQuery.prototype = { //fn即對應prototype    constructor: jQuery,    init: function( selector, context, rootjQuery ) {        ...        return this;    }    ...}jQuery.fn.init.prototype = jQuery.fn;

Chrome里調試時候添加JQ對象的watch,會看到類似如下的結果:

$('*'): n.fn.init[14]

看到上面這段源碼,原因就很明顯了,其實我們所說的JQ對象根本就是init函數的實例對象,而init則是jQuery原型上的一個對象,它本身是沒有什么方法的,全靠從jQuery原型上拿。

"jQuery.fn.init.prototype = jQuery.fn"這句很重要,它將init的原型指向jQuery的原型,所以JQ對象才可以訪問‘css'、'show'、'hide'這些寫在jQuery.fn上的方法。

我們可能會有疑問,為何要從init這繞這么一大圈來訪問jQuery的原型,而不是直接返回一個jQuery實例直接通過這個實例來訪問自身原型?比如說代碼可以寫成這樣:

jQuery = function( selector, context ) {        return new jQuery();} 

問題很明顯,這樣做只會大家一起死,死在循環里。

好,那我接受init的存在,但是我這樣寫難道不可以嗎?

jQuery = function( selector, context ) {        return jQuery.fn.init();//不同點在于去掉了new關鍵字}

讓我們做點動作來證明加上new是有用的。

jQuery = function( selector, context ) {    return jQuery.fn.init();},jQuery.fn = jQuery.prototype = {    init: function() {            this.name='sheila';            return this;    },    anotherName:'sunwukong'};var jq=jQuery();console.log(jq.anotherName);//"sunwukong"console.log(jq.name);//"sheila"

上面這段代碼是為了說明this的作用域問題,其不僅能訪問init函數內部,還能向上一層到fn對象。我聽人家說,做框架的,作用域要獨立才好呢。
給它加上new關鍵字:

...return new jQuery.fn.init();...console.log(jq.anotherName);//undefinedconsole.log(jq.name);//"sheila"

這樣this的作用域就獨立出來了。

經博友評論提醒,加不加new還牽涉到一個更重要的問題:返回的對象究竟是誰。不加new的情況下,'jQuery.fn.init()'相當于調用方法,this指向的以及最后返回的都是同一個jQuery.fn對象,$('body')和$('p')就沒有區分了。顯然,這是不合理的。而加了new,就是每次用構造函數實例化了一個新對象,彼此都是不同的。

有任何不妥之處或錯誤歡迎各位指出,不勝感激~

題外話

經常看別人的博客,有些表述方式實在獨特而有趣,每每讀來都覺妙趣橫生,啞然失笑。不禁心生羨慕,技術過硬,知識面廣還寫得一手好文章,贊!
想起在學校時每次我們做presentation,上臺第一句,“大家好,我今天講的題目是……”,然后幻燈片一頁頁劃過去,“歷史背景”,“研究現狀”,“我使用的方法”……導師都聽得一臉崩潰,“nonono,不要,不要這樣,你們這樣講,不會有人有耐心聽下去的……我們要像說故事一樣娓娓道來,抓住聽眾的注意力,一點點引入……”于是以后我都盡量按照“說故事”這個思路去講,最后畢業答辯的時候,一個老師說,“為什么我覺得你像故宮導覽哈哈哈哈”……
果然還是沒有掌握表述的技巧啊。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 嵊州市| 疏附县| 南木林县| 裕民县| 长泰县| 宣威市| 景宁| 乳源| 慈溪市| 新昌县| 花垣县| 普陀区| 凤山市| 红安县| 阿克苏市| 汶川县| 梁平县| 海宁市| 天长市| 绩溪县| 彭阳县| 元阳县| 亚东县| 淮阳县| 婺源县| 湖南省| 彭山县| 垫江县| 本溪市| 韶山市| 濮阳市| 龙里县| 清水河县| 安义县| 盐山县| 焉耆| 大庆市| 安达市| 阳信县| 铁岭县| 湖州市|