1. 程式人生 > >jQuery源碼淺析

jQuery源碼淺析

使用下標 src 定義 ring col 得出 oba nba 簡易

如果說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>

jQuery源碼淺析