錯誤之處,歡迎指正,持續(xù)更新中。
1. 基礎(chǔ)
1. ==和===的區(qū)別是什么?
console.log(1 == '1'); //true
console.log(1 === '1'); //false
使用===進(jìn)行比較時,如果等號兩邊的數(shù)據(jù)類型不同,直接返回false;
使用==進(jìn)行比較,會先進(jìn)行類型轉(zhuǎn)換,再進(jìn)行比較。
2. 運行下列代碼在控制臺輸出的結(jié)果是?
for(var index = 0; index < 5; index ++){
var result = index + '';
}
console.log(result);
console.log(index);
console.log((1 - 'result') == (1 - 'result'));
輸出結(jié)果為'4'、5和false。
3. "2" + 3 + 4的結(jié)果為?
結(jié)果為"234"。
4. 以下哪些數(shù)據(jù)經(jīng)過fn處理后,返回的是false?
function fn(a) {
return !!a;
}
A. NaN
B. -1
C. ""
D. "0"
答案為A、C選項。
5. 下列代碼的輸出結(jié)果為?
var a = new Boolean(false);
console.log(typeof a)
輸出結(jié)果為object。
6. 怎樣找出x和y哪個數(shù)值最大?
A. Math.ceil(x, y);
B. top(x, y);
C. ceil(x, y);
D. Math.max(x, y);
E. max(x,y);
正確選項為D。
7. 下列結(jié)果為true的表達(dá)式是?
A. null instanceof Object
B. NaN == NaN
C. null == undefined
D. false == undefined
正確選項為C。
8. 將字符串轉(zhuǎn)換為JSON對象的方法是?
A. var obj = JSON.parse(str);
B. var obj = eval('(' + str + ')');
C. var obj = str.parseJSON();
D. 都是。
正確選項為A和B。
9. 如何翻轉(zhuǎn)字符串?
const str = '123456';
const arr = str.split(''); //["1", "2", "3", "4", "5", "6"]
const newArr = arr.reverse(); //["6", "5", "4", "3", "2", "1"]
const newStr = newArr.join('');
console.log(newStr); //654321
10. 如何分割字符串?
-
slice方法
按照索引值來截取字符串,左開右閉區(qū)間。
var str = '123-1234-124';
console.log(str.slice(4, 8)); //1234
-
split方法
var str = '123-1234-124';
console.log(str.split('-')); //["123", "1234", "124"]
按照指定的字符分割字符串。
11. 如何刪除字符串中的空格?
-
split方法和join方法
const str = '1 2 345 6789 ';
const arr = str.split(' '); //["1", "2", "345", "6789", ""]
const newStr = arr.join('');
console.log(newStr); //123456789
- 正則表達(dá)式和
replace方法
const str = '1 2 345 6789 ';
const reg = /\s/g; //匹配到所有空白符
const newStr = str.replace(reg,''); //將空白符替換為空字符串
console.log(newStr); //123456789
-
trim方法
只能去掉字符串首尾兩端的空格。
const str = ' 1 2 345 6789 ';
const newStr = str.trim();
console.log(newStr); //1 2 345 6789
12. 如何返回下列數(shù)組的第一個值?
var myArr = [1, 2, 3, 4, 5];
A. myArr[1];
B. myArr.pop();
C. myArr[0];
D. myArr.unshift();
E. myArr.shift();
正確選項為C、E。
13. 如何區(qū)別數(shù)組和對象?
- 查詢構(gòu)造函數(shù)
const arr = [1,2,3,4,5];
const obj = {
name: 'chris'
}
console.log(arr.__proto__.constructor); //Array()
console.log(obj.__proto__.constructor); //Object()
instanceof
const arr = [1,2,3,4,5];
const obj = {
name: 'chris'
}
console.log(obj instanceof Array); //false
console.log(obj instanceof Object); //true
isPrototypeOf
const arr = [1,2,3,4,5];
const obj = {
name: 'chris'
}
console.log(Array.prototype.isPrototypeOf(arr)); //true
console.log(Array.prototype.isPrototypeOf(obj)); //false
-
isArray靜態(tài)方法
const arr = [1,2,3,4,5];
const obj = {
name: 'chris'
}
console.log(Array.isArray(arr)); //true
console.log(Array.isArray(obj)); //false
14. 如何翻轉(zhuǎn)數(shù)組?
var arr = [1,2,3,4,5];
console.log(arr.reverse()); //[5, 4, 3, 2, 1]
console.log(arr); //[5, 4, 3, 2, 1]
要注意的是,該方法會改變原數(shù)組。
15. 簡要說明splice的用法。
splice方法可以用來刪除數(shù)組中的某些值,或者添加一些值,返回值為被刪除的項目,該方法會改變原數(shù)組。
參數(shù)分別為:
- 刪除的起始位置(負(fù)數(shù)是指從數(shù)組結(jié)尾開始)
- 要刪除的數(shù)量
- 添加的新內(nèi)容
var arr = [0, 1, 2, 3, 4, 5];
arr.splice(0, 2, 1); //[0, 1]
console.log(arr); //[1, 2, 3, 4, 5]
16. 如何實現(xiàn)數(shù)組去重?
-
indexOf和includes
var arr = [1,2,3,4,5,1,5];
var newArr = [];
for (let index = 0; index < arr.length; index++) {
if(newArr.indexOf(arr[index]) == -1){
newArr.push(arr[index]);
}
}
console.log(newArr); //[1, 2, 3, 4, 5]
無論是使用indexOf還是includes,原理都是遍歷舊數(shù)組,然后查找新數(shù)組是否已存在該項,不存在時進(jìn)行push。
SET
var arr = [1,2,3,4,5,1,5];
var newArr = Array.from(new Set(arr));
console.log(newArr); //[1, 2, 3, 4, 5]
17. 如何實現(xiàn)對數(shù)組和對象的深淺拷貝?
- 淺拷貝
var arr = [1,2,3,4,5];
var newArr = arr;
newArr[0] = 0;
console.log(newArr); //[0, 2, 3, 4, 5]
console.log(arr); //[0, 2, 3, 4, 5]
//arr和newArr共用同一塊內(nèi)存。
- 深拷貝
var arr = [1,2,3,4,5];
var newArr = [];
for (let index = 0; index < arr.length; index++) {
newArr.push(arr[index]);
}
newArr[0] = 0;
console.log(newArr); //[0, 2, 3, 4, 5]
console.log(arr); //[1, 2, 3, 4, 5]
18. 如何實現(xiàn)一個遞歸階乘?
function getResult(num) {
if(num === 0) {
return 0;
}
if(num === 1){
return 1;
}else{
return num * getResult(num - 1);
}
}
console.log(getResult(5)); //120
19. touch和click以及mousedown有什么區(qū)別?
- 觸發(fā)點不同
touch(不包括touchstart)事件不一定發(fā)生在所監(jiān)聽的DOM元素上,mouse事件必須發(fā)生在所監(jiān)聽的DOM元素上。
-
touch
touchstart觸發(fā)后,即使鼠標(biāo)移出監(jiān)聽的DOM區(qū)域,touchend、touchmove和touchend依然可以觸發(fā)。 -
mouse、click
mousedown觸發(fā)后,鼠標(biāo)移出監(jiān)聽的DOM區(qū)域,mouseup不會觸發(fā)。click也是如此,按下之后,離開目標(biāo)區(qū)域,抬起鼠標(biāo),click事件不會觸發(fā)。
- 觸發(fā)的先后順序
boxDom.addEventListener('mousedown', () => {
console.log('mousedown發(fā)生了');
})
boxDom.addEventListener('mouseup', () => {
console.log('mouseup發(fā)生了');
})
boxDom.addEventListener('click', () => {
console.log('click發(fā)生了');
})
boxDom.addEventListener('touchstart', () => {
console.log('touchstart發(fā)生了');
})
boxDom.addEventListener('touchend', () => {
console.log('touchend發(fā)生了');
})

綜上,
touch事件的發(fā)生時間是早于mouse事件和click事件的。
20. 事件有哪幾個階段?
- 捕獲階段
事件從最不精確的對象到最精確的對象。
當(dāng)我們在DOM樹上某個節(jié)點設(shè)置并發(fā)生了一些操作后,就會有一個事件觸發(fā),這個事件從Window發(fā)出,到目標(biāo)節(jié)點結(jié)束。 - 目標(biāo)階段
事件經(jīng)過捕獲階段,直至目標(biāo)節(jié)點,捕獲階段結(jié)束,在目標(biāo)節(jié)點觸發(fā)事件,此時就是目標(biāo)階段。 - 冒泡階段
事件從最精確的對象到最不精確的對象。
事件經(jīng)過目標(biāo)階段,由目標(biāo)節(jié)點開始,逐級向上傳遞。
DOM的樹形結(jié)構(gòu)決定了子元素肯定在父元素之中,所以點擊子元素就同時點擊了子元素和父元素,以及父元素的父元素,以此類推,那么子元素的父級上的綁定事件,都會由于子元素的點擊而執(zhí)行。
可以通過調(diào)用stopPropagation方法來阻止事件冒泡。
21. 為什么要使用事件委托?
事件委托是利用了事件冒泡的原理。例如有100個li標(biāo)簽,每一個都需要點擊事件,那么就把這個點擊事件委托給他的父元素ul。
事件委托可以減少重復(fù)代碼量,減少了事件注冊,從而降低了DOM操作,提升性能。其次就是對于新添加的子元素,無需再次綁定事件。
22. 分別說明querySelector和querySelectorAll的作用。
-
querySelector
document.querySelector("p");選中文檔中的第一個p標(biāo)簽元素。
document.querySelector("h2, h3");此時要看文檔中的第一個h2標(biāo)簽和第一個h3標(biāo)簽?zāi)囊粋€在前面,誰在前面就選中誰。 -
querySelectorAll
document.querySelectorAll(".example");選中文檔中所有類名為example的元素。
23. 簡要描述閉包是什么,有什么特性,如何創(chuàng)建一個閉包?
首先,javascript中允許在函數(shù)中聲明函數(shù):
function main() {
function help() {
}
}
存在一種場景,有一個主函數(shù),需要復(fù)雜的運算和邏輯,所以為了后期的維護(hù)性,以及代碼可讀性,會拆分主函數(shù),把一些運算單獨抽離出來封裝成函數(shù),然后這些抽離出來的函數(shù),僅僅是對這個主函數(shù)有用,在這種情況下,我們會把這些抽離出來的函數(shù),寫在主函數(shù)內(nèi)部,可以避免全局對象污染。
這種寫在函數(shù)內(nèi)部的函數(shù)(嵌套函數(shù)),就稱之為閉包函數(shù),例如上述代碼中的help函數(shù)。
function a() {
var num = 10;
function b() { //閉包函數(shù)
console.log(num); //內(nèi)部函數(shù)可以訪問到外部函數(shù)的作用域
}
return b;
}
var fo = a(); //a函數(shù)執(zhí)行,把a(bǔ)的執(zhí)行結(jié)果賦給fo
fo(); //10
所謂閉包,就是這種外部環(huán)境能夠訪問內(nèi)部函數(shù)作用域的變量的現(xiàn)象。
閉包的缺點是會造成內(nèi)存泄露,可以理解為,本來a函數(shù)執(zhí)行完畢要釋放內(nèi)存,但是由于閉包,存放num的內(nèi)存不能被釋放,那么如果有新的數(shù)據(jù)就不能存進(jìn)來,就會被泄露。
24. 請完善下列代碼,使控制臺輸出chris@123。
function Person(name) {
this.name = name;
}
var person = new Person('chris');
person.run();
添加代碼:
Person.prototype.run = function () {
console.log(`${this.name}@123`);
}
這里考察的是原型的問題。
25. 什么是原型和原型鏈?
- 原型
對象都有__proto__這個屬性,這個屬性指向該對象的構(gòu)造器的原型。
函數(shù)都有prototype這個屬性,這個屬性指向該函數(shù)的原型,原型是一個對象格式。
var a = 123;
console.log(a.__proto__ === Number.prototype); //true
例如上述代碼,a的原型就是Number.prototype。
要注意的是:
console.log(Function.__proto__ === Function.prototype); //true
console.log(Object.prototype.__proto__ === null); //true
- 原型鏈
當(dāng)調(diào)用一個對象或者函數(shù)上的屬性時,先去自身找,如果自身沒有,去原型上找,如果原型上沒有,去原型的原型上找,一直到找到,或者返回null為止,形成了一種鏈?zhǔn)浇Y(jié)構(gòu),稱之為原型鏈。
26. 運行下列代碼,控制臺會輸出什么?
var x = { foo: "A" };
x.constructor.prototype.foo = 'B';
var y = {};
console.log(x.foo);
console.log(y.foo);
輸出結(jié)果為A和B。
27. 如果對象obj包含一個非繼承的名為propertyName的屬性,下面正確的是?
A. obj.doesPropertyExist('propertyName');
B. obj.hasProperty('propertyName');
C. obj.exists('propertyName');
D. obj.contains('propertyName');
E. obj.hasOwnProperty('propertyName');
正確選項為E。
28. 請選擇下列正確選項。
function People(name) {
this.name = name;
}
var stu = new People("chris");
A. stu.__proto__ === People.prototype;
B. stu.prototype === People.__proto__;
C. People.__proto__ === Object.__proto__;
D. People.prototype === Object.prototype;
E. People.__proto__ === Function.prototype;
正確選項為A和E。
29. 簡要說明javascript事件循環(huán)機(jī)制。
- 執(zhí)行同步代碼。
- 遇到宏任務(wù)放到宏任務(wù)隊列。
- 遇到微任務(wù)放到微任務(wù)隊列。
- 同步代碼執(zhí)行完畢。
- 執(zhí)行微任務(wù)隊列,微任務(wù)隊列執(zhí)行完畢。
- 執(zhí)行宏任務(wù)隊列,宏任務(wù)隊列執(zhí)行完畢。
30. javascript是如何處理異步的?
由于javascript是單線程的,只能每次做一件事情,只有當(dāng)前的事情做完,才可以做下一件事,因此javascript提供了一些異步解決方案(回調(diào)、Promise),來提高執(zhí)行效率。
在javascript中只有同步代碼都執(zhí)行完畢,執(zhí)行棧為空的時候,才會執(zhí)行異步代碼。
異步有兩個隊列,微任務(wù)隊列(relove、reject)和宏任務(wù)隊列(setTimeout),微隊列執(zhí)行優(yōu)先于宏任務(wù)隊列。
31. 請根據(jù)下列要求實現(xiàn)一個函數(shù)getSum。
- 該函數(shù)接收兩個參數(shù)。
- 第一個參數(shù)為長度不定的
arr數(shù)組,且數(shù)組元素均為number類型。 - 第二個參數(shù)為
number類型的變量sum如果數(shù)組arr中的任意兩個元素之和等于sum,則返回true否則返回false。
function getSum(arr, sum) {
for (let index = 0; index < arr.length; index++) {
if(arr.indexOf(sum - arr[index]) !== -1 && arr.indexOf(sum - arr[index]) !== index){
return true;
};
}
return false;
}
var arr = [1,2,3,4,5];
var sum = 5;
getSum(arr, sum);
32. 請選擇下列正確選項。
A. 除非拋出異常,否則foreach方法無法終止。
B. typeof NaN === 'NaN'
C. javascript中只有變量會提升,函數(shù)則不會。
D. 數(shù)組方法shift會在數(shù)組頭部添加一個新元素。
選擇A選項。
33. 下列代碼,調(diào)用output時入?yún)⑹鞘裁矗?/h3>
var item;
var obj = {
name: 'chris',
email: 'chris@example.com',
sendMail: function () {}
}
for(item in obj) {
ouput(item);
}
var item;
var obj = {
name: 'chris',
email: 'chris@example.com',
sendMail: function () {}
}
for(item in obj) {
ouput(item);
}
A. 類型錯誤。
B. name, "email", "sendMail"
C. name, "email"
D. chris, chris@example.com, undefined
E. chris, chris@example.com, null
正確選項為B。
34. cookie、localStorage和sessionStorage有什么區(qū)別?
- 每次
http請求都會攜帶cookie數(shù)據(jù),而sessionStorage和localStorage不會。 -
cookie存儲的數(shù)據(jù)不能超過4KB,sessionStorage和localStorage存儲大小一般為5MB或者更多。 -
cookie只在設(shè)置的cookie過期時間之前有效,localStorage始終有效,窗口或瀏覽器關(guān)閉也一直保存。
sessionStorage僅在當(dāng)前瀏覽器窗口關(guān)閉之前有效。 -
sessionStorage在不同的瀏覽器窗口中不共享,即使是同一個頁面,localstorage在所有同源窗口中都是共享的,cookie在所有同源窗口中也是共享的。
35. 下列哪些關(guān)于cookie的描述是正確的?
A. cookie必須要在https下傳輸,而不能在http下傳輸。
B. cookie的有效期,不能被設(shè)為瀏覽器關(guān)閉時自動刪除。
C. cookie是存儲在客戶端的。
D. 全部正確。
正確選項是D。
36. 簡要描述javascript語言的特性。
javascript是一種解釋型單線程弱類型的語言。
- 解釋型
解釋型語言可以通俗的理解為一個會中文的人讀一本英文書籍,讀到不會的地方,就去查中英字典,如果一個西班牙人來進(jìn)行閱讀,那么就使用中西字典。諸如javascript、PHP語言都是解釋型序言。
- 解釋型語言的優(yōu)點:適應(yīng)各種環(huán)境。
- 解釋型語言的缺點:執(zhí)行速度稍慢。
編譯型語言可以通俗的理解為將一本英文書籍翻譯成中文書籍,然后在進(jìn)行閱讀,但是如果此時一個西班牙人來閱讀,還需要再次翻譯成西語書籍才可以。諸如C、C++、Java都是編譯型語言。 - 編譯型語言的優(yōu)點:執(zhí)行速度快。
- 編譯型語言的缺點:難以適應(yīng)各種環(huán)境。
- 單線程
單線程,可以理解為有一堆物品,但是只能由一個人運輸。相反的,多線程,就是可以由多個人來運輸這堆物品。
單線程會帶來同步現(xiàn)象:當(dāng)前的事情沒有做完,就不能做下一件事,只能阻塞在這里,只有當(dāng)前的事情做完,才可以做下一件事。但是,javascript中會有一些異步的解決方案,來避免這些阻塞,從而提高單線程的執(zhí)行效率。 - 弱類型
通俗的來說弱類型就是存放的數(shù)據(jù)類型靈活可變,但是與此同時,帶來了數(shù)據(jù)不嚴(yán)謹(jǐn)?shù)娜秉c。
反之,強(qiáng)類型就是存放的數(shù)據(jù)類型不可變,雖然不靈活,但是很嚴(yán)謹(jǐn)。
比如在javascript中,可以讓一個數(shù)字和字符串相加,結(jié)果會是一個新的字符串,javascript會把數(shù)字轉(zhuǎn)換成字符串類型,然后再相加。
37. 執(zhí)行下列代碼控制臺的輸出內(nèi)容是什么?
let a = 5;
console.log(a);
console.log(a++);
console.log(a);
console.log(++a);
console.log(a);
a++是先輸出后運算,++a是先運算后輸出。
控制臺輸出內(nèi)容為:5、5、6、7、7
38. javascript有哪些數(shù)據(jù)類型?
- 基礎(chǔ)類型
Number、String、Boolean、undefined、null(注意,null的typeof輸出類型為Object)
通俗的講,可以用占座的方式來理解undefined和null,當(dāng)座位沒被占的時候就是undefined,當(dāng)座位被占用了,但是占座的人還沒來,這種情況是null。 - 引用類型
Object、Function、Array
39. 函數(shù)字面量和函數(shù)表達(dá)式有什么區(qū)別?
console.log(test1); //test1函數(shù)體
console.log(test2); //not define
console.log(test3); //not define
function test1() {
console.log('test1');
}
(function test2() {
console.log('test2');
})
var a = function test3() {
console.log('test3')
}
test1(); //test1
console.log(window.test1); //test1函數(shù)體
test2(); //not define
console.log(window.test2); //undefined
a(); //test3
test3(); //not define
console.log(window.a); //test3函數(shù)體
console.log(window.test3); //undefined
- 函數(shù)字面量
上述代碼的test1函數(shù)就是通過函數(shù)字面量方式進(jìn)行聲明的,這種方法:
- 會讓函數(shù)進(jìn)行提升。
- 會污染全局對象。
- 函數(shù)表達(dá)式
上述代碼中的test2和test3都是通過函數(shù)表達(dá)式的方式進(jìn)行聲明的,這種方法:
- 不會讓函數(shù)進(jìn)行提升。
- 不會污染全局對象。
40. 簡單介紹函數(shù)內(nèi)的變量聲明。
function test() {
console.log(a); //undefined
console.log(b); //not define
var a = 1;
b = 2;
console.log(a); //1
console.log(b); //2
}
test();
console.log(a); //not define
console.log(b); //2
- 函數(shù)內(nèi)用
var聲明的變量會被提升到函數(shù)體頂部var a;。 - 函數(shù)內(nèi)直接進(jìn)行賦值的變量會成為全局對象上的一個屬性。
- 函數(shù)體外部不能拿到函數(shù)體內(nèi)部的變量。
41. 簡單介紹return關(guān)鍵字。
-
return會結(jié)束函數(shù)的運行。 -
return默認(rèn)返回undefined。
function test() {
return;
}
console.log(test()); //undefined
- 不手動添加
return,函數(shù)會在末尾自動書寫return。
function test() {}
console.log(test()); //undefined
42. 簡單介紹作用域。
- 全局作用域
var a = 1;
var b = 2;
function test() {
var c = 3;
}
可以理解為,直接在script標(biāo)簽下聲明的是處于全局作用域。
此時全局作用域中有:變量a,變量b,函數(shù)test。
- 函數(shù)作用域
以上述代碼為例,變量c就處于test函數(shù)作用域。 - 區(qū)別
- 全局作用域只能訪問處于全局作用域的變量。
- 函數(shù)作用域可以訪問函數(shù)作用域內(nèi)的變量和外部作用域的變量。
43. 簡單介紹this關(guān)鍵字。
- 全局作用域
this關(guān)鍵字指向window對象。 - 函數(shù)作用域
const obj = {
foo: function () {
console.log(this);
}
}
obj.foo();
如果直接使用test()這種方式調(diào)用函數(shù),函數(shù)中的this指向window對象;如果像上述代碼,this指向obj。
44. call、bind和apply有什么區(qū)別?
const obj = {
name: "chris",
age: "18",
print(job){
console.log(`${this.name} + ${this.age} + ${job}`);
}
}
const newObj = {
name: "john"
}
obj.print() //chris + 18 + undefined
obj.print.call(newObj, "engineer"); //john + undefined + engineer
obj.print.apply(newObj, ["engineer"]); //john + undefined + engineer
obj.print.bind(newObj, "engineer")(); //john + undefined + engineer
45. 如何創(chuàng)建一個文檔片段?
createdocumentfragment方法創(chuàng)建了一虛擬的節(jié)點對象,也就是文檔片段,也可理解為一個虛擬的父元素,將其他新創(chuàng)建的多個子元素添加到此父元素下,然后只需要通過一次操作將父元素添加到body下即可時效最終效果,減少了對于DOM的操作。
const d = document.createDocumentFragment();
const arrElement = new Array(10);
for (let index = 0; index < arrElement.length; index++) {
const element = document.createElement("input");
d.appendChild(element);
}
document.body.appendChild(d);
2. 進(jìn)階
1. 什么是防抖函數(shù)和截流函數(shù)?
- 防抖函數(shù)
直白來說,就是過一段時間以后再去執(zhí)行。
例如:電梯門在有人通過之后,隔一段時間之后才會進(jìn)行關(guān)門,如果此時又有人進(jìn)入,繼續(xù)等一段時間(重新計時)后再進(jìn)行關(guān)門。例如某些搜索框也是如此,在輸入完搜索內(nèi)容后,過一段時間才會展示內(nèi)容,如果在展示內(nèi)容之前,又進(jìn)行了輸入,那么就會再次重新等待一段時間。
const input = document.getElementsByClassName('input')[0];
const handle = debunce(); //把return的返回函數(shù)給handle
input.oninput = (val) => {
handle(() => {console.log(val)});
//每次輸入框輸入時,就調(diào)用handle,并把打印val的函數(shù)傳遞過去
}
function debunce() {
let timer; //每次return函數(shù)都使用這個timer
return function (callback) { //這里callback接受了打印val的函數(shù)
clearInterval(timer); //清除定時器
timer = setTimeout(() => { //1s之后執(zhí)行打印val函數(shù)
callback();
}, 1000);
}
}
如果在等待這1s時,再次輸入,就會清除上一次的定時器,不會進(jìn)行打印,然后創(chuàng)建一個新的計時器,如果再次輸入,就會重復(fù)流程,如果沒輸入,等待1s后輸出val。
防抖函數(shù)使用了高階函數(shù)(在函數(shù)內(nèi)部返回一個新的函數(shù)),閉包(外部操作timer),定時器來實現(xiàn)。
- 截流函數(shù)
- 規(guī)定時間內(nèi)無論觸發(fā)多少次,只在規(guī)定時間結(jié)束后執(zhí)行一次。
const inputDom = document.getElementById('input');
const handle = throttle();
inputDom.oninput = (value => {
handle(value.data);
})
function throttle() {
let timer = null;
return function (value) {
if(timer){ //判斷此時是否有定時器,如果有就直接return
return
}
timer = setTimeout(() => {
console.log(value)
timer = null;
}, 1000);
}
}
- 馬上觸發(fā)一次,然后規(guī)定時間結(jié)束后才會被第二次觸發(fā)(利用時間戳)。
const inputDom = document.getElementById('input');
const handle = throttle();
inputDom.oninput = (value => {
handle(value.data);
})
function throttle() {
let time = null;
return function (value) {
if (!time || Date.now() - time > 1000) {
console.log(value);
time = Date.now(); //得到當(dāng)前的時間戳
}
}
}
如果是一定要等某個動作結(jié)束后才觸發(fā),那么就使用防抖函數(shù);
如果是規(guī)定時間內(nèi)只能觸發(fā)一次,那么就使用截流函數(shù)。
2. 如何進(jìn)行性能優(yōu)化?
- 減少http請求
http請求文件需要時間,尤其在網(wǎng)速不好的情況下,會消耗大量時間,而且每一次請求都需要建立連接、釋放連接,會對瀏覽器以及服務(wù)器產(chǎn)生性能消耗。
盡量合并js和css文件,這樣就可以減少文件請求的數(shù)量。
減少對于圖片的請求,使用css sprites或者進(jìn)行懶加載處理,或者對圖片進(jìn)行壓縮。 - 減少重排和重繪
重繪:重繪僅僅是外觀改變,例如背景顏色、文字顏色等改變,對性能影響不大。
重排:例如當(dāng)元素的大小改變時,瀏覽器會重新渲染當(dāng)前的元素,以及計算被該元素大小改變影響的元素,會造成比較多的性能消耗。
例如可以使用absolute定位,這樣當(dāng)元素大小改變時不會影響其他元素,減少性能消耗。 - 減少DOM操作
操作DOM會消耗比較可觀的性能,我們可以這樣理解:
DOM是一座島嶼,javascript是另一座島嶼,兩者之間以一座橋梁連接,那么每次的DOM操作都要通過這座橋梁。
DOM操作是避免不了的,我們只能通過一些變通的辦法來進(jìn)行優(yōu)化,例如查詢DOM時,可以賦值給變量,這樣就不需要每次都進(jìn)行查詢。