對象的深拷貝與淺拷貝的區別:json
淺拷貝的實現:數組
var obj = { age : 18, person : { hobby : "movie", skill : "Java" } } //方法一 function shallowClone(initial) { var obj = {}; for( var i in initial ) { obj[i] = initial[i]; } return obj; } //方法二 var newobj = Object.assign({}, obj); console.log(newobj); var clone = shallowClone(obj); console.log(clone.age); //18 clone.person.skill = "JavaScript"; console.log(obj.person.skill); //JavaScript
深拷貝的實現:瀏覽器
var obj = { age : 18, person : { hobby : "movie", skill : "Java" } } /* 方法一: * 這種方法能正確處理的對象只有 Number, String, Boolean, Array, 扁平對象。 * 即那些可以被 json 直接表示的 數據結構。RegExp對象是沒法經過這種方式深拷貝。 */ function deepCopy(initial) { var obj = {}; obj = JSON.parse(JSON.stringify(initial)); return obj; } var copy = deepCopy(obj); console.log(copy); //方法二 (遞歸拷貝) function deepClone(initial, final) { var obj = final || {}; for( var i in initial ) { var prop = initial[i]; //避免相互引用致使死循環 if( prop === obj ) { continue; } if( typeof prop === 'object' ) { obj[i] = ( prop.constructor === Array ) ? prop : Object.create(prop); }else { obj[i] = prop; } } return obj; } var now = {} deepClone(obj, now); now.person.hobby = "sport"; console.log(obj.person.hobby); //movie
咱們常常會遇到這樣一種情景, 用戶高頻率觸發一些JS事件。可是在必定時間內執行代碼的次數太多每每會致使瀏覽器的性能降低甚至形成卡頓的現象, 因此咱們能夠把js執行代碼的次數控制在合理的範圍內, 在實現相同效果的狀況下使頁面交互變得更加流暢。這就是函數防抖和節流要作的事情。緩存
函數防抖:性能優化
//debounce function debounce(func, context) { clearTimeout(func.setTime); func.setTime = setTimeout(() => { func.call(context); }, 300); } window.onscroll = function() { debounce(doSomething); } function doSomething() { console.log("函數防抖"); //執行一些耗費性能的事件... }
從上面代碼能夠看出函數防抖的核心思想是在調用定時器執行某個函數以前首先清除這個定時器。當函數屢次被調用時, 每一次都會將以前的定時器清除, 即只有在執行函數的請求中止了一段時間以後纔會真正執行函數。服務器
函數節流:數據結構
//throttle function throttle(func, time, context) { let start = Date.now(); return function() { if (Date.now() - start > time && time > 0) { func.call(context); start = Date.now(); } } } window.onscroll = throttle(doSomething, 300); function doSomething() { console.log("函數節流"); //執行一些耗費性能的事件... }
函數節流的思想是設置一個執行函數間隔時間time, 當屢次觸發某個事件時便將執行函數的頻率下降到time。閉包
這樣一來就達到了咱們所想要的效果了。app
/*函數節流的另外一種實現方式*/ var flag = true; function throttle(fn, context) { if(!flag) { return; } flag = false; setTimeout(() => { fn.call(context); flag = true; }, 300) } window.onscroll = function() { throttle(doSomething); } function doSomething() { console.log("函數節流"); //執行一些耗費性能的事件... }
值得注意的是這兩種方法在具體瀏覽器中的運行下效果有所不一樣。函數防抖當觸發頻率太高時函數基本中止執行, 而函數節流則是按照必定的頻率執行js事件。異步
/* @防抖與節流混合版 --- 有第三個參數時爲節流效果, 若沒有則爲防抖效果 --- */ var tdmixer = function(fn, delay, reqDelay, context) { var timer = null; var start; return function() { var args = arguments; var current = +new Date(); clearTimeout(timer); if ( !start ) { start = current; } if ( current - start >= reqDelay ) { fn.apply(context, args); start = current; }else { timer = setTimeout( function() { fn.apply(context, args); }, delay); } } } window.onscroll = tdmixer(doSomething, 100, 300); function doSomething() { console.log("This is a mix version."); //執行一些耗費性能的事件... }
它與函數綁定緊密相關, 用於建立已經設置好了一個或多個參數的函數, 其具體作法時使用一個閉包返回一個函數, 當函數被調用時, 返回的函數還須要設置一些傳入的參數。
柯里化的三個做用 : 1.參數複用 2. 提早返回 3.延遲計算
function curry(fn) { var args = Array.prototype.slice.call(arguments, 1); return function() { var innerargs = Array.prototype.slice.call(arguments); var finalargs = args.concat(innerargs); return fn.apply(null, finalargs); } } function addAll(x,y,z) { return x + y + z; } var excute = curry(addAll,5,10); excute(50); //65
ES5中的bind方法也用到過柯里化, 下面是簡單的函數綁定的實現。
function bind(fn, context) { var args = Array.prototype.slice.call(arguments, 2); return function() { var innerargs = Array.prototype.slice.call(arguments); var finalargs = args.concat(innerargs); return fn.apply(context, finalargs); } } var handler = { message: "PIPI", handleClick(name) { console.log(name + "and" + this.message); } } var excute = bind(handler.handleClick, handler); excute("POP"); //POPandPIPI
顧名思義, 圖片的預加載就是將圖片預先加載到瀏覽器的本地緩存中, 當須要時直接從本地加載圖片到頁面中, 如此一來就很好的提升了用戶的體驗。但缺點是增長了服務器端的開銷。
也叫延遲加載, 即延遲加載圖片或者當符合某些條件時纔開始加載圖片, 它與預加載相反, 其做用是對服務器端的性能優化, 減小請求數或延遲請求數, 從而達到緩解服務器端壓力的效果。
--- preload code ---
//對預加載圖片進行一些回調事件處理 function preLoadImg(url, callback) { var img = new Image(); if ( img.complete ) { //若圖片已經在本地緩存, 則直接調用回調函數 callback.call(img); return; } img.onload = function() { //圖片下載完以後異步調用callback img.onload = null; callback.call(img); } img.src = url; } //大量圖片預加載 var arr = ['pic1.png', 'pic2.png', 'pic3.png']; function fn() { console.log('Do something...') }; function preLoadImages(urls, callback) { var wrap = Array.prototype.slice.call(arguments, 0, 1); var urls = [].concat.apply([], wrap); //將其轉化爲一維數組 for ( var i = 0; i < urls.length; i++) { var img = new Image(); img.onload = function() { callback.call(img); } img.src = urls[i]; } } preLoadImages(arr, fn);
--- lazyload code ---
//懶加載的實現 var lazyload = { //初始化 init() { this.container = document.querySelector('Container'); //獲取容器元素 this.images = this.getImages(); this.update(); this.bindEvent(); }, //獲取圖片 getImages() { var arr = []; var images = this.container.querySelectorAll('img'); images.forEach( (img) => { arr.push(img); }); return arr; }, //加載圖片 update() { if ( !this.images.length ) { return }; var i = this.images.length; for ( i--; i >= 0; i-- ) { if ( this.couldShow(i) ) { this.images[i].src = this.images[i].getAttribute('data-src'); //需事先設置路徑 this.images.splice(i, 1); } } }, //判斷圖片是否在可視區域並賦予src值 couldShow(i) { var img = this.images[i]; scrollTop = document.documentElement.scrollTop || document.body.scrollTop; scrollBottom = scrollTop + document.documentElement.clientHeight; imgTop = this.rectY(img); imgBottom = imgTop + img.offsetHeight; if ( imgBottom < scrollBottom && imgBottom > scrollTop || imgTop > scrollTop && imgTop < scrollBottom ) { return true; }else { return false; } }, //遞歸調用獲取圖片頂部到整個頁面的最頂端的距離 rectY(el) { if ( el.offsetParent ) { return el.offsetTop + this.rectY(el.offsetParent); }else { return el.offsetTop; } }, //事件綁定 bindEvent() { var that = this; that.on(window, "scroll", () => { var fn = tdmixer(that.update, 100, 300, that); //函數節流 fn(); } ) }, //監聽 on(el, type, fn) { if ( el.addEventListener ) { el.addEventListener(type, fn); }else { el.attachEvent("on" + type, fn); } } } lazyload.init(); //上文所給出的混合節流函數 var tdmixer = function(fn, delay, reqDelay, context) { var timer = null; var start; return function() { var args = arguments; var current = +new Date(); clearTimeout(timer); if ( !start ) { start = current; } if ( current - start >= reqDelay ) { fn.apply(context, args); start = current; }else { timer = setTimeout( function() { fn.apply(context, args); }, delay); } } }
從上面的兩段代碼能夠看出, 圖片預加載實現起來要簡單許多, 固然兩種功能都有不少種不一樣的實現方法, 有簡單的也有複雜的, 這都須要根據具體的情景來編寫代碼。預加載一次性就加載須要的圖片到本地儲存從而提升了用戶的體驗卻也加大了服務器端的負擔, 而懶加載則須要根據某些具體的條件來判斷什麼時候向服務器端請求圖片數據, 雖然減小了服務器端的開銷, 但具體實現的步驟也變得更加複雜。因此在實際狀況下二者最好混合使用且用在正確的地方上最爲合適。