▲ 點選上方藍字關注我 ▲
文 / 景朝霞
來源公号 / 朝霞的光影筆記
ID / zhaoxiajingjing
目錄:0 / moment庫1 / 工廠設計模式(1)問個問題(2)隻看factory(global)在浏覽器下的運作2 / jQuery中常用的方法(1)$(document).read() (2)JQ擷取的對象和原生JS擷取的對象(3)get和eq方法的差別(4)each方法3 / 代碼
0 / moment庫
API:http://momentjs.cn/docs/
我們的項目裡面用到moment.js這個庫來處理日期,有一天旁邊的同僚翻着這段源碼問我啥意思:

△ 圖1_moment.js的源碼
咱之前看過jQuery的源碼,再來看這個是不是輕松很多啦~
好,繼續來讀jQuery的源碼,跟着jQuery大佬學程式設計思想
1 / 工廠設計模式
(1)問個問題
var a = $(); console.log(a instanceof jQuery); //=> true var b = $('div'); console.log(b instanceof jQuery); //=> true
△ object instanceof constructor
instanceof
運算符用來檢測
constructor.prototype
是否存在于
object
的原型鍊上
問題來了:這裡沒有使用
new jQuery()
為什麼
$()
出來的就是jQuery的執行個體呢?
我看的jQuery的最新版本
3.5.1
△ 圖2_jQuery的源碼
(2)隻看factory(global)在浏覽器下的運作
拆了簡化的出來的,可以對照源碼檢視哈~
function factory(window, noGlobal) { var version = "3.5.1", jQuery = function (selector, context) { return new jQuery.fn.init(selector, context); }; jQuery.fn = jQuery.prototype = { jquery: version, constructor: jQuery }; var rootjQuery, rquickExpr = /^(?:\s*()[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function (selector, context, root) { //...CDOE }; init.prototype = jQuery.fn; if (typeof noGlobal === "undefined") { window.jQuery = window.$ = jQuery; } } factory(window);
△ 從jQuery源碼粘貼出來的
$() 這就是把jQuery方法執行:作為普通函數執行(JQ的選擇器),但是最後得到的結果是jQuery類的執行個體對象
$('.box')
和
jQuery('.box')
是怎麼建立jQuery的執行個體對象的呢?
△ 圖3_簡圖
從面向對象角度可以看出,
$()
jQuery()
調用了jQuery函數時,傳回了一個執行個體對象:new jQuery.fn.init()
而,jQuery.prototype 和 jQuery.fn.init.prototype 是一個原型對象
∴
$()
可以不使用new操作符就能得到jQuery的執行個體對象
工廠設計模式,抽象了建立具體對象的過程。
2 / jQuery中常用的方法
提問:
1、不用new操作符如何快速的建立Fn函數的一個執行個體對象?
2、
$(document).read(function (){} )
和
$(function(){})
的差別?
3、原生JS擷取的DOM元素集合與JQ擷取的集合怎麼互相轉換?
4、JQ的get方法和eq方法的差別?
5、JQ的each方法怎麼實作的?
(1)$(document).read()
$(document).read(function (){})
與
$(function (){})
有差別嗎?
△ 圖4_差別
$(document).ready(函數)
裡面的ready方法監聽了DOMContentLoaded事件
$(function (){ // 等待頁面中的DOM結構渲染完成後,去執行回調函數 });
(2)JQ擷取的對象和原生JS擷取的對象
//=>由原生JS擷取的DOM元素 var box = document.getElementById('box'); //=> 由JQ擷取的執行個體對象 var $box = $('#box');
△ 擷取元素
他們兩種擷取的對象之間的方法是不能混着調用的,需要切換才可以:
① 原生=>JQ,
$(原生對象)
,即:
$(box)
類數組集合
② JQ=>原生,
$xxx[0]
,即:
$box[0]
或者
$box.get(0)
怎麼實作的呢?看源碼:
△ 圖5_原生JS的DOM轉為JQ對象
思考題:那如果selector是一個字元串,那段代碼是啥意思呢?
if(typeof selector === "string") {....}
這裡的代碼啥意思呢?
還有一種
$(document.body).get(0)
,咱去找原型上的get方法源碼:
△ 圖6_$('*').get()方法:将JQ對象轉為原生JS的對象
(3)get和eq方法的差別
△ 圖7_get方法和eq的差別
(4)each方法
△ 圖8_JQ的each方法
isArrayLike
方法:見名知意,判斷是否為類數組,那源碼怎麼寫的呢?自己翻翻看
for in
循環處理對象,3個注意點:
① 可以周遊到原型上咱自己擴充的公共的屬性
② 數字的順序(這個咱控制不了)
③ 無法找到symbol的屬性
好,改造一下:
var keys = Object.kesy(obj) typeof Symbol !== 'undefined' ? keys = kyes.concat(Object.getOwnPropertySymbols(obj)) : null; for(; i < keys.length; i++){ var key = keys[i]; if(callback.call(obj[key], key, obj[key] === false)) { break; } }
3 / 代碼
function factory(window, noGlobal) { var arr = []; var slice = arr.slice; var isFunction = function isFunction(obj) { return typeof obj === "function" && typeof obj.nodeType !== "number"; }; function isArrayLike(obj) { // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = !!obj && "length" in obj && obj.length, type = toType(obj); if (isFunction(obj) || isWindow(obj)) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj; } var version = "3.5.1", // Define a local copy of jQuery jQuery = function (selector, context) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init(selector, context); }; jQuery.fn = jQuery.prototype = { jquery: version, constructor: jQuery, length: 0, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function (num) { // Return all the elements in a clean array if (num == null) { return slice.call(this); } // Return just the one element from the set return num < 0 ? this[num + this.length] : this[num]; }, eq: function (i) { var len = this.length, j = +i + (i < 0 ? len : 0); return this.pushStack(j >= 0 && j < len ? [this[j]] : []); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function (elems) { // Build a new jQuery matched element set var ret = jQuery.merge(this.constructor(), elems); // Add the old object onto the stack (as a reference) ret.prevObject = this; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. each: function (callback) { return jQuery.each(this, callback); } }; jQuery.extend = jQuery.fn.extend = function () { // 把方法擴充到jQuery對象上、jQuery.fn上 }; jQuery.extend({ each: function (obj, callback) { var length, i = 0; if (isArrayLike(obj)) { length = obj.length; for (; i < length; i++) { if (callback.call(obj[i], i, obj[i]) === false) { break; } } } else { for (i in obj) { if (callback.call(obj[i], i, obj[i]) === false) { break; } } } return obj; } }); var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with // Shortcut simple #id case for speed rquickExpr = /^(?:\s*()[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function (selector, context, root) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if (!selector) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if (typeof selector === "string") { if (selector[0] === " && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec(selector); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById(match[2]); if (elem) { // Inject the element directly into the jQuery object this[0] = elem; this.length = 1; } return this; } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { return (context || root).find(selector); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor(context).find(selector); } // HANDLE: $(DOMElement) } else if (selector.nodeType) { this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if (isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present selector(jQuery); } 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); if (typeof noGlobal === "undefined") { window.jQuery = window.$ = jQuery; } } factory(window);
△ 從jQuery中粘貼的
- end -
提問:
1、不用new操作符如何快速的建立Fn函數的一個執行個體對象?
2、
$(document).read(function (){} )
和
$(function(){})
的差別?
3、原生JS擷取的DOM元素集合與JQ擷取的集合怎麼互相轉換?
4、JQ的get方法和eq方法的差別?
5、JQ的each方法怎麼實作的?
思考題:如何判斷是類數組isArrayLike?
從"你"到"更好的你",有無限可能~