摘要:對象是無法通過這種方式深拷貝。這就是函數(shù)防抖和節(jié)流要做的事情。函數(shù)防抖當(dāng)觸發(fā)頻率過高時函數(shù)基本停止執(zhí)行而函數(shù)節(jié)流則是按照一定的頻率執(zhí)行事件。
對象的深淺拷貝
對象的深拷貝與淺拷貝的區(qū)別:
淺拷貝:僅僅復(fù)制對象的引用, 而不是對象本身。
深拷貝:把復(fù)制的對象所引用的全部對象都復(fù)制一遍
淺拷貝的實現(xiàn):
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
深拷貝的實現(xiàn):
var obj = { age : 18, person : { hobby : "movie", skill : "Java" } } /* 方法一: * 這種方法能正確處理的對象只有 Number, String, Boolean, Array, 扁平對象。 * 即那些能夠被 json 直接表示的 數(shù)據(jù)結(jié)構(gòu)。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]; //避免相互引用導(dǎo)致死循環(huán) 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函數(shù)的防抖與節(jié)流
我們經(jīng)常會遇到這樣一種情景, 用戶高頻率觸發(fā)一些JS事件。但是在一定時間內(nèi)執(zhí)行代碼的次數(shù)太多往往會導(dǎo)致瀏覽器的性能下降甚至造成卡頓的現(xiàn)象, 所以我們可以把js執(zhí)行代碼的次數(shù)控制在合理的范圍內(nèi), 在實現(xiàn)相同效果的情況下使頁面交互變得更加流暢。這就是函數(shù)防抖和節(jié)流要做的事情。
函數(shù)防抖:
//debounce function debounce(func, context) { clearTimeout(func.setTime); func.setTime = setTimeout(() => { func.call(context); }, 300); } window.onscroll = function() { debounce(doSomething); } function doSomething() { console.log("函數(shù)防抖"); //執(zhí)行一些耗費性能的事件... }
從上面代碼可以看出函數(shù)防抖的核心思想是在調(diào)用定時器執(zhí)行某個函數(shù)之前首先清除這個定時器。當(dāng)函數(shù)多次被調(diào)用時, 每一次都會將之前的定時器清除, 即只有在執(zhí)行函數(shù)的請求停止了一段時間之后才會真正執(zhí)行函數(shù)。
函數(shù)節(jié)流:
//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("函數(shù)節(jié)流"); //執(zhí)行一些耗費性能的事件... }
函數(shù)節(jié)流的思想是設(shè)置一個執(zhí)行函數(shù)間隔時間time, 當(dāng)多次觸發(fā)某個事件時便將執(zhí)行函數(shù)的頻率降低到time。
這樣一來就達到了我們所想要的效果了。
/*函數(shù)節(jié)流的另一種實現(xiàn)方式*/ 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("函數(shù)節(jié)流"); //執(zhí)行一些耗費性能的事件... }
值得注意的是這兩種方法在具體瀏覽器中的運行下效果有所不同。函數(shù)防抖當(dāng)觸發(fā)頻率過高時函數(shù)基本停止執(zhí)行, 而函數(shù)節(jié)流則是按照一定的頻率執(zhí)行js事件。
/* @防抖與節(jié)流混合版 --- 有第三個參數(shù)時為節(jié)流效果, 若沒有則為防抖效果 --- */ 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."); //執(zhí)行一些耗費性能的事件... }函數(shù)柯里化
它與函數(shù)綁定緊密相關(guān), 用于創(chuàng)建已經(jīng)設(shè)置好了一個或多個參數(shù)的函數(shù), 其具體做法時使用一個閉包返回一個函數(shù), 當(dāng)函數(shù)被調(diào)用時, 返回的函數(shù)還需要設(shè)置一些傳入的參數(shù)。
柯里化的三個作用 : 1.參數(shù)復(fù)用 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方法也用到過柯里化, 下面是簡單的函數(shù)綁定的實現(xiàn)。
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圖片預(yù)加載與懶加載
預(yù)加載:
顧名思義, 圖片的預(yù)加載就是將圖片預(yù)先加載到瀏覽器的本地緩存中, 當(dāng)需要時直接從本地加載圖片到頁面中, 如此一來就很好的提高了用戶的體驗。但缺點是增加了服務(wù)器端的開銷。
懶加載:
也叫延遲加載, 即延遲加載圖片或者當(dāng)符合某些條件時才開始加載圖片, 它與預(yù)加載相反, 其作用是對服務(wù)器端的性能優(yōu)化, 減少請求數(shù)或延遲請求數(shù), 從而達到緩解服務(wù)器端壓力的效果。
--- preload code ---
//對預(yù)加載圖片進行一些回調(diào)事件處理 function preLoadImg(url, callback) { var img = new Image(); if ( img.complete ) { //若圖片已經(jīng)在本地緩存, 則直接調(diào)用回調(diào)函數(shù) callback.call(img); return; } img.onload = function() { //圖片下載完之后異步調(diào)用callback img.onload = null; callback.call(img); } img.src = url; } //大量圖片預(yù)加載 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); //將其轉(zhuǎn)化為一維數(shù)組 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 ---
//懶加載的實現(xiàn) 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"); //需事先設(shè)置路徑 this.images.splice(i, 1); } } }, //判斷圖片是否在可視區(qū)域并賦予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; } }, //遞歸調(diào)用獲取圖片頂部到整個頁面的最頂端的距離 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); //函數(shù)節(jié)流 fn(); } ) }, //監(jiān)聽 on(el, type, fn) { if ( el.addEventListener ) { el.addEventListener(type, fn); }else { el.attachEvent("on" + type, fn); } } } lazyload.init(); //上文所給出的混合節(jié)流函數(shù) 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); } } }
從上面的兩段代碼可以看出, 圖片預(yù)加載實現(xiàn)起來要簡單許多, 當(dāng)然兩種功能都有很多種不同的實現(xiàn)方法, 有簡單的也有復(fù)雜的, 這都需要根據(jù)具體的情景來編寫代碼。預(yù)加載一次性就加載需要的圖片到本地儲存從而提高了用戶的體驗卻也加大了服務(wù)器端的負(fù)擔(dān), 而懶加載則需要根據(jù)某些具體的條件來判斷何時向服務(wù)器端請求圖片數(shù)據(jù), 雖然減少了服務(wù)器端的開銷, 但具體實現(xiàn)的步驟也變得更加復(fù)雜。所以在實際情況下兩者最好混合使用且用在正確的地方上最為合適。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/107442.html
摘要:文章來源詳談防抖和節(jié)流輕松理解函數(shù)節(jié)流和函數(shù)防抖函數(shù)防抖和節(jié)流好啦,今天的小菊花課堂之的防抖與節(jié)流的內(nèi)容就告一段落啦,感各位能耐心看到這里。 前言 陸游有一首《冬夜讀書示子聿》——古人學(xué)問無遺力,少壯工夫老始成。紙上得來終覺淺,絕知此事要躬行。,其中的意思想必大家都能明白,在學(xué)習(xí)或工作中,不斷的印證著這首詩的內(nèi)涵。所以,又有了此篇小菊花文章。 詳解 在前端開發(fā)中,我們經(jīng)常會碰到一些會持...
摘要:文章來源詳談防抖和節(jié)流輕松理解函數(shù)節(jié)流和函數(shù)防抖函數(shù)防抖和節(jié)流好啦,今天的小菊花課堂之的防抖與節(jié)流的內(nèi)容就告一段落啦,感各位能耐心看到這里。 前言 陸游有一首《冬夜讀書示子聿》——古人學(xué)問無遺力,少壯工夫老始成。紙上得來終覺淺,絕知此事要躬行。,其中的意思想必大家都能明白,在學(xué)習(xí)或工作中,不斷的印證著這首詩的內(nèi)涵。所以,又有了此篇小菊花文章。 詳解 在前端開發(fā)中,我們經(jīng)常會碰到一些會持...
摘要:專題系列共計篇,主要研究日常開發(fā)中一些功能點的實現(xiàn),比如防抖節(jié)流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數(shù)組合專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實現(xiàn)模式需求我們需要寫一個函數(shù),輸入,返回。 JavaScript 專題之從零實現(xiàn) jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現(xiàn)一個 jQuery 的 ext...
摘要:基礎(chǔ)防抖我們現(xiàn)在寫一個最基礎(chǔ)的防抖處理標(biāo)記事件也做如下改寫現(xiàn)在試一下,我們會發(fā)現(xiàn)只有我們停止?jié)L動秒鐘的時候,控制臺才會打印出一行隨機數(shù)。 為何要防抖和節(jié)流 有時候會在項目開發(fā)中頻繁地觸發(fā)一些事件,如 resize、 scroll、 keyup、 keydown等,或者諸如輸入框的實時搜索功能,我們知道如果事件處理函數(shù)無限制調(diào)用,會大大加重瀏覽器的工作量,有可能導(dǎo)致頁面卡頓影響體驗;后臺...
摘要:寫在前面專題系列是我寫的第二個系列,第一個系列是深入系列。專題系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 專題系列是我寫的第二個系列,第一個系列是 JavaScript 深入系列。 JavaScript 專題系列共計 20 篇,主要研究日常開發(fā)中一些功能點的實現(xiàn),比如防抖、節(jié)流、去重、類型判斷、拷貝、最值、扁平、柯里...
閱讀 1000·2023-04-25 14:20
閱讀 1876·2021-11-24 10:20
閱讀 3774·2021-11-11 16:55
閱讀 2919·2021-10-14 09:42
閱讀 3472·2019-08-30 15:56
閱讀 1163·2019-08-30 15:55
閱讀 1072·2019-08-30 15:44
閱讀 782·2019-08-29 11:28