有時候我在想jQuery為什么可以直接$操作,可以擁有比原生js更便利的DOM操作,而且只要你想就可以直接鏈式操作下去
核心框架
揭開一萬多行代碼的jQuery核心代碼:
(function(window, undefined) {function jQuery(selector){return new jQuery.fn.init(selector) } jQuery.fn = jQuery.prototype = { init: function () { } } jQuery.fn.init.prototype = jQuery.fn;window.jQuery = window.$ = jQuery; })(window)
閉包結構傳參window
減少內部每次引用window的查詢時間
方便壓縮代碼
閉包結構傳入實參window,然后里面用形參接收
形參undefined
因為ie低版本的瀏覽器可以給undefined賦值成功,所以為了保證undefined的純潔給它一個形參的位置而沒有實參,保證了它一定是undefined
jQuery傳參selector
selector可以是一對標簽,可以是id、類、后代、子代等等,可以是jQuery對象,
jQuery原型對象賦值
方便擴展jQuery的原型方法
return 實例化原型方法init
其實就是為了我們每次使用$不用new $();
為什么jQuery要new自己的原型方法呢,因為不new自己的就要new其他的函數返回,那干嘛不自己利用自己
jQuery原型對象賦值給jQuery原型方法init的原型
因為內部給jQuery原型每擴展一個方法init也會有該方法,是不是很酷炫,init有了那么$()出來的jQuery對象是不是也有啦
給window暴露可利用成員jQuery,$
給window暴露后那么全局都可以直接使用了jQuery和$了
至于為什么有$,因為短啊,當然你也可以每次jQuery()來使用
御用選擇器-Sizzle
Sizzle也是jQuery的根本,當然了你也單獨使用Sizzle
上面說過$(selector)的參數selector可以是id、類、后代、子代等等,可以是jQuery對象,那么咱們每次$一下就可以心想事成的得到我們想要的jQuery對象是怎么辦到的呢,沒錯,就是因為Sizzle,Sizzle封裝了獲取各種dom對象的方法,并且會把他們包裝成jQuery對象
瀏覽器能力測試
Sizzle內部有個support對象,support對象存儲著正則測試瀏覽器能力的結果
對于有能力問題的選擇器使用通用兼容方案解決(繁瑣的判斷代碼)
正則
正則表達式在jQuery中使用的還是比較多的,正則的使用可以很大的提交我們對數據的處理效率
判斷
列如可能是個html標簽,那么直接create一個selector標簽的DOM對象包裝成jQuery對象return出去
列如可能是個id名、類名、標簽名等等,那么直接通過Sizzle獲取到DOM對象包裝成jQuery對象return出去
判斷是在init內部判斷selector的類型,
包裝
我已經說了很多次的包裝了,沒錯,jQuery對象其實也是個偽數組,這也是它的設計巧妙之處,因為用數組存儲數據方便我們去進行更多的數據處理,比如 $("div").css("color": "red") ,那么jQuery會自動幫我們隱式迭代、再給頁面上所有div包含的文字顏色設置為red,簡單粗暴的一行代碼搞定簡直是程序猿的最愛
對外擴展-extend
jQuery核心的結構處理完畢之后基本上就可以對外使用了,但是我們知道我們是可以基于jQuery來實現插件的,包括jQuery自己可擴展性也必須要求他要對外提供一個接口方便進行二次開發,所以有了extend方法
簡單的extend就是混入,列子:
function extend(obj) { var k; for(k in obj) { this[k] = obj[k]; } } Baiya.extend = extend; Baiya.fn.extend = extend;
對靜態方法的和實例方法的擴展都要有,比如each方法,可以$.each來使用,也可以是$("div").each來使用
之后jQuery一些方法都是基于extend來擴展的,當然我們自己也可以基于jQuery擴展方法
DOM操作
DOM操作也是jQuery的一大特點,因為它太好用了,包含了我們所能想到的所有使用場景,完善了增刪查改常用的方法
jQuery獲取和設置類的方法如html()/css()/val()等等這些傳參是設置值不傳參是獲取值
##鏈式編程
jQuery是支持鏈式編程的,只要你想你就可以一行代碼寫完所有的功能,這是怎么做到的呢
每一個改變原型鏈的方法都會把當前的this對象保存成他自己的屬性,然后可以調用end方法找到上一級鏈從而方便我們可以進行鏈式操作
事件操作
jQuery的事件操作一般可以通過click類(mouseover/mouseleave等等)和on來使用,但是click類的實現是調用on的
on的實現是對原生的onclick類的處理,因為相同的原生的事件在同一個DOM對象上只能被綁定一次,如果再次綁定會覆蓋掉上一次的,所以jQuery幫我們封裝了事件的存儲,把相同的事件分成一個數組存儲在一個對象里面,然后對數組進行遍歷,依次調用數組里存儲的每個方法
on實現之后會把所有的事件處理字符串處理一下用on來改造一下,如下:
Baiya.each(("onclick,onmousedown,onmouseenter,onmouseleave," + "onmousemove,onmouseout,onmouseover,onmouseup,onfocus," + "onmousewheel, onkeydown,onkeypress,onkeyup,onblur").split(","), function (i, v) { var event = v.slice(2); Baiya.fn[event] = function (callback) { return this.on(event, callback); } });
屬性操作
jQuery也提供給了我們方便的屬性操作,底層就是對原生方法的包裝,處理兼容性問題,如果jQuery不對IE瀏覽器的兼容處理的話,那么它的代碼量可能會縮一半,當然鍋不能全部甩給IE,比如innerText方法火狐是不支持的,但是支持textContent方法,所以jQuery會盡可能的處理這種瀏覽器帶來的差異
樣式操作
基本思想如上
Ajax操作
Ajax可以說是前端的跨越性進步,毫不夸張的說如果沒有Ajax的發展,那么今天的前端可能不叫前端,可能是美工……
Ajax是什么?
在我的理解來看Ajax就是一個方法,這個方法遵循著http協議的規范,我們可以使用這個方法來向服務器請求少量的數據,有了數據之后我們就可以操作DOM來達到局部更新網頁的目的,這是一個非常酷的事情
jQuery的Ajax是基于XMLHttpRequest的封裝,當然了他也有兼容性問題,具體的封裝見我之前的文章 簡單的ajax封裝
具體就是區別get和post請求的區別,get請求的傳參是直接拼接在url結尾,而post請求需要在send()里面傳遞,并且post請求還要設置請求頭setRequestHeader("content-type", "application/x-www-form-urlencoded")
請求后對json或者text或者xml的數據進行處理就可以渲染到頁面了
提到Ajax就不得不提到跨域了
跨域簡單的來說限制了非同源(ip/域名/端口/協議)的數據交互,當然這肯定是極好的,因為如果不限制那么你的網頁別人也可以操作是不是很恐怖
但是有些情況下我們需要調用別人的服務器數據,而且別人也愿意怎么辦呢,程序員是很聰明的,html標簽中img,script,link等一些帶有src屬性的標簽是可以請求外部資源的,img和link得到的數據是不可用的,只有script標簽請求的數據我們可以通過函數來接收,函數的參數傳遞可以是任何類型,所以創建一個函數,來接收,參數就是請求到的數據,而對方的數據也要用該函數來調用就可以實現跨域了
簡單封裝jsonp實現
// url是請求的接口// params是傳遞的參數// fn是回調函數function jsonp(url, params, fn){ // cbName實現給url加上哈希,防止同一個地址請求出現緩存 var cbName = `jsonp_${(Math.random() * Math.random()).toString().substr(2)}`; window[cbName] = function (data) { fn(data); // 獲取數據后移除script標簽 window.document.body.removeChild(scriptElement); }; // 組合最終請求的url地址 var querystring = ''; for (var key in params) { querystring += `${key}=${params[key]}&`; } // 告訴服務端我的回調叫什么 querystring += `callback=${cbName}`; url = `${url}?${querystring}`; // 創建一個script標簽,并將src設置為url地址 var scriptElement = window.document.createElement('script'); scriptElement.src = url; // appendChild(執行) window.document.body.appendChild(scriptElement); }
Animate
封裝的代碼
// element設置動畫的DOM對象// attrs設置動畫的屬性object// fn是回調函數function animate(element, attrs, fn) { //清除定時器 if(element.timerId) { clearInterval(element.timerId); } element.timerId = setInterval(function () { //設置開關 var stop = true; //遍歷attrs對象,獲取所有屬性 for(var k in attrs) { //獲取樣式屬性 對應的目標值 var target = parseInt(attrs[k]); var current = 0; var step = 0; //判斷是否是要修改透明度的屬性 if(k === "opacity") { current = parseFloat( getStyle(element, k)) * 100 || 0; target = target * 100; step = (target - current) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); current += step; element.style[k] = current / 100; //兼容ie element.style["filter"] = "alpha(opacity="+ current +")"; }else if(k === "zIndex") { element.style[k] = target; } else { //獲取任意樣式屬性的值,如果轉換數字失敗,返回為0 current = parseInt(getStyle(element, k)) || 0; step = (target - current) / 10; console.log("current:" + current + " step:" + step); step = step > 0 ? Math.ceil(step) : Math.floor(step); current += step; //設置任意樣式屬性的值 element.style[k] = current + "px"; } if(step !== 0) { //如果有一個屬性的值沒有到達target ,設置為false stop = false; } } //如果所有屬性值都到達target 停止定時器 if(stop) { clearInterval(element.timerId); //動畫執行完畢 回調函數 if(fn) { fn(); } } },30); } //獲取計算后的樣式的值 function getStyle(element, attr) { //能力檢測 if(window.getComputedStyle) { return window.getComputedStyle(element, null)[attr]; }else{ return element.currentStyle[attr]; } }
以上講述這么多來分析jQuery,我相信大家也一定對jQuery有了新的認識和認知,希望大家能有收獲。
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com