看完這幾道 JavaScript 面試題,讓你與考官對(duì)答如流(中)
接上篇《看完這幾道 JavaScript 面試題,讓你與考官對(duì)答如流(上)》
26. 什么是 IIFE,它的用途是什么?
IIFE或立即調(diào)用的函數(shù)表達(dá)式是在創(chuàng)建或聲明后將被調(diào)用或執(zhí)行的函數(shù)。創(chuàng)建IIFE的語(yǔ)法是,將function (){}包裹在在括號(hào)()內(nèi),然后再用另一個(gè)括號(hào)()調(diào)用它,如:(function(){})()
- (function(){...}());(function(){...})();(functionnamed(params){...})();(()=>{});(function(global){...})(window);constutility=(function(){return{...}})
這些示例都是有效的IIFE。倒數(shù)第二個(gè)救命表明我們可以將參數(shù)傳遞給IIFE函數(shù)。最后一個(gè)示例表明,我們可以將IIFE的結(jié)果保存到變量中,以便稍后使用。
IIFE的一個(gè)主要作用是避免與全局作用域內(nèi)的其他變量命名沖突或污染全局命名空間,來(lái)個(gè)例子。
- <scriptsrcscriptsrc="https://cdnurl.com/somelibrary.js"></script>
假設(shè)我們引入了一個(gè)omelibr.js的鏈接,它提供了一些我們?cè)诖a中使用的全局函數(shù),但是這個(gè)庫(kù)有兩個(gè)方法我們沒(méi)有使用:createGraph和drawGraph,因?yàn)檫@些方法都有bug。我們想實(shí)現(xiàn)自己的createGraph和drawGraph方法。
解決此問(wèn)題的一種方法是直接覆蓋:
- <scriptsrcscriptsrc="https://cdnurl.com/somelibrary.js"></script><script>functioncreateGraph(){//createGraphlogichere}functiondrawGraph(){//drawGraphlogichere}</script>
當(dāng)我們使用這個(gè)解決方案時(shí),我們覆蓋了庫(kù)提供給我們的那兩個(gè)方法。
另一種方式是我們自己改名稱:
- <scriptsrcscriptsrc="https://cdnurl.com/somelibrary.js"></script><script>functionmyCreateGraph(){//createGraphlogichere}functionmyDrawGraph(){//drawGraphlogichere}</script>
當(dāng)我們使用這個(gè)解決方案時(shí),我們把那些函數(shù)調(diào)用更改為新的函數(shù)名。
還有一種方法就是使用IIFE:
- <scriptsrcscriptsrc="https://cdnurl.com/somelibrary.js"></script><script>constgraphUtility=(function(){functioncreateGraph(){//createGraphlogichere}functiondrawGraph(){//drawGraphlogichere}return{createGraph,drawGraph}})</script>
在此解決方案中,我們要聲明了graphUtility 變量,用來(lái)保存IIFE執(zhí)行的結(jié)果,該函數(shù)返回一個(gè)包含兩個(gè)方法createGraph和drawGraph的對(duì)象。
IIFE 還可以用來(lái)解決一個(gè)常見(jiàn)的面試題:
- varli=document.querySelectorAll('.list-group>li');for(vari=0,len=li.length;i<len;i++){li[i].addEventListener('click',function(e){console.log(i);})
假設(shè)我們有一個(gè)帶有l(wèi)ist-group類的ul元素,它有5個(gè)li子元素。當(dāng)我們單擊單個(gè)li元素時(shí),打印對(duì)應(yīng)的下標(biāo)值。但在此外上述代碼不起作用,這里每次點(diǎn)擊 li 打印 i 的值都是5,這是由于閉包的原因。
閉包只是函數(shù)記住其當(dāng)前作用域,父函數(shù)作用域和全局作用域的變量引用的能力。當(dāng)我們?cè)谌肿饔糜騼?nèi)使用var關(guān)鍵字聲明變量時(shí),就創(chuàng)建全局變量i。因此,當(dāng)我們單擊li元素時(shí),它將打印5,因?yàn)檫@是稍后在回調(diào)函數(shù)中引用它時(shí)i的值。
使用 IIFE 可以解決此問(wèn)題:
- varli=document.querySelectorAll('.list-group>li');for(vari=0,len=li.length;i<len;i++){(function(currentIndex){li[currentIndex].addEventListener('click',function(e){console.log(currentIndex);})})(i);}
該解決方案之所以行的通,是因?yàn)镮IFE會(huì)為每次迭代創(chuàng)建一個(gè)新的作用域,我們捕獲i的值并將其傳遞給currentIndex參數(shù),因此調(diào)用IIFE時(shí),每次迭代的currentIndex值都是不同的。
27. Function.prototype.apply 方法的用途是什么?
apply() 方法調(diào)用一個(gè)具有給定this值的函數(shù),以及作為一個(gè)數(shù)組(或類似數(shù)組對(duì)象)提供的參數(shù)。
- constdetails={message:'HelloWorld!'};functiongetMessage(){returnthis.message;}getMessage.apply(details);//'HelloWorld!'
call()方法的作用和 apply() 方法類似,區(qū)別就是call()方法接受的是參數(shù)列表,而apply()方法接受的是一個(gè)參數(shù)數(shù)組。
- constperson={name:"MarkoPolo"};functiongreeting(greetingMessage){return`${greetingMessage}${this.name}`;}greeting.apply(person,['Hello']);//"HelloMarkoPolo!"
28. `Function.prototype.call` 方法的用途是什么?
call() 方法使用一個(gè)指定的 this 值和單獨(dú)給出的一個(gè)或多個(gè)參數(shù)來(lái)調(diào)用一個(gè)函數(shù)。
- constdetails={message:'HelloWorld!'};functiongetMessage(){returnthis.message;}getMessage.call(details);//'HelloWorld!'
注意:該方法的語(yǔ)法和作用與 apply() 方法類似,只有一個(gè)區(qū)別,就是call() 方法接受的是一個(gè)參數(shù)列表,而 apply() 方法接受的是一個(gè)包含多個(gè)參數(shù)的數(shù)組。
- constperson={name:"MarkoPolo"};functiongreeting(greetingMessage){return`${greetingMessage}${this.name}`;}greeting.call(person,'Hello');//"HelloMarkoPolo!"
29. Function.prototype.apply 和 Function.prototype.call 之間有什么區(qū)別?
apply()方法可以在使用一個(gè)指定的 this 值和一個(gè)參數(shù)數(shù)組(或類數(shù)組對(duì)象)的前提下調(diào)用某個(gè)函數(shù)或方法。call()方法類似于apply(),不同之處僅僅是call()接受的參數(shù)是參數(shù)列表。
- constobj1={result:0};constobj2={result:0};functionreduceAdd(){letresult=0;for(leti=0,len=arguments.length;i<len;i++){result+=arguments[i];}this.result=result;}reduceAdd.apply(obj1,[1,2,3,4,5]);//15reduceAdd.call(obj2,1,2,3,4,5);//15
30. Function.prototype.bind 的用途是什么?
bind() 方法創(chuàng)建一個(gè)新的函數(shù),在 bind() 被調(diào)用時(shí),這個(gè)新函數(shù)的this 被指定為 bind() 的第一個(gè)參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調(diào)用時(shí)使用。
- importReactfrom'react';classMyComponentextendsReact.Component{constructor(props){super(props);this.state={value:""}thisthis.handleChange=this.handleChange.bind(this);//將“handleChange”方法綁定到“MyComponent”組件}handleChange(e){//dosomethingamazinghere}render(){return(<><inputtypeinputtype={this.props.type}value={this.state.value}onChange={this.handleChange}/></>)}}
31. 什么是函數(shù)式編程? JavaScript 的哪些特性使其成為函數(shù)式語(yǔ)言的候選語(yǔ)言?
函數(shù)式編程(通常縮寫為FP)是通過(guò)編寫純函數(shù),避免共享狀態(tài)、可變數(shù)據(jù)、副作用 來(lái)構(gòu)建軟件的過(guò)程。數(shù)式編程是聲明式 的而不是命令式 的,應(yīng)用程序的狀態(tài)是通過(guò)純函數(shù)流動(dòng)的。與面向?qū)ο缶幊绦纬蓪?duì)比,面向?qū)ο笾袘?yīng)用程序的狀態(tài)通常與對(duì)象中的方法共享和共處。
函數(shù)式編程是一種編程范式 ,這意味著它是一種基于一些基本的定義原則(如上所列)思考軟件構(gòu)建的方式。當(dāng)然,編程范示的其他示例也包括面向?qū)ο缶幊毯瓦^(guò)程編程。
函數(shù)式的代碼往往比命令式或面向?qū)ο蟮拇a更簡(jiǎn)潔,更可預(yù)測(cè),更容易測(cè)試 - 但如果不熟悉它以及與之相關(guān)的常見(jiàn)模式,函數(shù)式的代碼也可能看起來(lái)更密集雜亂,并且 相關(guān)文獻(xiàn)對(duì)新人來(lái)說(shuō)是不好理解的。
JavaScript支持閉包和高階函數(shù)是函數(shù)式編程語(yǔ)言的特點(diǎn)。
32. 什么是高階函數(shù)?
高階函數(shù)只是將函數(shù)作為參數(shù)或返回值的函數(shù)。
- functionhigherOrderFunction(param,callback){returncallback(param);}
33. 為什么函數(shù)被稱為一等公民?
在JavaScript中,函數(shù)不僅擁有一切傳統(tǒng)函數(shù)的使用方式(聲明和調(diào)用),而且可以做到像簡(jiǎn)單值一樣賦值(var func = function(){})、傳參(function func(x,callback){callback();})、返回(function(){return function(){}}),這樣的函數(shù)也稱之為第一級(jí)函數(shù)(First-class Function)。不僅如此,JavaScript中的函數(shù)還充當(dāng)了類的構(gòu)造函數(shù)的作用,同時(shí)又是一個(gè)Function類的實(shí)例(instance)。這樣的多重身份讓JavaScript的函數(shù)變得非常重要。
34. 手動(dòng)實(shí)現(xiàn) `Array.prototype.map 方法`
map() 方法創(chuàng)建一個(gè)新數(shù)組,其結(jié)果是該數(shù)組中的每個(gè)元素都調(diào)用一個(gè)提供的函數(shù)后返回的結(jié)果。
- functionmap(arr,mapCallback){//首先,檢查傳遞的參數(shù)是否正確。if(!Array.isArray(arr)||!arr.length||typeofmapCallback!=='function'){return[];}else{letresult=[];//每次調(diào)用此函數(shù)時(shí),我們都會(huì)創(chuàng)建一個(gè)result數(shù)組//因?yàn)槲覀儾幌敫淖冊(cè)紨?shù)組。for(leti=0,len=arr.length;i<len;i++){result.push(mapCallback(arr[i],i,arr));//將mapCallback返回的結(jié)果push到result數(shù)組中}returnresult;}}
35. 手動(dòng)實(shí)現(xiàn)`Array.prototype.filter`方法
filter() 方法創(chuàng)建一個(gè)新數(shù)組, 其包含通過(guò)所提供函數(shù)實(shí)現(xiàn)的測(cè)試的所有元素。
- functionfilter(arr,filterCallback){//首先,檢查傳遞的參數(shù)是否正確。if(!Array.isArray(arr)||!arr.length||typeoffilterCallback!=='function'){return[];}else{letresult=[];//每次調(diào)用此函數(shù)時(shí),我們都會(huì)創(chuàng)建一個(gè)result數(shù)組//因?yàn)槲覀儾幌敫淖冊(cè)紨?shù)組。for(leti=0,len=arr.length;i<len;i++){//檢查filterCallback的返回值是否是真值if(filterCallback(arr[i],i,arr)){//如果條件為真,則將數(shù)組元素push到result中result.push(arr[i]);}}returnresult;//returntheresultarray}}
36. 手動(dòng)實(shí)現(xiàn)`Array.prototype.reduce`方法
reduce() 方法對(duì)數(shù)組中的每個(gè)元素執(zhí)行一個(gè)由您提供的reducer函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個(gè)返回值。
- functionreduce(arr,reduceCallback,initialValue){//首先,檢查傳遞的參數(shù)是否正確。if(!Array.isArray(arr)||!arr.length||typeofreduceCallback!=='function'){return[];}else{//如果沒(méi)有將initialValue傳遞給該函數(shù),我們將使用第一個(gè)數(shù)組項(xiàng)作為initialValueinitialValuelethasInitialValue=initialValue!==undefined;letvalue=hasInitialValue?initialValue:arr[0];、//如果有傳遞initialValue,則索引從1開(kāi)始,否則從0開(kāi)始for(leti=hasInitialValue?0:1,len=arr.length;i<len;i++){value=reduceCallback(value,arr[i],i,arr);}returnvalue;}}
37. arguments 的對(duì)象是什么?
arguments對(duì)象是函數(shù)中傳遞的參數(shù)值的集合。它是一個(gè)類似數(shù)組的對(duì)象,因?yàn)樗幸粋€(gè)length屬性,我們可以使用數(shù)組索引表示法arguments[1]來(lái)訪問(wèn)單個(gè)值,但它沒(méi)有數(shù)組中的內(nèi)置方法,如:forEach、reduce、filter和map。
我們可以使用Array.prototype.slice將arguments對(duì)象轉(zhuǎn)換成一個(gè)數(shù)組。
- functionone(){returnArray.prototype.slice.call(arguments);}
注意:箭頭函數(shù)中沒(méi)有arguments對(duì)象。
- functionone(){returnarguments;}consttwo=function(){returnarguments;}constthree=functionthree(){returnarguments;}constfour=()=>arguments;four();//Throwsanerror-argumentsisnotdefined
當(dāng)我們調(diào)用函數(shù)four時(shí),它會(huì)拋出一個(gè)ReferenceError: arguments is not defined error。使用rest語(yǔ)法,可以解決這個(gè)問(wèn)題。
- constfour=(...args)=>args;
這會(huì)自動(dòng)將所有參數(shù)值放入數(shù)組中。
38. 如何創(chuàng)建一個(gè)沒(méi)有 prototype(原型)的對(duì)象?
我們可以使用Object.create方法創(chuàng)建沒(méi)有原型的對(duì)象。
- consto1={};console.log(o1.toString());//[objectObject]consto2=Object.create(null);console.log(o2.toString());//throwsanerroro2.toStringisnotafunction
39. 為什么在調(diào)用這個(gè)函數(shù)時(shí),代碼中的`b`會(huì)變成一個(gè)全局變量?
- functionmyFunc(){leta=b=0;}myFunc();
原因是賦值運(yùn)算符是從右到左的求值的。這意味著當(dāng)多個(gè)賦值運(yùn)算符出現(xiàn)在一個(gè)表達(dá)式中時(shí),它們是從右向左求值的。所以上面代碼變成了這樣:
- functionmyFunc(){leta=(b=0);}myFunc();
首先,表達(dá)式b = 0求值,在本例中b沒(méi)有聲明。因此,JS引擎在這個(gè)函數(shù)外創(chuàng)建了一個(gè)全局變量b,之后表達(dá)式b = 0的返回值為0,并賦給新的局部變量a。
我們可以通過(guò)在賦值之前先聲明變量來(lái)解決這個(gè)問(wèn)題。
- functionmyFunc(){leta,b;a=b=0;}myFunc();
40. ECMAScript 是什么?
ECMAScript 是編寫腳本語(yǔ)言的標(biāo)準(zhǔn),這意味著JavaScript遵循ECMAScript標(biāo)準(zhǔn)中的規(guī)范變化,因?yàn)樗荍avaScript的藍(lán)圖。
ECMAScript 和 Javascript,本質(zhì)上都跟一門語(yǔ)言有關(guān),一個(gè)是語(yǔ)言本身的名字,一個(gè)是語(yǔ)言的約束條件
只不過(guò)發(fā)明JavaScript的那個(gè)人(Netscape公司),把東西交給了ECMA(European Computer Manufacturers Association),這個(gè)人規(guī)定一下他的標(biāo)準(zhǔn),因?yàn)楫?dāng)時(shí)有java語(yǔ)言了,又想強(qiáng)調(diào)這個(gè)東西是讓ECMA這個(gè)人定的規(guī)則,所以就這樣一個(gè)神奇的東西誕生了,這個(gè)東西的名稱就叫做ECMAScript。
javaScript = ECMAScript + DOM + BOM(自認(rèn)為是一種廣義的JavaScript)
ECMAScript說(shuō)什么JavaScript就得做什么!
JavaScript(狹義的JavaScript)做什么都要問(wèn)問(wèn)ECMAScript我能不能這樣干!如果不能我就錯(cuò)了!能我就是對(duì)的!
——突然感覺(jué)JavaScript好沒(méi)有尊嚴(yán),為啥要搞個(gè)人出來(lái)約束自己,
那個(gè)人被創(chuàng)造出來(lái)也好委屈,自己被創(chuàng)造出來(lái)完全是因?yàn)橐s束JavaScript。
41. ES6或ECMAScript 2015有哪些新特性?
- 箭頭函數(shù)
- 類
- 模板字符串
- 加強(qiáng)的對(duì)象字面量
- 對(duì)象解構(gòu)
- Promise
- 生成器
- 模塊
- Symbol
- 代理
- Set
- 函數(shù)默認(rèn)參數(shù)
- rest 和展開(kāi)
- 塊作用域
42. `var`,`let`和`const`的區(qū)別是什么?
var聲明的變量會(huì)掛載在window上,而let和const聲明的變量不會(huì):
- vara=100;console.log(a,window.a);//10100100letb=10;console.log(b,window.b);//110undefinedconstc=1;console.log(c,window.c);//1undefined
var聲明變量存在變量提升,let和const不存在變量提升:
- console.log(a);//undefined===>a已聲明還沒(méi)賦值,默認(rèn)得到undefined值vara=100;console.log(b);//報(bào)錯(cuò):b isnotdefined===>找不到b這個(gè)變量letb=10;console.log(c);//報(bào)錯(cuò):c isnotdefined===>找不到c這個(gè)變量constc=10;
let和const聲明形成塊作用域
- if(1){vara=100;letb=10;}console.log(a);//100console.log(b)//報(bào)錯(cuò):b is not defined ===>找不到b這個(gè)變量-------------------------------------------------------------if(1){vara=100;constc=1;}console.log(a);//100console.log(c)//報(bào)錯(cuò):c is not defined ===>找不到c這個(gè)變量
同一作用域下let和const不能聲明同名變量,而var可以
- vara=100;console.log(a);//10100vara=10;console.log(a);//10-------------------------------------leta=100;leta=10;//控制臺(tái)報(bào)錯(cuò):Identifier 'a' has already been declared ===>標(biāo)識(shí)符a已經(jīng)被聲明了。
暫存死區(qū)
- vara=100;if(1){a=10;//在當(dāng)前塊作用域中存在a使用let/const聲明的情況下,給a賦值10時(shí),只會(huì)在當(dāng)前作用域找變量a,//而這時(shí),還未到聲明時(shí)候,所以控制臺(tái)Error:aisnotdefinedleta=1;}
const
- /** 1、一旦聲明必須賦值,不能使用null占位。** 2、聲明后不能再修改** 3、如果聲明的是復(fù)合類型數(shù)據(jù),可以修改其屬性***/consta=100;constlist=[];list[0]=10;console.log(list); //[10]constobj={a:100};obj.name='apple';obj.a=10000;console.log(obj); //{a:10000,name:'apple'}
43. 什么是箭頭函數(shù)?
箭頭函數(shù)表達(dá)式的語(yǔ)法比函數(shù)表達(dá)式更簡(jiǎn)潔,并且沒(méi)有自己的this,arguments,super或new.target。箭頭函數(shù)表達(dá)式更適用于那些本來(lái)需要匿名函數(shù)的地方,并且它不能用作構(gòu)造函數(shù)。
- //ES5VersionvargetCurrentDate=function(){returnnewDate();}//ES6VersionconstgetCurrentDate=()=>newDate();
在本例中,ES5 版本中有function(){}聲明和return關(guān)鍵字,這兩個(gè)關(guān)鍵字分別是創(chuàng)建函數(shù)和返回值所需要的。在箭頭函數(shù)版本中,我們只需要()括號(hào),不需要 return 語(yǔ)句,因?yàn)槿绻覀冎挥幸粋€(gè)表達(dá)式或值需要返回,箭頭函數(shù)就會(huì)有一個(gè)隱式的返回。
- //ES5Versionfunctiongreet(name){return'Hello'+name+'!';}//ES6Versionconstgreet=(name)=>`Hello${name}`;constgreet2=name=>`Hello${name}`;
我們還可以在箭頭函數(shù)中使用與函數(shù)表達(dá)式和函數(shù)聲明相同的參數(shù)。如果我們?cè)谝粋€(gè)箭頭函數(shù)中有一個(gè)參數(shù),則可以省略括號(hào)。
- constgetArgs=()=>argumentsconstgetArgs2=(...rest)=>rest
箭頭函數(shù)不能訪問(wèn)arguments對(duì)象。所以調(diào)用第一個(gè)getArgs函數(shù)會(huì)拋出一個(gè)錯(cuò)誤。相反,我們可以使用rest參數(shù)來(lái)獲得在箭頭函數(shù)中傳遞的所有參數(shù)。
- constdata={result:0,nums:[1,2,3,4,5],computeResult(){//這里的“this”指的是“data”對(duì)象constaddAll=()=>{returnthis.nums.reduce((total,cur)=>total+cur,0)};this.result=addAll();}};
箭頭函數(shù)沒(méi)有自己的this值。它捕獲詞法作用域函數(shù)的this值,在此示例中,addAll函數(shù)將復(fù)制computeResult 方法中的this值,如果我們?cè)谌肿饔糜蚵暶骷^函數(shù),則this值為 window 對(duì)象。
44. 什么是類?
類(class)是在 JS 中編寫構(gòu)造函數(shù)的新方法。它是使用構(gòu)造函數(shù)的語(yǔ)法糖,在底層中使用仍然是原型和基于原型的繼承。
- //ES5VersionfunctionPerson(firstName,lastName,age,address){this.firstName=firstName;this.lastName=lastName;this.age=age;this.address=address;}Person.self=function(){returnthis;}Person.prototype.toString=function(){return"[objectPerson]";}Person.prototype.getFullName=function(){returnthis.firstName+""+this.lastName;}//ES6VersionclassPerson{constructor(firstName,lastName,age,address){this.lastName=lastName;this.firstName=firstName;this.age=age;this.address=address;}staticself(){returnthis;}toString(){return"[objectPerson]";}getFullName(){return`${this.firstName}${this.lastName}`;}}
重寫方法并從另一個(gè)類繼承。
- //ES5VersionEmployee.prototype=Object.create(Person.prototype);functionEmployee(firstName,lastName,age,address,jobTitle,yearStarted){Person.call(this,firstName,lastName,age,address);this.jobTitle=jobTitle;this.yearStarted=yearStarted;}Employee.prototype.describe=function(){return`Iam${this.getFullName()}andIhaveapositionof${this.jobTitle}andIstartedat${this.yearStarted}`;}Employee.prototype.toString=function(){return"[objectEmployee]";}//ES6VersionclassEmployeeextendsPerson{//Inheritsfrom"Person"classconstructor(firstName,lastName,age,address,jobTitle,yearStarted){super(firstName,lastName,age,address);this.jobTitle=jobTitle;this.yearStarted=yearStarted;}describe(){return`Iam${this.getFullName()}andIhaveapositionof${this.jobTitle}andIstartedat${this.yearStarted}`;}toString(){//Overridingthe"toString"methodof"Person"return"[objectEmployee]";}}
所以我們要怎么知道它在內(nèi)部使用原型?
- classSomething{}functionAnotherSomething(){}constas=newAnotherSomething();consts=newSomething();console.log(typeofSomething);//"function"console.log(typeofAnotherSomething);//"function"console.log(as.toString());//"[objectObject]"console.log(as.toString());//"[objectObject]"console.log(as.toString===Object.prototype.toString);//trueconsole.log(s.toString===Object.prototype.toString);//true
45. 什么是模板字符串?
模板字符串是在 JS 中創(chuàng)建字符串的一種新方法。我們可以通過(guò)使用反引號(hào)使模板字符串化。
- //ES5Versionvargreet='HiI\'mMark';//ES6Versionletgreet=`HiI'mMark`;
在 ES5 中我們需要使用一些轉(zhuǎn)義字符來(lái)達(dá)到多行的效果,在模板字符串不需要這么麻煩:
- //ES5VersionvarlastWords='\n'+'I\n'+'Am\n'+'IronMan\n';//ES6VersionletlastWords=`IAmIronMan`;
在ES5版本中,我們需要添加\n以在字符串中添加新行。在模板字符串中,我們不需要這樣做。
- //ES5Versionfunctiongreet(name){return'Hello'+name+'!';}//ES6Versionfunctiongreet(name){return`Hello${name}!`;}
在 ES5 版本中,如果需要在字符串中添加表達(dá)式或值,則需要使用+運(yùn)算符。在模板字符串s中,我們可以使用${expr}嵌入一個(gè)表達(dá)式,這使其比 ES5 版本更整潔。
46. 什么是對(duì)象解構(gòu)?
對(duì)象析構(gòu)是從對(duì)象或數(shù)組中獲取或提取值的一種新的、更簡(jiǎn)潔的方法。假設(shè)有如下的對(duì)象:
- constemployee={firstName:"Marko",lastName:"Polo",position:"SoftwareDeveloper",yearHired:2017};
從對(duì)象獲取屬性,早期方法是創(chuàng)建一個(gè)與對(duì)象屬性同名的變量。這種方法很麻煩,因?yàn)槲覀円獮槊總€(gè)屬性創(chuàng)建一個(gè)新變量。假設(shè)我們有一個(gè)大對(duì)象,它有很多屬性和方法,用這種方法提取屬性會(huì)很麻煩。
- varfirstName=employee.firstName;varlastName=employee.lastName;varposition=employee.position;varyearHired=employee.yearHired;
使用解構(gòu)方式語(yǔ)法就變得簡(jiǎn)潔多了:
- {firstName,lastName,position,yearHired}=employee;
我們還可以為屬性取別名:
- let{firstName:fName,lastName:lName,position,yearHired}=employee;
當(dāng)然如果屬性值為 undefined 時(shí),我們還可以指定默認(rèn)值:
- let{firstName="Mark",lastName:lName,position,yearHired}=employee;
47. 什么是 ES6 模塊?
模塊使我們能夠?qū)⒋a基礎(chǔ)分割成多個(gè)文件,以獲得更高的可維護(hù)性,并且避免將所有代碼放在一個(gè)大文件中。在 ES6 支持模塊之前,有兩個(gè)流行的模塊。
- CommonJS-Node.js
- AMD(異步模塊定義)-瀏覽器
基本上,使用模塊的方式很簡(jiǎn)單,import用于從另一個(gè)文件中獲取功能或幾個(gè)功能或值,同時(shí)export用于從文件中公開(kāi)功能或幾個(gè)功能或值。
(1) 導(dǎo)出
使用 ES5 (CommonJS)
- //使用ES5CommonJS-helpers.jsexports.isNull=function(val){returnval===null;}exports.isUndefined=function(val){returnval===undefined;}exports.isNullOrUndefined=function(val){returnexports.isNull(val)||exports.isUndefined(val);}
使用 ES6 模塊
- //使用ES6Modules-helpers.jsexportfunctionisNull(val){returnval===null;}exportfunctionisUndefined(val){returnval===undefined;}exportfunctionisNullOrUndefined(val){returnisNull(val)||isUndefined(val);}
在另一個(gè)文件中導(dǎo)入函數(shù)
- //使用ES5(CommonJS)-index.jsconsthelpers=require('./helpers.js');//helpershelpersisanobjectconstisNull=helpers.isNull;constisUndefined=helpers.isUndefined;constisNullOrUndefined=helpers.isNullOrUndefined;//orifyourenvironmentsupportsDestructuringconst{isNull,isUndefined,isNullOrUndefined}=require('./helpers.js');-------------------------------------------------------//ES6Modules-index.jsimport*ashelpersfrom'./helpers.js';//helpersisanobject//orimport{isNull,isUndefined,isNullOrUndefinedasisValid}from'./helpers.js';//using"as"forrenamingnamedexports
(2) 在文件中導(dǎo)出單個(gè)功能或默認(rèn)導(dǎo)出
使用 ES5 (CommonJS)
- //使用ES5(CommonJS)-index.jsclassHelpers{staticisNull(val){returnval===null;}staticisUndefined(val){returnval===undefined;}staticisNullOrUndefined(val){returnthis.isNull(val)||this.isUndefined(val);}}module.exports=Helpers;
使用ES6 Modules
- //使用ES6Modules-helpers.jsclassHelpers{staticisNull(val){returnval===null;}staticisUndefined(val){returnval===undefined;}staticisNullOrUndefined(val){returnthis.isNull(val)||this.isUndefined(val);}}exportdefaultHelpers
(3) 從另一個(gè)文件導(dǎo)入單個(gè)功能
使用ES5 (CommonJS)
- //使用ES5(CommonJS)-index.jsconstHelpers=require('./helpers.js');console.log(Helpers.isNull(null));
使用 ES6 Modules
- importHelpersfrom'.helpers.js'console.log(Helpers.isNull(null));
48. 什么是`Set`對(duì)象,它是如何工作的?
Set 對(duì)象允許你存儲(chǔ)任何類型的唯一值,無(wú)論是原始值或者是對(duì)象引用。
我們可以使用Set構(gòu)造函數(shù)創(chuàng)建Set實(shí)例。
- constset1=newSet();constset2=newSet(["a","b","c","d","d","e"]);
我們可以使用add方法向Set實(shí)例中添加一個(gè)新值,因?yàn)閍dd方法返回Set對(duì)象,所以我們可以以鏈?zhǔn)降姆绞皆俅问褂胊dd。如果一個(gè)值已經(jīng)存在于Set對(duì)象中,那么它將不再被添加。
- set2.add("f");set2.add("g").add("h").add("i").add("j").add("k").add("k");//后一個(gè)“k”不會(huì)被添加到set對(duì)象中,因?yàn)樗呀?jīng)存在了
我們可以使用has方法檢查Set實(shí)例中是否存在特定的值。
- set2.has("a")//trueset2.has("z")//true
我們可以使用size屬性獲得Set實(shí)例的長(zhǎng)度。
- set2.size//returns10
可以使用clear方法刪除 Set 中的數(shù)據(jù)。
- set2.clear();
我們可以使用Set對(duì)象來(lái)刪除數(shù)組中重復(fù)的元素。
- constnumbers=[1,2,3,4,5,6,6,7,8,8,5];constuniqueNums=[...newSet(numbers)];//[1,2,3,4,5,6,7,8]
49. 什么是回調(diào)函數(shù)?
回調(diào)函數(shù)是一段可執(zhí)行的代碼段,它作為一個(gè)參數(shù)傳遞給其他的代碼,其作用是在需要的時(shí)候方便調(diào)用這段(回調(diào)函數(shù))代碼。
在JavaScript中函數(shù)也是對(duì)象的一種,同樣對(duì)象可以作為參數(shù)傳遞給函數(shù),因此函數(shù)也可以作為參數(shù)傳遞給另外一個(gè)函數(shù),這個(gè)作為參數(shù)的函數(shù)就是回調(diào)函數(shù)。
- constbtnAdd=document.getElementById('btnAdd');btnAdd.addEventListener('click',functionclickCallback(e){//dosomethinguseless});
在本例中,我們等待id為btnAdd的元素中的click事件,如果它被單擊,則執(zhí)行clickCallback函數(shù)?;卣{(diào)函數(shù)向某些數(shù)據(jù)或事件添加一些功能。
數(shù)組中的reduce、filter和map方法需要一個(gè)回調(diào)作為參數(shù)?;卣{(diào)的一個(gè)很好的類比是,當(dāng)你打電話給某人,如果他們不接,你留下一條消息,你期待他們回調(diào)。調(diào)用某人或留下消息的行為是事件或數(shù)據(jù),回調(diào)是你希望稍后發(fā)生的操作。
50. Promise 是什么?
Promise 是異步編程的一種解決方案:從語(yǔ)法上講,promise是一個(gè)對(duì)象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過(guò)一段時(shí)間會(huì)給你一個(gè)結(jié)果。promise有三種狀態(tài):pending(等待態(tài)),fulfiled(成功態(tài)),rejected(失敗態(tài));狀態(tài)一旦改變,就不會(huì)再變。創(chuàng)造promise實(shí)例后,它會(huì)立即執(zhí)行。
- fs.readFile('somefile.txt',function(e,data){if(e){console.log(e);}console.log(data);});
如果我們?cè)诨卣{(diào)內(nèi)部有另一個(gè)異步操作,則此方法存在問(wèn)題。我們將有一個(gè)混亂且不可讀的代碼。此代碼稱為“回調(diào)地獄”。
- //回調(diào)地獄fs.readFile('somefile.txt',function(e,data){//yourcodeherefs.readdir('directory',function(e,files){//yourcodeherefs.mkdir('directory',function(e){//yourcodehere})})})
如果我們?cè)谶@段代碼中使用promise,它將更易于閱讀、理解和維護(hù)。
- promReadFile('file/path').then(data=>{returnpromReaddir('directory');}).then(data=>{returnpromMkdir('directory');}).catch(e=>{console.log(e);})
promise有三種不同的狀態(tài):
- pending:初始狀態(tài),完成或失敗狀態(tài)的前一個(gè)狀態(tài)
- fulfilled:操作成功完成
- rejected:操作失敗
pending 狀態(tài)的 Promise 對(duì)象會(huì)觸發(fā) fulfilled/rejected 狀態(tài),在其狀態(tài)處理方法中可以傳入?yún)?shù)/失敗信息。當(dāng)操作成功完成時(shí),Promise 對(duì)象的 then 方法就會(huì)被調(diào)用;否則就會(huì)觸發(fā) catch。如:
- constmyFirstPromise=newPromise((resolve,reject)=>{setTimeout(function(){resolve("成功!");},250);});myFirstPromise.then((data)=>{console.log("Yay!"+data);}).catch((e)=>{...});
由于篇幅過(guò)長(zhǎng),我將此系列分成上中下三篇,下篇我們?cè)谝?jiàn)。