前端基本功-示例代碼 (一) 點這裏
前端基本功-示例代碼 (二) 點這裏javascript
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 則是傳遞值
<script> var script = document.createElement('script'); script.type = 'text/javascript'; // 傳參並指定回調執行函數爲onBack script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回調執行函數 function onBack(res) { alert(JSON.stringify(res)); } </script>
服務端返回以下(返回時即執行全局函數):css
onBack({"status": true, "user": "admin"})
$.ajax({ url: 'http://www.domain2.com:8080/login', type: 'get', dataType: 'jsonp', // 請求方式爲jsonp jsonpCallback: "onBack", // 自定義回調函數名 data: {} });
this.$http.jsonp('http://www.domain2.com:8080/login', { params: {}, jsonp: 'onBack' }).then((res) => { console.log(res); })
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音樂首頁推薦html
Promise對象調用前端
let p =new Promise(function(resolve, reject){ if(/* 異步操做成功 */){ resolve(data) }else{ reject(err) } })
p.then((res)=>{ console.log(res) },(err)=>{ console.log(err) })
實現一個簡單的Promisevue
function Promise(fn){ var status = 'pending' function successNotify(){ status = 'fulfilled'//狀態變爲fulfilled toDoThen.apply(undefined, arguments)//執行回調 } function failNotify(){ status = 'rejected'//狀態變爲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改變狀態以後會調用回調函數,根據狀態的不一樣選擇須要執行的回調函數。java
本節參考文章:面向面試題和實際使用談promisenode
示例2jquery
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學習過程總結web
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); }
事件代理(Event Delegation),又稱之爲事件委託。是 JavaScript 中經常使用綁定事件的經常使用技巧。「事件代理」便是把本來須要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。 事件代理的原理是DOM元素的事件冒泡。
<div class="wrap" id="wrap"> <div class="btn" data-type="btn" data-feat="add">添加</div> <div class="btn" data-type="btn" data-feat="delete">繪畫</div> <div class="btn" data-type="btn" data-feat="delete">散步</div> <div class="btn" data-type="btn" data-feat="delete">靜坐</div> </div> <script type="text/javascript"> var n = 0 document.getElementById('wrap').addEventListener('click', function(e) { var target = e.target; var type = target.dataset.type; var feat = target.dataset.feat; if (type == 'btn') { switch (feat) { case 'add': this.innerHTML += `<div class="btn" data-type="btn" data-feat="delete">靜坐${n}</div>` n++ return; case 'delete': target.parentNode.removeChild(target); return; } } }, false); </script>
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('<p>hello</p>').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 }
// 爲元素添加類名 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) }
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) }
定義:延遲加載也稱爲惰性加載,即在長網頁中延遲加載圖像。用戶滾動到它們以前,視口外的圖像不會加載。這與圖像預加載相反,在長網頁上使用延遲加載將使網頁加載更快。在某些狀況下,它還能夠幫助減小服務器負載。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Lazyload 1</title> <style> img { display: block; margin-bottom: 50px; height: 200px; } </style> </head> <body> <img src="images/loading.gif" data-src="images/1.png"> <img src="images/loading.gif" data-src="images/2.png"> <img src="images/loading.gif" data-src="images/3.png"> <img src="images/loading.gif" data-src="images/4.png"> <img src="images/loading.gif" data-src="images/5.png"> <img src="images/loading.gif" data-src="images/6.png"> <img src="images/loading.gif" data-src="images/7.png"> <img src="images/loading.gif" data-src="images/8.png"> <img src="images/loading.gif" data-src="images/9.png"> <img src="images/loading.gif" data-src="images/10.png"> <img src="images/loading.gif" data-src="images/11.png"> <img src="images/loading.gif" data-src="images/12.png"> <!-- <script> var num = document.getElementsByTagName('img').length; var img = document.getElementsByTagName("img"); var n = 0; //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷 lazyload(); //頁面載入完畢加載但是區域內的圖片 window.onscroll = lazyload; function lazyload() { //監聽頁面滾動事件 var seeHeight = document.documentElement.clientHeight; //可見區域高度 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滾動條距離頂部高度 for (var i = n; i < num; i++) { if (img[i].offsetTop < seeHeight + scrollTop) { if (img[i].getAttribute("src") == "images/loading.gif") { img[i].src = img[i].getAttribute("data-src"); } n = i + 1; } } } </script>--> //對比一下上下兩種代碼,一個變量是全局變量,一個是函數的局部做用域, <script> function lazyload() { var images = document.getElementsByTagName('img'); var len = images.length; var n = 0; //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷 return function() { var seeHeight = document.documentElement.clientHeight; var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; for(var i = n; i < len; i++) { if(images[i].offsetTop < seeHeight + scrollTop) { if(images[i].getAttribute('src') === 'images/loading.gif') { images[i].src = images[i].getAttribute('data-src'); } n = n + 1; } } } } var loadImages = lazyload(); loadImages(); //初始化首頁的頁面圖片 window.addEventListener('scroll', loadImages, false); </script> </body> </html>
jQuery
<script> var n = 0, imgNum = $("img").length, img = $('img'); lazyload(); $(window).scroll(lazyload); function lazyload(event) { for (var i = n; i < imgNum; i++) { if (img.eq(i).offset().top < parseInt($(window).height()) + parseInt($(window).scrollTop())) { if (img.eq(i).attr("src") == "default.jpg") { var src = img.eq(i).attr("data-src"); img.eq(i).attr("src", src); n = i + 1; } } } } </script>
<img src="image.png" onError='this.src="http://ww.jpg"' />
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"); });
html部分:
<div class="zoomImage" style="background-image:url(images/test1.jpg)"></div>
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技巧
html的結構和樣式:
<style type="text/css"> #div1 div{ width: 200px; height:200px; border: 1px #000 solid; display: none; } .active{ background: red; } </style> <body> <div id="div1"> <button class="active">1</button> <button>2</button> <button>3</button> <div style="display: block;">111</div> <div>222</div> <div>333</div> </div> </body>
//過程式的編程思想 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'; };
#div1{ width: 100px; height: 100px; background: red; position: absolute; } <body> <div id='div1'></div> </body>
//過程式的編程思想 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; };
//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));
// 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));
若是一次得到了不少數據(好比有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();
參考文章:高階函數,你怎麼那麼漂亮呢!
假如你要寫一個函數,裏面有一些判斷語句
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高級技巧(簡潔版)
function test(){ alert('hello'); } var once = function (fn) { var isFirst = true; return function () { if (isFirst) { isFirst = !isFirst; fn(); } }; }; once(test); once(test);
require.js的誕生,就是爲了解決這兩個問題:
/** 網頁中引入require.js及main.js **/ <script src="js/require.js" data-main="js/main"></script> /** 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