摘要:惰性載入表示函數執行的分支只會發生一次,這里有兩種解決方式。插件,可以讓回調函數在頁面結構加載完成后再運行。
前端基本功-示例代碼 (一) 點這里
前端基本功-示例代碼 (二) 點這里
var xhr = new XMLHttpRequest(); // 聲明一個請求對象 // 前端設置是否帶cookie xhr.withCredentials = true; xhr.open("GET", "xxxx"); //xhr.open("post", "http://www.domain2.com:8080/login", true); // 如何設置請求頭? xhr.setRequestHeader(header, value); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ // readyState 4 代表已向服務器發送請求 if(xhr.status === 200){ // status 200 代表服務器返回成功 console.log(xhr.responseText); // 這是返回的文本 } else{ console.log("Error: "+ xhr.status); // 連接失敗的時候拋出錯誤 } } } xhr.send(null); //xhr.send("user=admin"); // get方法 send null(亦或者不傳,則直接是傳遞 header) ,post 的 send 則是傳遞值2.jsonp 1.)原生實現:
服務端返回如下(返回時即執行全局函數):
onBack({"status": true, "user": "admin"})2.)jquery ajax:
$.ajax({ url: "http://www.domain2.com:8080/login", type: "get", dataType: "jsonp", // 請求方式為jsonp jsonpCallback: "onBack", // 自定義回調函數名 data: {} });3.)vue.js:
this.$http.jsonp("http://www.domain2.com:8080/login", { params: {}, jsonp: "onBack" }).then((res) => { console.log(res); })4.)npm包jsonp:
npm install jsonp --save
import originJSONP from "jsonp" //引入jsonp //進行封裝并export export default function jsonp(url,data,option) { url += (url.indexOf("?")<0? "?" : "&")+param(data) return new Promise((resolve,reject)=>{ originJSONP(url,option,(err,data)=>{ if(!err){ resolve(data) }else{ reject(err) } }) }) } //對data進行處理,并encodeURIComponent()進行轉碼。 function param(data) { let url = "" for(var k in data) { let value = data[k] !== undefined? data[k] : "" url += "&" + k + "=" + encodeURIComponent(value) } return url ? url.substring(1) : "" }
本節參考文章: vue項目中jsonp跨域獲取qq音樂首頁推薦
3.實現一個簡單的PromisePromise對象調用
let p =new Promise(function(resolve, reject){ if(/* 異步操作成功 */){ resolve(data) }else{ reject(err) } })
p.then((res)=>{ console.log(res) },(err)=>{ console.log(err) })
實現一個簡單的Promise
function Promise(fn){ var status = "pending" function successNotify(){ status = "fulfilled"http://狀態變為fulfilled toDoThen.apply(undefined, arguments)//執行回調 } function failNotify(){ status = "rejected"http://狀態變為rejected toDoThen.apply(undefined, arguments)//執行回調 } function toDoThen(){ setTimeout(()=>{ // 保證回調是異步執行的 if(status === "fulfilled"){ for(let i =0; i< successArray.length;i ++) { successArray[i].apply(undefined, arguments)//執行then里面的回掉函數 } }else if(status === "rejected"){ for(let i =0; i< failArray.length;i ++) { failArray[i].apply(undefined, arguments)//執行then里面的回掉函數 } } }) } var successArray = [] var failArray = [] fn.call(undefined, successNotify, failNotify) return { then: function(successFn, failFn){ successArray.push(successFn) failArray.push(failFn) return undefined // 此處應該返回一個Promise } } }
解題思路:Promise中的resolve和reject用于改變Promise的狀態和傳參,then中的參數必須是作為回調執行的函數。因此,當Promise改變狀態之后會調用回調函數,根據狀態的不同選擇需要執行的回調函數。
本節參考文章:面向面試題和實際使用談promise
示例2
const PENDING = "pending"; //等待 const FULFILLED = "fulfilled"; //已完成 const REJECTED = "rejected"; // 已拒絕 function Promise(executor) { let self = this; self.status = PENDING; self.value; self.reason; function resolve(value) { if (self.status === PENDING) { self.status = FULFILLED; self.value = value; } } function reject(reason) { if (self.status === PENDING) { self.status = REJECTED; self.reason = reason; } } try { // 規范提到,執行器拋異常會reject executor(resolve, reject); } catch(e) { reject(e) } } // then方法實現 Promise.prototype.then = function (onFulfilled, onRjected) { let self = this; /** * onFulfilled 和 onRejected 都是可選參數。 * 如果 onFulfilled 不是函數,其必須被忽略 * 如果 onRejected 不是函數,其必須被忽略 */ onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function(value) { return value; }; onRjected = typeof onRjected === "function" ? onRjected : function(reason) { throw reason; } if (self.status === FULFILLED) { onFulfilled(self.value); } if (self.status === REJECTED) { onRjected(self.reason); } }
本節參考文章:Javascript Promise學習過程總結
4.閉包var fn = function() { var divs = document.querySelectorAll("div"); for (var i = 0; i < 3; i++) { divs[i].onclick = (function(i) { return function() { alert(i); }; })(i); } }; fn();
或者如下的寫法:
var fn = function() { var divs = document.querySelectorAll("div"); for (var i = 0; i < 3; i++) { (function(i) { divs[i].onclick = function() { alert(i); }; })(i); } }; fn();
for (var i = 0; i < 3; i++) { setTimeout((function(i) { return function() { console.log(i); }; })(i), 0); console.log(i); }5.事件代理
事件代理(Event Delegation),又稱之為事件委托。是 JavaScript 中常用綁定事件的常用技巧。“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素擔當事件監聽的職務。事件代理的原理是DOM元素的事件冒泡。
減少事件注冊,節省內存占用,提高性能
可以實現當新增子對象時無需再次對其綁定
6.封裝dom查詢(面向對象)添加繪畫散步靜坐
function Elem(id){ this.elem = document.getElementById(id) } Elem.prototype.html = function(val){ var elem = this.elem if(val) { elem.innerHTML = val return this //鏈式 } else { return elem.innerHTML } } Elem.prototype.on = function(type,fn){ var elem = this.elem elem.addEventListener(type, fn) return this //鏈式 } //調用 var div = new Elem("id") div.html("7.DOM劫持hello
").on("click",function(){ console.log("suceess") })
function nodeToFragment (node) { var flag = document.createDocumentFragment(); var child; // 首先,所有表達式必然會返回一個值,賦值表達式亦不例外 // 理解了上面這一點,就能理解 while (child = node.firstChild) 這種用法 // 其次,appendChild 調用以后 child 會從原來 DOM 中移除 // 所以,第二次循環時,node.firstChild 已經不再是之前的第一個子元素了 while (child = node.firstChild) { flag.appendChild(child); // 將子節點劫持到文檔片段中 } return flag }8.添加calssName
// 為元素添加類名 export function addClass(el, className) { // 先判斷一下元素是否含有需要添加的類名,有則直接 return if(hasClass(el, className)) { return } // 把該元素含有的類名以空格分割 let newClass = el.className.split(" ") // 把需要的類名 push 進來 newClass.push(className) // 最后以空格拼接 el.className = newClass.join(" ") } // 判斷是否有要查看的 className,有則返回true,否則返回 false export function hasClass(el, className) { let reg = new RegExp("(^|s)" + className + "(s|$)") return reg.test(el.className) }9.自動添加游覽器前綴
let elementStyle = document.createElement("div").style // 主流瀏覽器內核 let vendor = (() => { let transfromNames = { webkit: "webkitTransform", Moz: "MozTransform", ms: "msTransform", O: "OTransform", standard: "transform" } for(let key in transfromNames) { if(elementStyle[transfromNames[key]] !== undefined) { return key } } return false })() // 添加樣式的瀏覽器前綴 export function prefixStyle(style) { if(vendor === false) { return false } if(vendor === "standard") { return style } return vendor + style.charAt(0).toUpperCase() + style.substr(1) }10.圖片懶加載
定義:延遲加載也稱為惰性加載,即在長網頁中延遲加載圖像。用戶滾動到它們之前,視口外的圖像不會加載。這與圖像預加載相反,在長網頁上使用延遲加載將使網頁加載更快。在某些情況下,它還可以幫助減少服務器負載。
Lazyload 1
jQuery的error事件
$("img").error(function(){ $(this).attr("src","http://ww.jpg"); });
jQuery的one綁定
使用onerror或者jQuery的error事件時,如果默認圖片也發生加載失敗,則會形成死循環,最好的辦法是使用one綁定事件,只執行一次$("img").one("error", function(e){ $(this).attr("src", "http://ww.jpg"); });12.圖片按比例響應式縮放、裁剪html部分:
css部分:
.zoomImage{ width:100%; height:0; padding-bottom: 100%; overflow:hidden; //padding為百分比的時候根據他父層的寬度來進行計算 background-position: center center; background-repeat: no-repeat; -webkit-background-size:cover; -moz-background-size:cover; //把背景圖像擴展至完全覆蓋背景區域 background-size:cover; }總結:你所需要的比例,就是width與padding-bottom的比例
用的時候,直接把.zoomImage當成img標簽來用就可以了。思維擴展
很多輪播的插件本來是響應式的, 但可能有兩個問題:
1.這個輪播圖你必須要給他一個高度,但高度不是固定死的,是需要按比例的...
2.輪播圖里的圖片不是需要的比例...
所以我們可以用剛剛上面的padding方法
拿swiper輪播圖插件舉例優化前
優化后
本節詳細:如何讓圖片按比例響應式縮放、并自動裁剪的css技巧
13.選項卡切換html的結構和樣式:
111222333//過程式的編程思想 window.onload=function(){ //獲取元素 var oParent=document.getElementById("div1"); var btns=oParent.getElementsByTagName("button"); var divs=oParent.getElementsByTagName("div"); //通過循環給每個btn添加點擊事件 for (var i = 0; i < btns.length; i++) { btns[i].index=i;//存儲當前btn的下標 btns[i].onclick=function(){ for (var i = 0; i < btns.length; i++) { btns[i].className=""; divs[i].style.display="none"; } this.className="active"; divs[this.index].style.display="block";//讓對應當前btn的div顯示 } } }//面向對象 window.onload = function(){ var t1=new Tab(); t1.init(); }; function Tab() { this.btns=oParent.getElementsByTagName("button"); this.divs=oParent.getElementsByTagName("div"); } Tab.prototype.init=function(){ var This=this; for (var i = 0; i < this.btns.length; i++) { this.btns[i].index=i; this.btns[i].onclick=function(){ This.change(this); } } } Tab.prototype.change=function(btn) { for (var i = 0; i < this.btns.length; i++) { this.btns[i].className=""; this.divs[i].style.display="none"; } btn.className="active"; this.divs[btn.index].style.display="block"; };14.元素拖拽#div1{ width: 100px; height: 100px; background: red; position: absolute; }//過程式的編程思想 window.onload=function(){ var oDiv=document.getElementById("div1"); var disX=0; var disY=0; oDiv.onmousedown=function(ev){ var ev=ev || window.event; disX=ev.clientX-oDiv.offsetLeft; disY=ev.clientY-oDiv.offsetTop; document.onmousemove=function(ev){ var ev=ev || window.event; oDiv.style.left=ev.clientX-disX+"px"; oDiv.style.top=ev.clientY-disY+"px"; }; document.onmouseup=function(){ document.onmousemove=null; document.onmouseup=null; } return false; } }//面向對象 window.onload = function() { var d1 = new Drag("div1"); d1.init(); }; function Drag(id) { this.oDiv = document.getElementById(id); this.disX = 0; this.disY = 0; } Drag.prototype.init = function() { var This = this; this.oDiv.onmousedown = function(ev) { var ev = ev || window.event; This.fnDown(ev); return false; }; }; Drag.prototype.fnDown = function(ev) { var This = this; this.disX = ev.clientX - this.oDiv.offsetLeft; this.disY = ev.clientY - this.oDiv.offsetTop; document.onmousemove = function(ev) { var ev = ev || window.event; This.fnMove(ev); }; document.onmouseup = function() { This.fnUp(); } }; Drag.prototype.fnMove = function(ev) { this.oDiv.style.left = ev.clientX - this.disX + "px"; this.oDiv.style.top = ev.clientY - this.disY + "px"; }; Drag.prototype.fnUp = function() { document.onmousemove = null; document.onmouseup = null; };15.函數節流(throttle)//fn 要執行的函數 //delay 延遲 //atleast 在time時間內必須執行一次 function throttle(fn, delay, atleast) { var timeout = null, startTime = new Date(); return function() { var curTime = new Date(); clearTimeout(timeout); // 如果達到了規定的觸發時間間隔,觸發 handler if(curTime - startTime >= atleast) { fn(); startTime = curTime; }else { // 沒達到觸發間隔,重新設定定時器 timeout = setTimeout(fn, delay); } } } // 實際想綁定在 scroll 事件上的 handler function lazyload(event) { console.log("觸發了") }// 采用了節流函數 window.addEventListener("scroll",throttle(lazyload,500,1000));16.函數去抖(debounce)// debounce函數用來包裹我們的事件 function debounce(fn, delay) { // 持久化一個定時器 timer let timer = null; return function() { // 如果事件被觸發,清除 timer 并重新開始計時 clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } } // 實際想綁定在 scroll 事件上的 handler function lazyload(event) { console.log("觸發了") } // 采用了去抖函數 window.addEventListener("scroll",debounce(lazyload,500));17.分時函數如果一次獲得了很多數據(比如有10W數據),然后在前端渲染的時候會卡到爆,所以在處理這么多數據的時候,我們可以選擇分批進行。
function timeChunk(data, fn, count = 1, wait) { let obj, timer; function start() { let len = Math.min(count, data.length); for (let i = 0; i < len; i++) { val = data.shift(); // 每次取出一個數據,傳給fn當做值來用 fn(val); } } return function() { timer = setInterval(function() { if (data.length === 0) { // 如果數據為空了,就清空定時器 return clearInterval(timer); } start(); }, wait); // 分批執行的時間間隔 } } // 測試用例 let arr = []; for (let i = 0; i < 100000; i++) { // 這里跑了10萬數據 arr.push(i); } let render = timeChunk(arr, function(n) { // n為data.shift()取到的數據 let div = document.createElement("div"); div.innerHTML = n; document.body.appendChild(div); }, 8, 20); render();參考文章:高階函數,你怎么那么漂亮呢!
18.惰性載入函數假如你要寫一個函數,里面有一些判斷語句
function foo(){ if(a != b){ console.log("aaa") }else{ console.log("bbb") } }如果你的a和b是不變的,那么這個函數不論執行多少次,結果都是不變的,但是每次執行還要進行if判斷,這就造成了不必要的浪費。
惰性載入表示函數執行的分支只會發生一次,這里有兩種解決方式。// 常見的例子 if (window.addEventListener) { ele.addEventListener(type, fn, false); } else if (window.attachEvent) { ele.attachEvent("on" + type, fn); }在函數被調用時再處理函數
function foo(){ if(a != b){ foo = function(){ console.log("aaa") } }else{ foo = function(){ console.log("bbb") } } return foo(); }這樣進入每個分支后都會對foo進行賦值,覆蓋了之前的函數,之后每次調用foo就不會再執行if判斷
在聲明函數時就指定適當的函數
var foo = (function foo(){ if(a != b){ return function(){ console.log("aaa") } }else{ return function(){ console.log("bbb") } } })();本節參考文章:JS高級技巧(簡潔版)
19.實現once函數function test(){ alert("hello"); } var once = function (fn) { var isFirst = true; return function () { if (isFirst) { isFirst = !isFirst; fn(); } }; }; once(test); once(test);20.requirejs架子require.js的誕生,就是為了解決這兩個問題:
實現js文件的異步加載,避免網頁失去響應;
管理模塊之間的依賴性,便于代碼的編寫和維護。
/** 網頁中引入require.js及main.js **/ /** main.js 入口文件/主模塊 **/ // 首先用config()指定各模塊路徑和引用名 require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", //實際路徑為js/lib/jquery.min.js "underscore": "underscore.min", } }); // 執行基本操作 require(["jquery","underscore"],function($,_){ // some code here });引用模塊的時候,我們將模塊名放在[]中作為reqiure()的第一參數;如果我們定義的模塊本身也依賴其他模塊,那就需要將它們放在[]中作為define()的第一參數。
// 定義math.js模塊 define(function () { var basicNum = 0; var add = function (x, y) { return x + y; }; return { add: add, basicNum :basicNum }; }); // 定義一個依賴underscore.js的模塊 define(["underscore"],function(_){ var classify = function(list){ _.countBy(list,function(num){ return num > 30 ? "old" : "young"; }) }; return { classify :classify }; }) // 引用模塊,將模塊放在[]內 require(["jquery", "math"],function($, math){ var sum = math.add(10,20); $("#sum").html(sum); });加載非規范的模塊
理論上,require.js加載的模塊,必須是按照AMD規范、用define()函數定義的模塊。但是實際上,雖然已經有一部分流行的函數庫(比如jQuery)符合AMD規范,更多的庫并不符合。那么,require.js是否能夠加載非規范的模塊呢?這樣的模塊在用require()加載之前,要先用require.config()方法,定義它們的一些特征。舉例來說,underscore和backbone這兩個庫,都沒有采用AMD規范編寫。如果要加載它們的話,必須先定義它們的特征。require.config({ shim: { "underscore":{ exports: "_" }, "backbone": { deps: ["underscore", "jquery"], exports: "Backbone" } } });require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外,還有一個shim屬性,專門用來配置不兼容的模塊。具體來說,每個模塊要定義(1)exports值(輸出的變量名),表明這個模塊外部調用時的名稱;(2)deps數組,表明該模塊的依賴性。比如,jQuery的插件可以這樣定義:
shim: { "jquery.scroll": { deps: ["jquery"], exports: "jQuery.fn.scroll" } }require.js插件
require.js還提供一系列插件,實現一些特定的功能。domready插件,可以讓回調函數在頁面DOM結構加載完成后再運行。
require(["domready!"], function (doc){ // called once the DOM is ready });
text和image插件,則是允許require.js加載文本和圖片文件。define([ "text!review.txt", "image!cat.jpg" ], function(review,cat){ console.log(review); document.body.appendChild(cat); } );類似的插件還有json和mdown,用于加載json文件和markdown文件。
本節參考文章:require.js的用法
華米科技 招前端聯系: 于夢中(wx:tsw0618) 內推,備注來意,簡歷請甩 weihongjie@huami.com
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/53553.html
摘要:基本結構語言中,一個頁面是由四個部分組成文檔聲明標簽對標簽對標簽對圖示文檔聲明這是一個文檔聲明,表示這是一個頁面。標簽標簽表示頁面內容的范圍。 HTML HTML ...
摘要:可見對一個頁面正確渲染很重要。和標簽對用于標識頁面的頭部區域,和之間的內容都屬于頭部區域中的內容。是一個輔助性標簽,對頁面可以進行很多方面的特性的設置。當頁面沒有設置字符集時,瀏覽器會使用默認的字符編碼顯示。 showImg(https://segmentfault.com/img/bVbjNkI?w=900&h=383); 往期回顧 在 1.2 節介紹 HTML 語言時講到:HTML...
摘要:前端基本功示例代碼一點這里前端基本功示例代碼二點這里一像素偽類實現對于老項目,有沒有什么辦法能兼容的尷尬問題了,個人認為偽類是比較完美的方法了。 前端基本功-示例代碼 (一) 點這里前端基本功-示例代碼 (二) 點這里 1.一像素 偽類 + transform 實現對于老項目,有沒有什么辦法能兼容1px的尷尬問題了,個人認為偽類+transform是比較完美的方法了。 原理是把原先元素...
摘要:前端基本功示例代碼一點這里前端基本功示例代碼二點這里一像素偽類實現對于老項目,有沒有什么辦法能兼容的尷尬問題了,個人認為偽類是比較完美的方法了。 前端基本功-示例代碼 (一) 點這里前端基本功-示例代碼 (二) 點這里 1.一像素 偽類 + transform 實現對于老項目,有沒有什么辦法能兼容1px的尷尬問題了,個人認為偽類+transform是比較完美的方法了。 原理是把原先元素...
摘要:后代選擇器后代選擇器又稱包含選擇器,用于選擇指定元素的后代元素。這些選擇器既可以是基本選擇器,也可以是一個復合選擇器。注意元素選擇器及和屬性選擇器之間沒有空格。 showImg(https://segmentfault.com/img/bVblJEJ?w=900&h=383); 復合選擇器是通過基本選擇器進行組合后構成的,常用的復合選擇器有: 交集選擇器 并集選持器 后代選擇器 子元...
摘要:惰性載入表示函數執行的分支只會發生一次,這里有兩種解決方式。插件,可以讓回調函數在頁面結構加載完成后再運行。 前端基本功-示例代碼 (一) 點這里前端基本功-示例代碼 (二) 點這里 1.ajax var xhr = new XMLHttpRequest(); // 聲明一個請求對象 // 前端設置是否帶cookie xhr.withCredentials = true; xhr....
閱讀 1456·2021-09-02 13:57
閱讀 1870·2019-08-30 15:55
閱讀 2407·2019-08-30 15:54
閱讀 2241·2019-08-30 15:44
閱讀 2733·2019-08-30 13:18
閱讀 480·2019-08-30 13:02
閱讀 628·2019-08-29 18:46
閱讀 1665·2019-08-29 11:25