jquery 这些小技巧你懂吗
jquery有很多奇淫技巧,它的結構清晰,高內聚、低耦合,兼具優秀的性能與便利的擴展性。
這里僅對其中的一些小技巧做個總結。
?
1、jquery閉包
把當前沙箱需要的外部變量通過函數參數引入進來
只要保證參數對內提供的接口的一致性,你可以隨意替換傳進來的這個參數
(function(window, undefined) {// jQuery 代碼undefined = 20;console.log(undefined); // 20 })(window); (function(w, u) {// jQuery 代碼console.log(w); // windowu = 20;console.log(u); // 20 })(window); (function(window,undefined) {// jQuery 代碼undefined = 20;console.log(undefined); // 20})(window, undefined);undefined = 20;console.warn(undefined); // undefined?
2、jquery 無 new 構造
// 無 new 構造 // s也能訪問jq原型鏈上的所有方法 var s = $('#test');// 當然也可以使用 new // q能訪問jq原型鏈上的所有方法 var q = new $('#test'); (function (window, undefined) {var// ...jQuery = function (selector, context) {// 實例化方法 jQuery() 實際上是調用了其拓展的原型方法 jQuery.fn.initreturn new jQuery.fn.init(selector, context, rootjQuery);},// jQuery.prototype 即是 jQuery 的原型,掛載在上面的方法,即可讓所有生成的 jQuery 對象使用jQuery.fn = jQuery.prototype = {// 實例化化方法,這個方法可以稱作 jQuery 對象構造器init: function (selector, context, rootjQuery) {// ...}},jQuery.fn.init.prototype = jQuery.fn;})(window);1)使用 $('xxx') 這種實例化方式,其內部調用的是?return new jQuery.fn.init(selector, context, rootjQuery) ,即構造實例是交給?jQuery.fn.init() 方法去完成。
2)將 jQuery.fn.init 的 prototype 屬性設置為 jQuery.fn,那么使用 new jQuery.fn.init() 生成的對象的原型對象就是 jQuery.fn ,所以掛載到 jQuery.fn 上面的函數就相當于掛載到 jQuery.fn.init() 生成的 jQuery 對象上,所有使用?new jQuery.fn.init()?生成的對象也能夠訪問到?jQuery.fn 上的所有原型方法。
3)也就是實例化方法存在這么一個關系鏈??
- jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;
- new jQuery.fn.init() 相當于 new jQuery() ;
- var obj1 = jQuery() 返回的是 new jQuery.fn.init(),var obj2 = new jQuery(),obj1和obj2是相當的,所以我們可以無 new 實例化 jQuery 對象。
?
3、jQuery.fn.extend 與 jQuery.extend
jQuey 內部用extend 方法來擴展靜態方法或實例方法, jQuery 插件開發的時候也會用到它。
1)jQuery.extend(object)?為擴展 jQuery 類本身,為類添加新的靜態方法;
2)jQuery.fn.extend(object)?給 jQuery 對象添加實例方法,也就是通過這個 extend 添加的新方法,實例化的 jQuery 對象都能使用,因為它是掛載在 jQuery.fn 上的方法(jQuery.fn = jQuery.prototype )。?
1)jQuery.extend(): 把兩個或者更多的對象合并到第一個當中,
2)jQuery.fn.extend():把對象掛載到 jQuery 的 prototype 屬性,來擴展一個新的 jQuery 實例方法。
也就是說,使用?jQuery.extend() 拓展的靜態方法,我們可以直接使用 $.xxx 進行調用(xxx是拓展的方法名),
而使用?jQuery.fn.extend()?拓展的實例方法,需要使用 $().xxx 調用。
jQuery.fn.extend({a: function() {console.log('使用$(xx).xxx調用');} });jQuery.extend({b: '使用$.xxx調用' })$().a(); $.b;需要注意的是這一句?jQuery.extend = jQuery.fn.extend = function() {} ,也就是 jQuery.extend 的實現和 jQuery.fn.extend 的實現共用了同一個方法,但是為什么能夠實現不同的功能了,這就要歸功于 Javascript 強大(怪異?)的 this 了。
1)在?jQuery.extend() 中,this 的指向是 jQuery 對象(或者說是 jQuery 類),所以這里擴展在 jQuery 上;
2)在?jQuery.fn.extend() 中,this 的指向是?fn 對象,前面有提到 jQuery.fn = jQuery.prototype ,也就是這里增加的是原型方法,也就是對象方法。
?
4、jQuery 的鏈式調用及回溯
只需要在要實現鏈式調用的方法的返回結果里,返回 this ,就能夠實現鏈式調用了。
var s = {a: function() {console.log(1);return this;},b: function() {console.log(2);return this;},c: function() {console.log(3);return this;} };s.a().b().c();jQuery 完整的鏈式調用、增棧、回溯通過?return this?、?return this.pushStack()?、return this.prevObject?實現
jQuery.fn = jQuery.prototype = {// 將一個 DOM 元素集合加入到 jQuery 棧// 此方法在 jQuery 的 DOM 操作中被頻繁的使用, 如在 parent(), find(), filter() 中// pushStack() 方法通過改變一個 jQuery 對象的 prevObject 屬性來跟蹤鏈式調用中前一個方法返回的 DOM 結果集合// 當我們在鏈式調用 end() 方法后, 內部就返回當前 jQuery 對象的 prevObject 屬性pushStack: function (elems) {// 構建一個新的jQuery對象,無參的 this.constructor(),只是返回引用this// jQuery.merge 把 elems 節點合并到新的 jQuery 對象// this.constructor 就是 jQuery 的構造函數 jQuery.fn.init,所以 this.constructor() 返回一個 jQuery 對象// 由于 jQuery.merge 函數返回的對象是第二個函數附加到第一個上面,所以 ret 也是一個 jQuery 對象,這里可以解釋為什么 pushStack 出入的 DOM 對象也可以用 CSS 方法進行操作var ret = jQuery.merge(this.constructor(), elems);// 給返回的新 jQuery 對象添加屬性 prevObject// 所以也就是為什么通過 prevObject 能取到上一個合集的引用了ret.prevObject = this;ret.context = this.context;// Return the newly-formed element setreturn ret;},// 回溯鏈式調用的上一個對象end: function () {// 回溯的關鍵是返回 prevObject 屬性// 而 prevObject 屬性保存了上一步操作的 jQuery 對象集合return this.prevObject || this.constructor(null);},// 取當前 jQuery 對象的第 i 個eq: function (i) {// jQuery 對象集合的長度var len = this.length,j = +i + (i < 0 ? len : 0);// 利用 pushStack 返回return this.pushStack(j >= 0 && j < len ? [this[j]] : []);} }總的來說,
1)end() 方法返回 prevObject 屬性,這個屬性記錄了上一步操作的 jQuery 對象合集;
2)而 prevObject 屬性由 pushStack() 方法生成,該方法將一個 DOM 元素集合加入到 jQuery 內部管理的一個棧中,通過改變 jQuery 對象的 prevObject 屬性來跟蹤鏈式調用中前一個方法返回的 DOM 結果集合
3)當我們在鏈式調用 end() 方法后,內部就返回當前 jQuery 對象的 prevObject 屬性,完成回溯。
?
5、jQuery方法的重載
// 獲取 title 屬性的值 $('#id').attr('title'); // 設置 title 屬性的值 $('#id').attr('title','jQuery'); function addMethod(obj, name, f) {var old = obj[name];obj[name] = function () {if (f.length === arguments.length) {return f.apply(this, arguments);} else {return old.apply(this, arguments);}} }var people = {name: ["張三", "李四", "王五"] };var find0 = function () {return this.name; };var find1 = function (name) {var arr = this.name;for (var i = 0; i < arr.length; i++) {if (arr[i] === name) {return arr[i] + "在" + i + "位";}} }function test(a) {console.log('形參個數', test.length); // 1console.log('實參個數', arguments.length); // 0 }test();addMethod(people, 'find', find0); addMethod(people, 'find', find1); console.log(people.find("王五"));?
6、jQuery 變量沖突處理
當需要處理沖突的時候,調用靜態方法 noConflict(),讓出變量的控制權
(function(window, undefined) {var// Map over jQuery in case of overwrite// 設置別名,通過兩個私有變量映射了 window 環境下的 jQuery 和 $ 兩個對象,以防止變量被強行覆蓋_jQuery = window.jQuery,_$ = window.$;jQuery.extend({// noConflict() 方法讓出變量 $ 的 jQuery 控制權,這樣其他腳本就可以使用它了// 通過全名替代簡寫的方式來使用 jQuery// deep -- 布爾值,指示是否允許徹底將 jQuery 變量還原(移交 $ 引用的同時是否移交 jQuery 對象本身)noConflict: function(deep) {// 判斷全局 $ 變量是否等于 jQuery 變量// 如果等于,則重新還原全局變量 $ 為 jQuery 運行之前的變量(存儲在內部變量 _$ 中)if (window.$ === jQuery) {// 此時 jQuery 別名 $ 失效window.$ = _$;}// 當開啟深度沖突處理并且全局變量 jQuery 等于內部 jQuery,則把全局 jQuery 還原成之前的狀況if (deep && window.jQuery === jQuery) {// 如果 deep 為 true,此時 jQuery 失效window.jQuery = _jQuery;}// 這里返回的是 jQuery 庫內部的 jQuery 構造函數(new jQuery.fn.init())// 像使用 $ 一樣盡情使用它吧return jQuery;}}) }(window)?
那么讓出了這兩個符號之后,是否就不能在我們的代碼中使用 jQuery 或者呢 $ 呢?莫慌,還是可以使用的:
// 讓出 jQuery 、$ 的控制權不代表不能使用 jQuery 和 $ ,方法如下: var query = jQuery.noConflict(true);(function($) {// 插件或其他形式的代碼,也可以將參數設為 jQuery })(query);// ... 其他用 $ 作為別名的庫的代碼7、短路表達式
var a = 1, b = 0, c = 3;var foo = a && b && c, // 0 ,相當于 a && (b && c)bar = a || b || c; // 11、在 Javascript 的邏輯運算中,0、""、null、false、undefined、NaN 都會判定為 false ,而其他都為 true ;
2、因為 Javascript 的內置弱類型域 (weak-typing domain),所以對嚴格的輸入驗證這一點不太在意,即便使用 && 或者 ||?運算符的運算數不是布爾值,仍然可以將它看作布爾運算。雖然如此,還是建議如下:
if(foo){ ... } //不夠嚴謹 if(!!foo){ ... } //更為嚴謹,!!可將其他類型的值轉換為boolean類型8、$.trim
let core_version = "1.19.2";let core_Trim = core_version.trim;function trim(data) {return core_Trim.call(data); } console.log(trim(" abds ")); // 源碼 core_version = "1.9.0", core_trim = core_version.trim, rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,trim: core_trim && !core_trim.call("\uFEFF\xA0") ?function( text ) {return text == null ?"" :core_trim.call( text );} :// Otherwise use our own trimming functionalityfunction( text ) {return text == null ?"" :( text + "" ).replace( rtrim, "" );}var core_trim = String.prototype.trim; if (core_trim && !core_trim.call("\uFEFF\xA0")) 相當于: if (String.prototype.trim && "\uFEFF\xA0".trim() !== "") 高級的瀏覽器已經支持原生的String的trim方法,但是jQuery還為了避免它沒法解析全角空白,所以加多了一個判斷:"\uFEFF\xA0".trim() !== ""
\uFEFF是utf8的字節序標記,詳見:字節順序標記?"\xA0"是全角空格 如果以上條件成立了,那就直接用原生的trim函數就好了,展開也即是:
$.trim = function( text ) {return text == null ?"" :text.trim(); }如果上述條件不成立了,那jQuery就自己實現一個trim方法:
$.trim = function( text ) {return text == null ?"" :( text + "" ).replace( rtrim, "" ); }?
?
參考文章:
【深入淺出jQuery】源碼淺析--整體架構
?
?
總結
以上是生活随笔為你收集整理的jquery 这些小技巧你懂吗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决CMU-15-213-ICS的部分l
- 下一篇: python 计算程序剩余时间的小程序