看板 KnucklesNote
作者 標題 [JS] 克服JS的奇怪部分 ch8 檢驗知名的框架與資源庫
時間 2016-12-10 Sat. 00:09:56
Udemy課程: JavaScript全攻略:克服JS的奇怪部分
https://www.udemy.com/javascriptjs/learn/v4/overview
上完第八章的心得筆記
章節8 檢驗知名的框架與資源庫
70. 深入瞭解原始碼:jQuery(一)
jQuery 是一個JS資源庫,用來簡化JS語法、解決跨瀏覽器問題
jQuery 主要是用來操作網頁的 DOM (Document Object Model)
例如這個網頁 http://knuckles.disp.cc/test/jsWeirdPart/ch8/index.html
HTML的部份為
<html>
</html>
我們先載入了1.11.2版的 jQuery<head>
</head>
<body>
<div id="main" class="container">
<h1>People</h1>
<ul class="people">
<li>John Doe</li>
<li>Jane Doe</li>
<li>Jim Doe</li>
</ul>
</div>
<script src="jquery-1.11.2.js"></script>
<script src="app.js"></script>
</body>
</html>
然後在 app.js 中,用 jQuery 取得網頁中的三個 <li> 元件(element)
var q = $("ul.people li");
console.log(q);
在開發人員工具的顯示結果為console.log(q);
![[圖]](http://i.imgur.com/wL6nKyC.png)
jQuery 使用 $ 符號做為全域變數,若「$」被其他資源庫用掉了,也可以改用「jQuery」
例如 $("ul.people li") 也可以改為 jQuery("ul.people li")
意思是執行函數 $(),傳入一組類似css選擇器(selector)的字串 "ul.people li"
代表要尋找 class="people" 的 <ul> 元件,裡面的所有 <li> 元件
使用 console.log 將輸出結果顯示出來,為一個 jQuery.fn.init 的陣列物件
這個 init 陣列物件存了三個 <li> 元件
點一下 __proto__ 看一下他的原型為一個 jQuery 物件
可以看到裡面有一大堆的 jQuery 函數可以用
接著我們要看 jQuery 的原始碼,看他的運作機制
打開 jquery-1.11.2.js
一開始是一個立即執行函數 function( global, factory ){ ... }
(function( global, factory ) {
// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
//...
裡面是在做一些執行環境的檢查,然後執行傳入的函數 factoryif ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments ...
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
//...
接著傳入兩個輸入值,第一個 global 為全域物件,傳入
typeof window !== "undefined" ? window : this
意思是若 window 不是 undefined 的話傳 window,不然的話用 this 傳目前的全域物件
第二個輸入值 factory 傳入一個匿名函數 function( window, noGlobal ){ ... }
裡面建立了一大堆的函數,這邊的內容才是 jQuery 的主程式
factory 會在立即執行函數裡被執行,
所以會得到新的執行環境
一開始先建立一些變數,接著建立最重要的 jQuery 函數
var
jQuery 為一個函數,第一個輸入值 selector 就是用來選取DOM的字串version = "1.11.2",
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
}
函數內容只有用 new 運算子加上 jQuery.fn.init 後輸出
所以我們在 app.js 中使用 $() 輸入 selecter 字串後
輸出為 jQuery.fn.init
我們不需要使用 new 運算子加上 $(),因為 $() 只是一般函數
他的內容會用 new 執行函數建構子 jQuery.fn.init 後,再輸出建立的物件
這樣就不用每次使用 $() 時還要再加 new 了
接著往下尋找函數建構子 jQuery.fn.init
先找到
jQuery.fn = jQuery.prototype = {
}
jQuery 雖然不是函數建構子,但也會有屬性 prototypejquery: version,
constructor: jQuery,
//...
}
將屬性 prototype 取個別名 fn
這樣就不用每次都要打 prototype
這邊將 jQuery.fn 用物件實體語法產生一個自訂的物件來取代了
再來是實作了ch5提過的 extend 函數
同時加在了 jQuery 下與 jQuery.fn 下
jQuery.extend = jQuery.fn.extend = function() {
}
函數 extend() 的功能是將第二個輸入值之後,每個輸入物件的所有成員//...
}
全部複製到第一個輸入值的物件中
如果只有一個輸入物件的話,就把這個輸入物件的成員加到jQuery這個函數物件上
接下來就是執行 extend 輸入一個物件實體,將一堆成員加進jQuery
jQuery.extend({
});
//...
});
71. 深入瞭解原始碼:jQuery(二)
繼續往下看到這個 Sizzle
var Sizzle =
/*!
* Sizzle CSS Selector Engine v2.2.0-pre
* http://sizzlejs.com/
*
* Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-12-16
*/
(function( window ) {
return Sizzle;
})( window );
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
這裡又使用了一個立即執行函數建立了新的執行環境/*!
* Sizzle CSS Selector Engine v2.2.0-pre
* http://sizzlejs.com/
*
* Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-12-16
*/
(function( window ) {
//...
return Sizzle;
})( window );
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
這一大段是將另一個資源庫 Sizzle CSS Selector Engine 整個加了進來
然後將產生的 Sizzle 物件再加進 jQuery 的成員
用來將 selector 字串轉為 HTML 上的元件
再繼續往下翻到
// Initialize a jQuery object
// A central reference to the root jQuery(document)
var rootjQuery,
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
// Initialize central reference
rootjQuery = jQuery( document );
終於找到了函數建構子 jQuery.fn.init// A central reference to the root jQuery(document)
var rootjQuery,
// Use the correct document accordingly with window argument (sandbox)
document = window.document,
// A simple way to check for HTML strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
// Strict HTML recognition (#11290: must start with <)
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
init = jQuery.fn.init = function( selector, context ) {
//...
return jQuery.makeArray( selector, this );
};
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
// Initialize central reference
rootjQuery = jQuery( document );
比較特別的是這個函數建構子在最後使用了
return jQuery.makeArray( selector, this );
函數建構子若不加 return,JS會自動輸出建立的物件 this
這邊加了 return,是為了將 this 用 makeArray 做些加工後再輸出
接下來的這行有點難懂
init.prototype = jQuery.fn;
其實就是把前面在 jQuery 的原型 jQuery.fn 上建立的一堆成員
覆寫到函數建構子 init 的原型上
因為我們不想每次使用 jQuery 函數時還要再加 new
所以我們是在 jQuery 函數中使用 new 加上建構子 jQuery.fn.init
產生的物件原型再指到 jQuery 函數的原型上
這樣就可以用一般函數來建立物件,並且讓物件有原型可以使用
72. 深入瞭解原始碼:jQuery(三)
前面的例子只有用 jQuery 取得網頁上的元件
接下來我們使用 jQuery 提供的函數來改變元件的 class 屬性
$("ul.people").addClass("newclass").removeClass("people");
使用 addClass() 將 <ul> 元件的 class 屬性加上 "newclass"接著用 removeClass() 將 class 屬性的 people 移除
使用開發者工具看,class的確由 "people" 變成了 "newclass"
![[圖]](http://i.imgur.com/ruF4tCM.png)
不過為什麼函數 removeClass() 可以直接用 . 接在函數 addClass() 後呢
因為這是 jQuery 的一個模式叫做方法鏈結(method chaining)
方法鏈結: 使用 obj.method1().method2() 時,
兩個函數的 this 都是指向一開始的物件 obj
來看看 jQuery 怎麼做的
在 jquery-1.11.2.js 中搜尋 addClass
jQuery.fn.extend({
找到這一段,使用 extend 在 jQuery 函數的原型加上的 addClassaddClass: function( value ) {
//...
return this;
},
removeClass: function( value ) {
//...
return this;
},
//...
最後是 return this;
所以當我們執行一個 jQuery 物件的成員函數 addClass 時
這物件本身沒有這個成員函數,所以會在原型鍵上找到 addClass
而 addClass 最後又回傳 this 代表一開始的物件本身
所以後面又可以再接另一個 jQuery 的成員函數
最後來看一下 jQuery 檔案的最後一段
var
jQuery.noConflict = function( deep ) {
};
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( typeof noGlobal === strundefined ) {
}
return jQuery;
}));
最後是要將 jQuery 與 $ 設定為全域變數// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$;
jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
};
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( typeof noGlobal === strundefined ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
}));
但為了避免蓋掉本來的全域變數
所以先把全域上的 window.jQuery 和 window.$ 備份在 _jQuery 和 _$
建立一個函數 noConflict
若要恢復成原本的 $ 和 jQuery 時可執行 $.noConflict()
然後用一個新的變數名稱來儲存回傳的 jQuery
最後就是將 jQuery 設定成全域變數
window.jQuery = window.$ = jQuery;
--
※ 作者: Knuckles 時間: 2016-12-10 00:09:56
※ 編輯: Knuckles 時間: 2016-12-13 16:33:42
※ 看板: KnucklesNote 文章推薦值: 0 目前人氣: 0 累積人氣: 741
回列表(←)
分享