jQuery源码浅析
如果说php是世界上最好的语言,那么javascript无疑可以称为世界上最飘逸的语言,最近看了下jQuery的源码,实现了一个简陋的jQuery。我觉得要看懂jQuery整体结构,需要搞懂js作用域链,闭包,js prototype继承,关于闭包网络上的定义实在太多了,这里参照了js权威指南里的定义,感觉从js的角度好理解一点。
闭包:js函数对象不仅包含函数的代码逻辑,还引用了当前的作用域链,
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内
这种特性在计算机科学文献中称为闭包,所有的js函数都是闭包。
javascript运行在它被定义的作用域里,而不是执行的作用域里
关于js作用域参见 : http://www.laruence.com/2009/05/28/863.html
废话少说,上滥代码
<!DOCTYPE html> <html> <head> <title>jQuery源码浅析</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <script> /** * * 参照jQuery 3.2.1源码 * 省略了一些规范,如AMD, Commonjs * 整个jQuery包含在匿名函数中,函数就是闭包 */ (function(window, factory){ factory(window); })(window, function(window){ /** * 为什么要传window对象? * 1.在jQuery里可以把window对象当局部变量使用 * 2.压缩的时候window变量名可以压缩至1个字节 */ var getProto = Object.getPrototypeOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var fnToString = hasOwn.toString; var ObjectFunctionString = fnToString.call( Object ); /** * 工厂方法,返回jQuery.fn.init的实例,即jQuery对象 * selector待查找的字符串,源码里还有context参数,此处省略 * 我们所说的jQuery其实准确的说是jQuery工厂方法,调用jQuery工厂方法返回的才是真正的jQuery对象 */ var jQuery = function(selector){ return new jQuery.fn.init(selector); }, version = "3.2.1"; //jQuery原型对象赋值给jQuery.fn,定义在jQuery.prototype里的方法可以被所有的jQuery对象使用 jQuery.fn = jQuery.prototype = { jquery: version, constructor: jQuery, //...省掉了一些方法 }; //jQuery.fn的属性init函数,jQuery的选择器使用了Ssize,这里简单的使用一个id选择器 jQuery.fn.init = function(selector){ /* (在Javascript中,This关键字永远都指向函数(方法)的所有者) this指向的是jQuery.fn * 这里简单的将DOM对象赋值给this[0],其他属性省略, 我们使用jQuery的时候使用下标0即可将jQuery对象转化为普通的DOM对象 */ this[0] = window.document.getElementById(selector); return this; }; /** * 这一句很关键 * 将jQuery.fn赋值给jQuery.fn.init的原型,这样jQuery.fn.init的实例(通常我们所说的jQuery对象就是它)可以使用jQuery.fn的方法 * 结合之前可以得出 jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype * jQuery.fn,jQuery.prototype扩展的方法属性 jQuery对象可以使用 */ jQuery.fn.init.prototype = jQuery.fn; //实现了jQuery的html方法 jQuery.fn.html = function(value){ if(typeof value === "string"){ this[0].innerHTML = value; return this; }else{ return this[0].innerHTML; } }; /** * * jQuery扩展方法,除了可以扩展jQuery外,还可以扩展你指定的对象 * jQuery.extend 扩展jQuery,可以理解为扩展类方法 * jQuery.fn.extend 扩展jQuery.fn,即jQuery实例可以使用,可以理解为扩展实例方法 * * 具体用法 * 1.jQuery.extend(obj) 扩展jQeury * 2.jQuery.extend(true, obj) 深度扩展jQuery * 3.jQuery.extend(obj1, obj2) 扩展obj1 * 4.jQuery.extend(true obj1, obj2) 深度扩展obj1 */ jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; //第一个参数为boolean且为true的时候为深度扩展 if ( typeof target === "boolean" ) { deep = target; // Skip the boolean and the target target = arguments[ i ] || {}; i++; } //被扩展的对象不是对象或者函数 if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { target = {}; } //参数只有1个对象的情况下,扩展jQuery,多个对象则扩展第一个对象 if ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { //只处理非空值 if ( ( options = arguments[ i ] ) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } //仅在属性为纯粹对象或者 数组的时候深度拷贝才有效 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 ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend( { isFunction: function( obj ) { return jQuery.type( obj ) === "function"; }, isPlainObject: function( obj ) { var proto, Ctor; // Detect obvious negatives // Use toString instead of jQuery.type to catch host objects if ( !obj || toString.call( obj ) !== "[object Object]" ) { return false; } proto = getProto( obj ); // Objects with no prototype (e.g., `Object.create( null )`) are plain if ( !proto ) { return true; } // Objects with prototype are plain iff they were constructed by a global Object function Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; }, type: function( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android <=2.3 only (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call( obj ) ] || "object" : typeof obj; }, }); //简易ready方法实现, 只支持DOMContentLoaded jQuery.extend( { readyList : [], ready: function(fn){ if(fn && typeof fn === 'function'){ //向数组末尾添加函数 jQuery.readyList.push(fn); }else{ //顺序执行ready绑定的方法 for(var i in jQuery.readyList){ fn = jQuery.readyList[i]; //fn来自于全局作用域,属于window对象 fn.call(window, jQuery); } } } }); //只支持DOMContentLoaded document.addEventListener( "DOMContentLoaded", completed ); window.addEventListener( "load", completed ); function completed() { document.removeEventListener( "DOMContentLoaded", completed ); window.removeEventListener( "load", completed ); jQuery.ready(); } //只暴露了两个变量到全局作用域 window.$ = window.jQuery = jQuery; }); $.ready(function(){ console.log('----设置id为test的元素文档内容,并获取文档内容----' + $('test').html('jQuery').html()); }); $.ready(function(){ console.log(1); }); $.ready(function(){ console.log(2); }); $.ready(function(){ console.log(3); }); var obj1 = { ball : { nba : 'nba' }, }; var obj2 = { ball : { cba : 'cba' } }; var obj3 = { ball : { nba : 'nba' } }; //扩展obj1 $.extend(obj1, obj2); /** * {ball : {'cba' : 'cba'}} */ //深度扩展obj3 $.extend(true, obj3, obj2); /** * {ball : {'nba' : 'nba'}, {'cba' : 'cba'}} */ </script> <body> <div id="test">my jQuery</div> </body> </html>