看完這幾道 JavaScript 面試題,讓你與考官對答如流(上)
1.undefined 和 null 有什么區(qū)別?
在理解undefined和null之間的差異之前,我們先來看看它們的相似類。
它們屬于 JavaScript 的 7 種基本類型。
- letprimitiveTypes=['string','number','null','undefined','boolean','symbol','bigint'];
它們是屬于虛值,可以使用Boolean(value)或!!value將其轉(zhuǎn)換為布爾值時(shí),值為false。
- console.log(!!null);//falseconsole.log(!!undefined);//falseconsole.log(Boolean(null));//falseconsole.log(Boolean(undefined));//false
接著來看看它們的區(qū)別。
undefined是未指定特定值的變量的默認(rèn)值,或者沒有顯式返回值的函數(shù),如:console.log(1),還包括對象中不存在的屬性,這些 JS 引擎都會為其分配 undefined 值。
- let_thisIsUndefined;constdoNothing=()=>{};constsomeObj={a:"ay",b:"bee",c:"si"};console.log(_thisIsUndefined);//undefinedconsole.log(doNothing());//undefinedconsole.log(someObj["d"]);//undefined
null是“不代表任何值的值”。null是已明確定義給變量的值。在此示例中,當(dāng)fs.readFile方法未引發(fā)錯(cuò)誤時(shí),我們將獲得null值。
- fs.readFile('path/to/file',(e,data)=>{console.log(e);//當(dāng)沒有錯(cuò)誤發(fā)生時(shí),打印nullif(e){console.log(e);}console.log(data);});
在比較null和undefined時(shí),我們使用==時(shí)得到true,使用===時(shí)得到false:
- console.log(null==undefined);//trueconsole.log(null===undefined);//false
2. && 運(yùn)算符能做什么
&& 也可以叫邏輯與,在其操作數(shù)中找到第一個(gè)虛值表達(dá)式并返回它,如果沒有找到任何虛值表達(dá)式,則返回最后一個(gè)真值表達(dá)式。它采用短路來防止不必要的工作。
- console.log(false&&1&&[]);//falseconsole.log(""&&true&&5);//5
使用if語句
- constrouter:Router=Router();router.get('/endpoint',(req:Request,res:Response)=>{letconMobile:PoolConnection;try{//dosomedboperations}catch(e){if(conMobile){conMobile.release();}}});
使用&&操作符
- constrouter:Router=Router();router.get('/endpoint',(req:Request,res:Response)=>{letconMobile:PoolConnection;try{//dosomedboperations}catch(e){conMobile&&conMobile.release()}});
3. || 運(yùn)算符能做什么
||也叫或邏輯或,在其操作數(shù)中找到第一個(gè)真值表達(dá)式并返回它。這也使用了短路來防止不必要的工作。在支持 ES6 默認(rèn)函數(shù)參數(shù)之前,它用于初始化函數(shù)中的默認(rèn)參數(shù)值。
- console.log(null||1||undefined);//1functionlogName(name){varn=name||"Mark";console.log(n);}logName();//"Mark"
4. 使用 + 或一元加運(yùn)算符是將字符串轉(zhuǎn)換為數(shù)字的最快方法嗎?
根據(jù)MDN文檔,+是將字符串轉(zhuǎn)換為數(shù)字的最快方法,因?yàn)槿绻狄呀?jīng)是數(shù)字,它不會執(zhí)行任何操作。
5. DOM 是什么?
DOM 代表文檔對象模型,是 HTML 和 XML 文檔的接口(API)。當(dāng)瀏覽器第一次讀取(解析)HTML文檔時(shí),它會創(chuàng)建一個(gè)大對象,一個(gè)基于 HTM L文檔的非常大的對象,這就是DOM。它是一個(gè)從 HTML 文檔中建模的樹狀結(jié)構(gòu)。DOM 用于交互和修改DOM結(jié)構(gòu)或特定元素或節(jié)點(diǎn)。
假設(shè)我們有這樣的 HTML 結(jié)構(gòu):
- <!DOCTYPEhtml><htmllanghtmllang="en"><head><metacharsetmetacharset="UTF-8"><metanamemetaname="viewport"content="width=device-width,initial-scale=1.0"><metahttp-equivmetahttp-equiv="X-UA-Compatible"content="ie=edge"><title>DocumentObjectModel</title></head><body><div><p><span></span></p><label></label><input></div></body></html>
等價(jià)的DOM是這樣的:
JS 中的document對象表示DOM。它為我們提供了許多方法,我們可以使用這些方法來選擇元素來更新元素內(nèi)容,等等。
6. 什么是事件傳播?
當(dāng)事件發(fā)生在DOM元素上時(shí),該事件并不完全發(fā)生在那個(gè)元素上。在“冒泡階段”中,事件冒泡或向上傳播至父級,祖父母,祖父母或父級,直到到達(dá)window為止;而在“捕獲階段”中,事件從window開始向下觸發(fā)元素 事件或event.target。
事件傳播有三個(gè)階段:
- 捕獲階段–事件從 window 開始,然后向下到每個(gè)元素,直到到達(dá)目標(biāo)元素。
- 目標(biāo)階段–事件已達(dá)到目標(biāo)元素。
- 冒泡階段–事件從目標(biāo)元素冒泡,然后上升到每個(gè)元素,直到到達(dá) window。
7. 什么是事件冒泡?
當(dāng)事件發(fā)生在DOM元素上時(shí),該事件并不完全發(fā)生在那個(gè)元素上。在冒泡階段,事件冒泡,或者事件發(fā)生在它的父代,祖父母,祖父母的父代,直到到達(dá)window為止。
假設(shè)有如下的 HTML 結(jié)構(gòu):
- <divclassdivclass="grandparent"><divclassdivclass="parent"><divclassdivclass="child">1</div></div></div>
- functionaddEvent(el,event,callback,isCapture=false){if(!el||!event||!callback||typeofcallback!=='function')return;if(typeofel==='string'){el=document.querySelector(el);};el.addEventListener(event,callback,isCapture);}addEvent(document,'DOMContentLoaded',()=>{constchild=document.querySelector('.child');constparent=document.querySelector('.parent');constgrandparent=document.querySelector('.grandparent');addEvent(child,'click',function(e){console.log('child');});addEvent(parent,'click',function(e){console.log('parent');});addEvent(grandparent,'click',function(e){console.log('grandparent');});addEvent(document,'click',function(e){console.log('document');});addEvent('html','click',function(e){console.log('html');})addEvent(window,'click',function(e){console.log('window');})});
addEventListener方法具有第三個(gè)可選參數(shù)useCapture,其默認(rèn)值為false,事件將在冒泡階段中發(fā)生,如果為true,則事件將在捕獲階段中發(fā)生。如果單擊child元素,它將分別在控制臺上記錄child,parent,grandparent,html,document和window,這就是事件冒泡。
8. 什么是事件捕獲?
當(dāng)事件發(fā)生在 DOM 元素上時(shí),該事件并不完全發(fā)生在那個(gè)元素上。在捕獲階段,事件從window開始,一直到觸發(fā)事件的元素。
假設(shè)有如下的 HTML 結(jié)構(gòu):
- <divclassdivclass="grandparent"><divclassdivclass="parent"><divclassdivclass="child">1</div></div></div>
- functionaddEvent(el,event,callback,isCapture=false){if(!el||!event||!callback||typeofcallback!=='function')return;if(typeofel==='string'){el=document.querySelector(el);};el.addEventListener(event,callback,isCapture);}addEvent(document,'DOMContentLoaded',()=>{constchild=document.querySelector('.child');constparent=document.querySelector('.parent');constgrandparent=document.querySelector('.grandparent');addEvent(child,'click',function(e){console.log('child');});addEvent(parent,'click',function(e){console.log('parent');});addEvent(grandparent,'click',function(e){console.log('grandparent');});addEvent(document,'click',function(e){console.log('document');});addEvent('html','click',function(e){console.log('html');})addEvent(window,'click',function(e){console.log('window');})});
addEventListener方法具有第三個(gè)可選參數(shù)useCapture,其默認(rèn)值為false,事件將在冒泡階段中發(fā)生,如果為true,則事件將在捕獲階段中發(fā)生。如果單擊child元素,它將分別在控制臺上打印window,document,html,grandparent和parent,這就是事件捕獲。
9. event.preventDefault() 和 event.stopPropagation()方法之間有什么區(qū)別?
event.preventDefault() 方法可防止元素的默認(rèn)行為。如果在表單元素中使用,它將阻止其提交。如果在錨元素中使用,它將阻止其導(dǎo)航。如果在上下文菜單中使用,它將阻止其顯示或顯示。event.stopPropagation()方法用于阻止捕獲和冒泡階段中當(dāng)前事件的進(jìn)一步傳播。
10. 如何知道是否在元素中使用了`event.preventDefault()`方法?
我們可以在事件對象中使用event.defaultPrevented屬性。它返回一個(gè)布爾值用來表明是否在特定元素中調(diào)用了event.preventDefault()。
11. 為什么此代碼 `obj.someprop.x` 會引發(fā)錯(cuò)誤?
- constobj={};console.log(obj.someprop.x);
顯然,由于我們嘗試訪問someprop屬性中的x屬性,而 someprop 并沒有在對象中,所以值為 undefined。記住對象本身不存在的屬性,并且其原型的默認(rèn)值為undefined。因?yàn)閡ndefined沒有屬性x,所以試圖訪問將會報(bào)錯(cuò)。
12. 什么是 event.target ?
簡單來說,event.target是發(fā)生事件的元素或觸發(fā)事件的元素。
假設(shè)有如下的 HTML 結(jié)構(gòu):
- <divonclickdivonclick="clickFunc(event)"style="text-align:center;margin:15px;border:1pxsolidred;border-radius:3px;"><divstyledivstyle="margin:25px;border:1pxsolidroyalblue;border-radius:3px;"><divstyledivstyle="margin:25px;border:1pxsolidskyblue;border-radius:3px;"><buttonstylebuttonstyle="margin:10px">Button</button></div></div></div>
- functionclickFunc(event){console.log(event.target);}
如果單擊 button,即使我們將事件附加在最外面的div上,它也將打印 button 標(biāo)簽,因此我們可以得出結(jié)論event.target是觸發(fā)事件的元素。
13. 什么是 event.currentTarget??
event.currentTarget是我們在其上顯式附加事件處理程序的元素。
假設(shè)有如下的 HTML 結(jié)構(gòu):
- <divonclickdivonclick="clickFunc(event)"style="text-align:center;margin:15px;border:1pxsolidred;border-radius:3px;"><divstyledivstyle="margin:25px;border:1pxsolidroyalblue;border-radius:3px;"><divstyledivstyle="margin:25px;border:1pxsolidskyblue;border-radius:3px;"><buttonstylebuttonstyle="margin:10px">Button</button></div></div></div>
- functionclickFunc(event){console.log(event.currentTarget);}
如果單擊 button,即使我們單擊該 button,它也會打印最外面的div標(biāo)簽。在此示例中,我們可以得出結(jié)論,event.currentTarget是附加事件處理程序的元素。
14. == 和 === 有什么區(qū)別?
==用于一般比較,===用于嚴(yán)格比較,==在比較的時(shí)候可以轉(zhuǎn)換數(shù)據(jù)類型,===嚴(yán)格比較,只要類型不匹配就返回flase。
先來看看 == 這兄弟:
強(qiáng)制是將值轉(zhuǎn)換為另一種類型的過程。在這種情況下,==會執(zhí)行隱式強(qiáng)制。在比較兩個(gè)值之前,==需要執(zhí)行一些規(guī)則。
假設(shè)我們要比較x == y的值。
- 如果x和y的類型相同,則 JS 會換成===操作符進(jìn)行比較。
- 如果x為null, y為undefined,則返回true。
- 如果x為undefined且y為null,則返回true。
- 如果x的類型是number, y的類型是string,那么返回x == toNumber(y)。
- 如果x的類型是string, y的類型是number,那么返回toNumber(x) == y。
- 如果x為類型是boolean,則返回toNumber(x)== y。
- 如果y為類型是boolean,則返回x == toNumber(y)。
- 如果x是string、symbol或number,而y是object類型,則返回x == toPrimitive(y)。
- 如果x是object,y是string,symbol則返回toPrimitive(x) == y。
- 剩下的 返回 false
注意:toPrimitive首先在對象中使用valueOf方法,然后使用toString方法來獲取該對象的原始值。
舉個(gè)例子。
- xyx == y55true1'1'truenullundefinedtrue0falsetrue'1,2'[1,2]true'[object Object]'{}true
這些例子都返回true。
第一個(gè)示例符合條件1,因?yàn)閤和y具有相同的類型和值。
第二個(gè)示例符合條件4,在比較之前將y轉(zhuǎn)換為數(shù)字。
第三個(gè)例子符合條件2。
第四個(gè)例子符合條件7,因?yàn)閥是boolean類型。
第五個(gè)示例符合條件8。使用toString()方法將數(shù)組轉(zhuǎn)換為字符串,該方法返回1,2。
最后一個(gè)示例符合條件8。使用toString()方法將對象轉(zhuǎn)換為字符串,該方法返回[object Object]。
- xyx === y55true1'1'falsenullundefinedfalse0falsefalse'1,2'[1,2]false'[object Object]'{}false
如果使用===運(yùn)算符,則第一個(gè)示例以外的所有比較將返回false,因?yàn)樗鼈兊念愋筒煌?,而第一個(gè)示例將返回true,因?yàn)閮烧叩念愋秃椭迪嗤?/p>
15. 為什么在 JS 中比較兩個(gè)相似的對象時(shí)返回 false?
先看下面的例子:
- leta={a:1};letb={a:1};letc=a;console.log(a===b);//打印false,即使它們有相同的屬性console.log(a===c);//true
JS 以不同的方式比較對象和基本類型。在基本類型中,JS 通過值對它們進(jìn)行比較,而在對象中,JS 通過引用或存儲變量的內(nèi)存中的地址對它們進(jìn)行比較。這就是為什么第一個(gè)console.log語句返回false,而第二個(gè)console.log語句返回true。a和c有相同的引用地址,而a和b沒有。
16. !! 運(yùn)算符能做什么?
!!運(yùn)算符可以將右側(cè)的值強(qiáng)制轉(zhuǎn)換為布爾值,這也是將值轉(zhuǎn)換為布爾值的一種簡單方法。
- console.log(!!null);//falseconsole.log(!!undefined);//falseconsole.log(!!'');//falseconsole.log(!!0);//falseconsole.log(!!NaN);//falseconsole.log(!!'');//trueconsole.log(!!{});//trueconsole.log(!![]);//trueconsole.log(!!1);//trueconsole.log(!![].length);//false
17. 如何在一行中計(jì)算多個(gè)表達(dá)式的值?
可以使用逗號運(yùn)算符在一行中計(jì)算多個(gè)表達(dá)式。它從左到右求值,并返回右邊最后一個(gè)項(xiàng)目或最后一個(gè)操作數(shù)的值。
- letx=5;x=(x++,x=addFive(x),x*=2,x-=5,x+=10);functionaddFive(num){returnnum+5;}
上面的結(jié)果最后得到x的值為27。首先,我們將x的值增加到6,然后調(diào)用函數(shù)addFive(6)并將6作為參數(shù)傳遞并將結(jié)果重新分配給x,此時(shí)x的值為11。之后,將x的當(dāng)前值乘以2并將其分配給x,x的更新值為22。然后,將x的當(dāng)前值減去5并將結(jié)果分配給x x更新后的值為17。最后,我們將x的值增加10,然后將更新的值分配給x,最終x的值為27。
18. 什么是提升?
提升是用來描述變量和函數(shù)移動到其(全局或函數(shù))作用域頂部的術(shù)語。
為了理解提升,需要來了解一下執(zhí)行上下文。執(zhí)行上下文是當(dāng)前正在執(zhí)行的“代碼環(huán)境”。執(zhí)行上下文有兩個(gè)階段:編譯和執(zhí)行。
- 編譯-在此階段,JS 引薦獲取所有函數(shù)聲明并將其提升到其作用域的頂部,以便我們稍后可以引用它們并獲取所有變量聲明(使用var關(guān)鍵字進(jìn)行聲明),還會為它們提供默認(rèn)值:undefined。
- 執(zhí)行——在這個(gè)階段中,它將值賦給之前提升的變量,并執(zhí)行或調(diào)用函數(shù)(對象中的方法)。
注意:只有使用var聲明的變量,或者函數(shù)聲明才會被提升,相反,函數(shù)表達(dá)式或箭頭函數(shù),let和const聲明的變量,這些都不會被提升。
假設(shè)在全局使用域,有如下的代碼:
- console.log(y);y=1;console.log(y);console.log(greet("Mark"));functiongreet(name){return'Hello'+name+'!';}vary;
上面分別打?。簎ndefined,1, Hello Mark!。
上面代碼在編譯階段其實(shí)是這樣的:
- functiongreet(name){return'Hello'+name+'!';}vary;//默認(rèn)值undefined//等待“編譯”階段完成,然后開始“執(zhí)行”階段/*console.log(y);y=1;console.log(y);console.log(greet("Mark"));*/
編譯階段完成后,它將啟動執(zhí)行階段調(diào)用方法,并將值分配給變量。
- functiongreet(name){return'Hello'+name+'!';}vary;//start"execution"phaseconsole.log(y);y=1;console.log(y);console.log(greet("Mark"));
19. 什么是作用域?
JavaScript 中的作用域是我們可以有效訪問變量或函數(shù)的區(qū)域。JS 有三種類型的作用域:全局作用域、函數(shù)作用域和塊作用域(ES6)。
- 全局作用域——在全局命名空間中聲明的變量或函數(shù)位于全局作用域中,因此在代碼中的任何地方都可以訪問它們。
- //globalnamespacevarg="global";functionglobalFunc(){functioninnerFunc(){console.log(g);//canaccess"g"because"g"isaglobalvariable}innerFunc();}
- functionmyFavoriteFunc(a){if(true){varb="Hello"+a;}returnb;}myFavoriteFunc("World");console.log(a);//ThrowsaReferenceError"a"isnotdefinedconsole.log(b);//doesnotcontinuehere
- functiontestBlock(){if(true){letz=5;}returnz;}testBlock();//ThrowsaReferenceError"z"isnotdefined
- /*作用域鏈內(nèi)部作用域->外部作用域->全局作用域*///全局作用域varvariable1="Comrades";varvariable2="Sayonara";functionouter(){//外部作用域varvariable1="World";functioninner(){//內(nèi)部作用域varvariable2="Hello";console.log(variable2+""+variable1);}inner();}outer();//HelloWorld
20. 什么是閉包?
這可能是所有問題中最難的一個(gè)問題,因?yàn)殚]包是一個(gè)有爭議的話題,這里從個(gè)人角度來談?wù)?,如果不妥,多多海涵?/p>
閉包就是一個(gè)函數(shù)在聲明時(shí)能夠記住當(dāng)前作用域、父函數(shù)作用域、及父函數(shù)作用域上的變量和參數(shù)的引用,直至通過作用域鏈上全局作用域,基本上閉包是在聲明函數(shù)時(shí)創(chuàng)建的作用域。
看看小例子:
- //全局作用域varglobalVar="abc";functiona(){console.log(globalVar);}a();//"abc"
在此示例中,當(dāng)我們聲明a函數(shù)時(shí),全局作用域是a閉包的一部分。
變量globalVar在圖中沒有值的原因是該變量的值可以根據(jù)調(diào)用函數(shù)a的位置和時(shí)間而改變。但是在上面的示例中,globalVar變量的值為abc。
來看一個(gè)更復(fù)雜的例子:
- varglobalVar="global";varouterVar="outer"functionouterFunc(outerParam){functioninnerFunc(innerParam){console.log(globalVar,outerParam,innerParam);}returninnerFunc;}constx=outerFunc(outerVar);outerVar="outer-2";globalVar="guess"x("inner");
上面打印結(jié)果是 guess outer inner。
當(dāng)我們調(diào)用outerFunc函數(shù)并將返回值innerFunc函數(shù)分配給變量x時(shí),即使我們?yōu)閛uterVar變量分配了新值outer-2,outerParam也繼續(xù)保留outer值,因?yàn)橹匦路峙涫窃谡{(diào)用outerFunc之后發(fā)生的,并且當(dāng)我們調(diào)用outerFunc函數(shù)時(shí),它會在作用域鏈中查找outerVar的值,此時(shí)的outerVar的值將為 "outer"。
現(xiàn)在,當(dāng)我們調(diào)用引用了innerFunc的x變量時(shí),innerParam將具有一個(gè)inner值,因?yàn)檫@是我們在調(diào)用中傳遞的值,而globalVar變量值為guess,因?yàn)樵谡{(diào)用x變量之前,我們將一個(gè)新值分配給globalVar。
下面這個(gè)示例演示沒有理解好閉包所犯的錯(cuò)誤:
- constarrFuncs=[];for(vari=0;i<5;i++){arrFuncs.push(function(){returni;});}console.log(i);//iis5for(leti=0;i<arrFuncs.length;i++){console.log(arrFuncs[i]());//都打印5}
由于閉包,此代碼無法正常運(yùn)行。var關(guān)鍵字創(chuàng)建一個(gè)全局變量,當(dāng)我們 push 一個(gè)函數(shù)時(shí),這里返回的全局變量i。因此,當(dāng)我們在循環(huán)后在該數(shù)組中調(diào)用其中一個(gè)函數(shù)時(shí),它會打印5,因?yàn)槲覀兊玫絠的當(dāng)前值為5,我們可以訪問它,因?yàn)樗侨肿兞俊?/p>
因?yàn)殚]包在創(chuàng)建變量時(shí)會保留該變量的引用而不是其值。我們可以使用IIFES或使用 let 來代替 var 的聲明。
21. JavaScript 中的虛值是什么?
- constfalsyValues=['',0,null,undefined,NaN,false];
簡單的來說虛值就是是在轉(zhuǎn)換為布爾值時(shí)變?yōu)?false 的值。
22. 如何檢查值是否虛值?
使用 Boolean 函數(shù)或者 !! 運(yùn)算符。
23. 'use strict' 是干嘛用的?
"use strict" 是 ES5 特性,它使我們的代碼在函數(shù)或整個(gè)腳本中處于嚴(yán)格模式。嚴(yán)格模式幫助我們在代碼的早期避免 bug,并為其添加限制。
嚴(yán)格模式的一些限制:
- 變量必須聲明后再使用
- 函數(shù)的參數(shù)不能有同名屬性,否則報(bào)錯(cuò)
- 不能使用with語句
- 不能對只讀屬性賦值,否則報(bào)錯(cuò)
- 不能使用前綴 0 表示八進(jìn)制數(shù),否則報(bào)錯(cuò)
- 不能刪除不可刪除的屬性,否則報(bào)錯(cuò)
- 不能刪除變量delete prop,會報(bào)錯(cuò),只能刪除屬性delete global[prop]
- eval不能在它的外層作用域引入變量
- eval和arguments不能被重新賦值
- arguments不會自動反映函數(shù)參數(shù)的變化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局對象
- 不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
- 增加了保留字(比如protected、static和interface)
設(shè)立”嚴(yán)格模式”的目的,主要有以下幾個(gè):
- 消除Javascript語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;
- 消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;
- 提高編譯器效率,增加運(yùn)行速度;
- 為未來新版本的Javascript做好鋪墊。
24. JavaScript 中 `this` 值是什么?
基本上,this指的是當(dāng)前正在執(zhí)行或調(diào)用該函數(shù)的對象的值。this值的變化取決于我們使用它的上下文和我們在哪里使用它。
- constcarDetails={name:"FordMustang",yearBought:2005,getName(){returnthis.name;},isRegistered:true};console.log(carDetails.getName());//FordMustang
這通常是我們期望結(jié)果的,因?yàn)樵趃etName方法中我們返回this.name,在此上下文中,this指向的是carDetails對象,該對象當(dāng)前是執(zhí)行函數(shù)的“所有者”對象。
接下我們做些奇怪的事情:
- varname="FordRanger";vargetCarName=carDetails.getName;console.log(getCarName());//FordRanger
上面打印Ford Ranger,這很奇怪,因?yàn)樵诘谝粋€(gè)console.log語句中打印的是Ford Mustang。這樣做的原因是getCarName方法有一個(gè)不同的“所有者”對象,即window對象。在全局作用域中使用var關(guān)鍵字聲明變量會在window對象中附加與變量名稱相同的屬性。請記住,當(dāng)沒有使用“use strict”時(shí),在全局作用域中this指的是window對象。
- console.log(getCarName===window.getCarName);//trueconsole.log(getCarName===this.getCarName);//true
本例中的this和window引用同一個(gè)對象。
解決這個(gè)問題的一種方法是在函數(shù)中使用apply和call方法。
- console.log(getCarName.apply(carDetails));//FordMustangconsole.log(getCarName.call(carDetails));//FordMustang
apply和call方法期望第一個(gè)參數(shù)是一個(gè)對象,該對象是函數(shù)內(nèi)部this的值。
IIFE或立即執(zhí)行的函數(shù)表達(dá)式,在全局作用域內(nèi)聲明的函數(shù),對象內(nèi)部方法中的匿名函數(shù)和內(nèi)部函數(shù)的this具有默認(rèn)值,該值指向window對象。
- (function(){console.log(this);})();//打印"window"對象functioniHateThis(){console.log(this);}iHateThis();//打印"window"對象constmyFavoriteObj={guessThis(){functiongetName(){console.log(this.name);}getName();},name:'MarkoPolo',thisIsAnnoying(callback){callback();}};myFavoriteObj.guessThis();//打印"window"對象myFavoriteObj.thisIsAnnoying(function(){console.log(this);//打印"window"對象});
如果我們要獲取myFavoriteObj對象中的name屬性(即Marko Polo)的值,則有兩種方法可以解決此問題。
一種是將 this 值保存在變量中。
- constmyFavoriteObj={guessThis(){constself=this;//把this值保存在self變量中functiongetName(){console.log(self.name);}getName();},name:'MarkoPolo',thisIsAnnoying(callback){callback();}};
第二種方式是使用箭頭函數(shù)
- constmyFavoriteObj={guessThis(){constgetName=()=>{console.log(this.name);}getName();},name:'MarkoPolo',thisIsAnnoying(callback){callback();}};
箭頭函數(shù)沒有自己的 this。它復(fù)制了這個(gè)封閉的詞法作用域中this值,在這個(gè)例子中,this值在getName內(nèi)部函數(shù)之外,也就是myFavoriteObj對象。
25. 對象的 prototype(原型) 是什么?
簡單地說,原型就是對象的藍(lán)圖。如果它存在當(dāng)前對象中,則將其用作屬性和方法的回退。它是在對象之間共享屬性和功能的方法,這也是JavaScript實(shí)現(xiàn)繼承的核心。
- consto={};console.log(o.toString());//logs[objectObject]
即使o對象中不存在o.toString方法,它也不會引發(fā)錯(cuò)誤,而是返回字符串[object Object]。當(dāng)對象中不存在屬性時(shí),它將查看其原型,如果仍然不存在,則將其查找到原型的原型,依此類推,直到在原型鏈中找到具有相同屬性的屬性為止。原型鏈的末尾是Object.prototype。
- console.log(o.toString===Object.prototype.toString);//logstrue