We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在jQuery中我们通常通过$()传入一个选择器来获取一个元素,然后会返回一个jQuery对象,把匹配的元素放到一个类似数组的集合中,而且执行完了还可以执行下一个函数,jQuery如此强大,我们就来分析一下它背后的原理。
jQuery
理解jQuery的原型
先看看$()执行都经历了哪些过程,下面是经过删减后的代码
$()
var jQuery = function () { return new jQuery.fn.init() }; jQuery.fn = jQuery.prototype = { constructor: jQuery, length: 0 } var init = jQuery.fn.init = function () { // 初始化函数 } init.prototype = jQuery.fn
通过上面的代码我们可以得到下面这样的原型图 我们发现jQuery函数和init函数共享同一个原型,这就是jQuery支持链式写法的原因
init
提示:每次执行$()都会初始化一个实例,如果你每次获取一个元素都初始化一个实例,那么就会消耗更多的性能,建议用变量保存这个值方便以后使用。
理解扩展函数extend
这是 jQeury 核心中很重要的一个函数 . 通过它我们就可以轻松地在 jQuery 或者 jQuery 原型对象中随意扩展自己想要的方法
jQuery.extend = jQuery.fn.extend = function () { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // 如果传进来的首个参数是一个 boolean 类型的变量,那就证明要进行深度拷贝。而这时传进来的 argumens[1] 就是要拷贝的对象 if (typeof target === "boolean") { deep = target; target = arguments[i] || {}; i++; } // 如果 target 不是 objet 并且也不是 function 就默认设置它为 {} if (typeof target !== "object" && !jQuery.isFunction(target)) { target = {}; } //如果只传入了一个参数 , 那么扩展的就是 jQuery 自身 if (i === length) { target = this; i--; } for (; i < length; i++) { if ((options = arguments[i]) != null) { for (name in options) { src = target[name]; copy = options[name]; // 不把自己的引用作为自己的一个成员 if (target === copy) { continue; } // options 是扩展对象 , 它的方法或属性将会被扩展到 target 上 if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && Array.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // 使用递归的方法实现深度拷贝 target[name] = jQuery.extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; };
通过阅读extend源码我们发现,不仅可以通过$.extend()在jQuery中添加静态方法和通过$.fn.extend()在jQuery原型中添加公共方法之外,而且还实现了像ES6中Object.assign()方法的对象合并功能,而且jQuery更强大,支持深浅拷贝,只需在第一个参数中传入true/false,ES6的那个方法是不支持深拷贝的
extend
$.extend()
$.fn.extend()
Object.assign()
$.extend({},{name: "张三", age: 18}) Object.assign({},{name: "张三", age: 18})
理解初始化函数init
init = jQuery.fn.init = function (selector, context, root) { var match, elem; // 没有传入参数会返回一个空对象,如:$() if (!selector) { return this; } root = root || rootjQuery; // 传入字符串的时候分别对有上下文和没有上下文做了处理 if (typeof selector === "string") { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { match = [null, selector, null]; } else { match = rquickExpr.exec(selector); } if (match && (match[1] || !context)) { if (match[1]) { context = context instanceof jQuery ? context[0] : context; // 调用 jQuery.merge 把两个数组拼接起来 ( 将第二个数组接到第一个数组的尾巴上 ) // 调用 jQuery.parseHTML 将字符串转化成真正的 DOM 元素然后放在一个数组里面 jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { if (jQuery.isFunction(this[match])) { this[match](context[match]); } else { this.attr(match, context[match]); } } } return this; } else { // 推荐使用ID选择器来匹配元素从而提高性能! elem = document.getElementById(match[2]); if (elem) { this[0] = elem; this.length = 1; } return this; } // 参数还可以传入一个表达式哦!如:$("#"+"box") 相当于 $("#box") } else if (!context || context.jquery) { return (context || root).find(selector); } else { return this.constructor(context).find(selector); } } else if (selector.nodeType) { // 参数可以传入一个DOM元素,如:$(document.getElementById('box')) 相当于 $("#box") this[0] = selector; this.length = 1; return this; // 参数可以传入一个函数,如:$(function (){}) 相当于 window.onload = function() {} } else if (jQuery.isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : selector(jQuery); } // 最后这条语句是将传入的一个参数转换为jQuery对象返回,如:$(this) return jQuery.makeArray(selector, this); };
链式写法的原因
支持链式写法和下面连个函数有关,我们来看看
jQuery.fn = jQuery.prototype = { pushStack: function (elems) { var ret = jQuery.merge(this.constructor(), elems); ret.prevObject = this; return ret; }, end: function () { return this.prevObject || this.constructor(); } }
pushStack使用传入的元素生成一个新的 jQuery 元素 , 并将这个对象的 prevObject 设置成当前这个对象 (this). 最后将这个新生成的 jQuery 对象返回,end方法是我返回之前设置的对象prevObject
pushStack
prevObject
this
end
有很多的函数 return this,这就是为什么支持链式写法的原因,但是每次都链式调用的同时都不能修改我之前匹配到的那个元素集合,如果某一个方法真的要修改匹配元素集合 , 那么它就会调用 pushStack 方法 , 把当前的 jQuery 对象保存起来 , 以便以后使用 end方法恢复这个 jQuery 对象 。
return this
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在jQuery中我们通常通过$()传入一个选择器来获取一个元素,然后会返回一个
jQuery
对象,把匹配的元素放到一个类似数组的集合中,而且执行完了还可以执行下一个函数,jQuery如此强大,我们就来分析一下它背后的原理。理解jQuery的原型
先看看
$()
执行都经历了哪些过程,下面是经过删减后的代码通过上面的代码我们可以得到下面这样的原型图
![图片](https://raw.githubusercontent.com/wuxianqiang/photo/2c5b463857f54499382a7322e1925b879b4dcbef/jq.png)
我们发现
jQuery
函数和init
函数共享同一个原型,这就是jQuery支持链式写法的原因提示:每次执行
$()
都会初始化一个实例,如果你每次获取一个元素都初始化一个实例,那么就会消耗更多的性能,建议用变量保存这个值方便以后使用。理解扩展函数extend
这是 jQeury 核心中很重要的一个函数 . 通过它我们就可以轻松地在 jQuery 或者 jQuery 原型对象中随意扩展自己想要的方法
通过阅读
extend
源码我们发现,不仅可以通过$.extend()
在jQuery中添加静态方法和通过$.fn.extend()
在jQuery原型中添加公共方法之外,而且还实现了像ES6中Object.assign()
方法的对象合并功能,而且jQuery更强大,支持深浅拷贝,只需在第一个参数中传入true/false,ES6的那个方法是不支持深拷贝的理解初始化函数init
链式写法的原因
支持链式写法和下面连个函数有关,我们来看看
pushStack
使用传入的元素生成一个新的 jQuery 元素 , 并将这个对象的prevObject
设置成当前这个对象 (this
). 最后将这个新生成的 jQuery 对象返回,end
方法是我返回之前设置的对象prevObject
有很多的函数
return this
,这就是为什么支持链式写法的原因,但是每次都链式调用的同时都不能修改我之前匹配到的那个元素集合,如果某一个方法真的要修改匹配元素集合 , 那么它就会调用pushStack
方法 , 把当前的 jQuery 对象保存起来 , 以便以后使用end
方法恢复这个 jQuery 对象 。The text was updated successfully, but these errors were encountered: