文 / 景朝霞
来源公号 / 朝霞的光影笔记
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?
从"你"到"更好的你",有无限可能~