看完這幾道 JavaScript 面試題,讓你與考官對答如流(上)
1.undefined 和 null 有什么區(qū)別?
在理解undefined和null之間的差異之前,我們先來看看它們的相似類。
它們屬于 JavaScript 的 7 種基本類型。
- letprimitiveTypes=['string','number','null','undefined','boolean','symbol','bigint'];
它們是屬于虛值,可以使用Boolean(value)或!!value將其轉(zhuǎn)換為布爾值時,值為false。
- console.log(!!null);//falseconsole.log(!!undefined);//falseconsole.log(Boolean(null));//falseconsole.log(Boolean(undefined));//false
接著來看看它們的區(qū)別。
undefined是未指定特定值的變量的默認值,或者沒有顯式返回值的函數(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是已明確定義給變量的值。在此示例中,當fs.readFile方法未引發(fā)錯誤時,我們將獲得null值。
- fs.readFile('path/to/file',(e,data)=>{console.log(e);//當沒有錯誤發(fā)生時,打印nullif(e){console.log(e);}console.log(data);});
在比較null和undefined時,我們使用==時得到true,使用===時得到false:
- console.log(null==undefined);//trueconsole.log(null===undefined);//false
2. && 運算符能做什么
&& 也可以叫邏輯與,在其操作數(shù)中找到第一個虛值表達式并返回它,如果沒有找到任何虛值表達式,則返回最后一個真值表達式。它采用短路來防止不必要的工作。
- 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. || 運算符能做什么
||也叫或邏輯或,在其操作數(shù)中找到第一個真值表達式并返回它。這也使用了短路來防止不必要的工作。在支持 ES6 默認函數(shù)參數(shù)之前,它用于初始化函數(shù)中的默認參數(shù)值。
- console.log(null||1||undefined);//1functionlogName(name){varn=name||"Mark";console.log(n);}logName();//"Mark"
4. 使用 + 或一元加運算符是將字符串轉(zhuǎn)換為數(shù)字的最快方法嗎?
根據(jù)MDN文檔,+是將字符串轉(zhuǎn)換為數(shù)字的最快方法,因為如果值已經(jīng)是數(shù)字,它不會執(zhí)行任何操作。
5. DOM 是什么?
DOM 代表文檔對象模型,是 HTML 和 XML 文檔的接口(API)。當瀏覽器第一次讀取(解析)HTML文檔時,它會創(chuàng)建一個大對象,一個基于 HTM L文檔的非常大的對象,這就是DOM。它是一個從 HTML 文檔中建模的樹狀結(jié)構(gòu)。DOM 用于交互和修改DOM結(jié)構(gòu)或特定元素或節(jié)點。
假設(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>
等價的DOM是這樣的:
JS 中的document對象表示DOM。它為我們提供了許多方法,我們可以使用這些方法來選擇元素來更新元素內(nèi)容,等等。
6. 什么是事件傳播?
當事件發(fā)生在DOM元素上時,該事件并不完全發(fā)生在那個元素上。在“冒泡階段”中,事件冒泡或向上傳播至父級,祖父母,祖父母或父級,直到到達window為止;而在“捕獲階段”中,事件從window開始向下觸發(fā)元素 事件或event.target。
事件傳播有三個階段:
- 捕獲階段–事件從 window 開始,然后向下到每個元素,直到到達目標元素。
- 目標階段–事件已達到目標元素。
- 冒泡階段–事件從目標元素冒泡,然后上升到每個元素,直到到達 window。
7. 什么是事件冒泡?
當事件發(fā)生在DOM元素上時,該事件并不完全發(fā)生在那個元素上。在冒泡階段,事件冒泡,或者事件發(fā)生在它的父代,祖父母,祖父母的父代,直到到達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方法具有第三個可選參數(shù)useCapture,其默認值為false,事件將在冒泡階段中發(fā)生,如果為true,則事件將在捕獲階段中發(fā)生。如果單擊child元素,它將分別在控制臺上記錄child,parent,grandparent,html,document和window,這就是事件冒泡。
8. 什么是事件捕獲?
當事件發(fā)生在 DOM 元素上時,該事件并不完全發(fā)生在那個元素上。在捕獲階段,事件從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方法具有第三個可選參數(shù)useCapture,其默認值為false,事件將在冒泡階段中發(fā)生,如果為true,則事件將在捕獲階段中發(fā)生。如果單擊child元素,它將分別在控制臺上打印window,document,html,grandparent和parent,這就是事件捕獲。
9. event.preventDefault() 和 event.stopPropagation()方法之間有什么區(qū)別?
event.preventDefault() 方法可防止元素的默認行為。如果在表單元素中使用,它將阻止其提交。如果在錨元素中使用,它將阻止其導航。如果在上下文菜單中使用,它將阻止其顯示或顯示。event.stopPropagation()方法用于阻止捕獲和冒泡階段中當前事件的進一步傳播。
10. 如何知道是否在元素中使用了`event.preventDefault()`方法?
我們可以在事件對象中使用event.defaultPrevented屬性。它返回一個布爾值用來表明是否在特定元素中調(diào)用了event.preventDefault()。
11. 為什么此代碼 `obj.someprop.x` 會引發(fā)錯誤?
- constobj={};console.log(obj.someprop.x);
顯然,由于我們嘗試訪問someprop屬性中的x屬性,而 someprop 并沒有在對象中,所以值為 undefined。記住對象本身不存在的屬性,并且其原型的默認值為undefined。因為undefined沒有屬性x,所以試圖訪問將會報錯。
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 標簽,因此我們可以得出結(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標簽。在此示例中,我們可以得出結(jié)論,event.currentTarget是附加事件處理程序的元素。
14. == 和 === 有什么區(qū)別?
==用于一般比較,===用于嚴格比較,==在比較的時候可以轉(zhuǎn)換數(shù)據(jù)類型,===嚴格比較,只要類型不匹配就返回flase。
先來看看 == 這兄弟:
強制是將值轉(zhuǎn)換為另一種類型的過程。在這種情況下,==會執(zhí)行隱式強制。在比較兩個值之前,==需要執(zhí)行一些規(guī)則。
假設(shè)我們要比較x == y的值。
- 如果x和y的類型相同,則 JS 會換成===操作符進行比較。
- 如果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方法來獲取該對象的原始值。
舉個例子。
- xyx == y55true1'1'truenullundefinedtrue0falsetrue'1,2'[1,2]true'[object Object]'{}true
這些例子都返回true。
第一個示例符合條件1,因為x和y具有相同的類型和值。
第二個示例符合條件4,在比較之前將y轉(zhuǎn)換為數(shù)字。
第三個例子符合條件2。
第四個例子符合條件7,因為y是boolean類型。
第五個示例符合條件8。使用toString()方法將數(shù)組轉(zhuǎn)換為字符串,該方法返回1,2。
最后一個示例符合條件8。使用toString()方法將對象轉(zhuǎn)換為字符串,該方法返回[object Object]。
- xyx === y55true1'1'falsenullundefinedfalse0falsefalse'1,2'[1,2]false'[object Object]'{}false
如果使用===運算符,則第一個示例以外的所有比較將返回false,因為它們的類型不同,而第一個示例將返回true,因為兩者的類型和值相同。
15. 為什么在 JS 中比較兩個相似的對象時返回 false?
先看下面的例子:
- leta={a:1};letb={a:1};letc=a;console.log(a===b);//打印false,即使它們有相同的屬性console.log(a===c);//true
JS 以不同的方式比較對象和基本類型。在基本類型中,JS 通過值對它們進行比較,而在對象中,JS 通過引用或存儲變量的內(nèi)存中的地址對它們進行比較。這就是為什么第一個console.log語句返回false,而第二個console.log語句返回true。a和c有相同的引用地址,而a和b沒有。
16. !! 運算符能做什么?
!!運算符可以將右側(cè)的值強制轉(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. 如何在一行中計算多個表達式的值?
可以使用逗號運算符在一行中計算多個表達式。它從左到右求值,并返回右邊最后一個項目或最后一個操作數(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,此時x的值為11。之后,將x的當前值乘以2并將其分配給x,x的更新值為22。然后,將x的當前值減去5并將結(jié)果分配給x x更新后的值為17。最后,我們將x的值增加10,然后將更新的值分配給x,最終x的值為27。
18. 什么是提升?
提升是用來描述變量和函數(shù)移動到其(全局或函數(shù))作用域頂部的術(shù)語。
為了理解提升,需要來了解一下執(zhí)行上下文。執(zhí)行上下文是當前正在執(zhí)行的“代碼環(huán)境”。執(zhí)行上下文有兩個階段:編譯和執(zhí)行。
- 編譯-在此階段,JS 引薦獲取所有函數(shù)聲明并將其提升到其作用域的頂部,以便我們稍后可以引用它們并獲取所有變量聲明(使用var關(guān)鍵字進行聲明),還會為它們提供默認值:undefined。
- 執(zhí)行——在這個階段中,它將值賦給之前提升的變量,并執(zhí)行或調(diào)用函數(shù)(對象中的方法)。
注意:只有使用var聲明的變量,或者函數(shù)聲明才會被提升,相反,函數(shù)表達式或箭頭函數(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!。
上面代碼在編譯階段其實是這樣的:
- functiongreet(name){return'Hello'+name+'!';}vary;//默認值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. 什么是閉包?
這可能是所有問題中最難的一個問題,因為閉包是一個有爭議的話題,這里從個人角度來談談,如果不妥,多多海涵。
閉包就是一個函數(shù)在聲明時能夠記住當前作用域、父函數(shù)作用域、及父函數(shù)作用域上的變量和參數(shù)的引用,直至通過作用域鏈上全局作用域,基本上閉包是在聲明函數(shù)時創(chuàng)建的作用域。
看看小例子:
- //全局作用域varglobalVar="abc";functiona(){console.log(globalVar);}a();//"abc"
在此示例中,當我們聲明a函數(shù)時,全局作用域是a閉包的一部分。
變量globalVar在圖中沒有值的原因是該變量的值可以根據(jù)調(diào)用函數(shù)a的位置和時間而改變。但是在上面的示例中,globalVar變量的值為abc。
來看一個更復雜的例子:
- 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。
當我們調(diào)用outerFunc函數(shù)并將返回值innerFunc函數(shù)分配給變量x時,即使我們?yōu)閛uterVar變量分配了新值outer-2,outerParam也繼續(xù)保留outer值,因為重新分配是在調(diào)用outerFunc之后發(fā)生的,并且當我們調(diào)用outerFunc函數(shù)時,它會在作用域鏈中查找outerVar的值,此時的outerVar的值將為 "outer"。
現(xiàn)在,當我們調(diào)用引用了innerFunc的x變量時,innerParam將具有一個inner值,因為這是我們在調(diào)用中傳遞的值,而globalVar變量值為guess,因為在調(diào)用x變量之前,我們將一個新值分配給globalVar。
下面這個示例演示沒有理解好閉包所犯的錯誤:
- 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}
由于閉包,此代碼無法正常運行。var關(guān)鍵字創(chuàng)建一個全局變量,當我們 push 一個函數(shù)時,這里返回的全局變量i。因此,當我們在循環(huán)后在該數(shù)組中調(diào)用其中一個函數(shù)時,它會打印5,因為我們得到i的當前值為5,我們可以訪問它,因為它是全局變量。
因為閉包在創(chuàng)建變量時會保留該變量的引用而不是其值。我們可以使用IIFES或使用 let 來代替 var 的聲明。
21. JavaScript 中的虛值是什么?
- constfalsyValues=['',0,null,undefined,NaN,false];
簡單的來說虛值就是是在轉(zhuǎn)換為布爾值時變?yōu)?false 的值。
22. 如何檢查值是否虛值?
使用 Boolean 函數(shù)或者 !! 運算符。
23. 'use strict' 是干嘛用的?
"use strict" 是 ES5 特性,它使我們的代碼在函數(shù)或整個腳本中處于嚴格模式。嚴格模式幫助我們在代碼的早期避免 bug,并為其添加限制。
嚴格模式的一些限制:
- 變量必須聲明后再使用
- 函數(shù)的參數(shù)不能有同名屬性,否則報錯
- 不能使用with語句
- 不能對只讀屬性賦值,否則報錯
- 不能使用前綴 0 表示八進制數(shù),否則報錯
- 不能刪除不可刪除的屬性,否則報錯
- 不能刪除變量delete prop,會報錯,只能刪除屬性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è)立”嚴格模式”的目的,主要有以下幾個:
- 消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
- 消除代碼運行的一些不安全之處,保證代碼運行的安全;
- 提高編譯器效率,增加運行速度;
- 為未來新版本的Javascript做好鋪墊。
24. JavaScript 中 `this` 值是什么?
基本上,this指的是當前正在執(zhí)行或調(diào)用該函數(shù)的對象的值。this值的變化取決于我們使用它的上下文和我們在哪里使用它。
- constcarDetails={name:"FordMustang",yearBought:2005,getName(){returnthis.name;},isRegistered:true};console.log(carDetails.getName());//FordMustang
這通常是我們期望結(jié)果的,因為在getName方法中我們返回this.name,在此上下文中,this指向的是carDetails對象,該對象當前是執(zhí)行函數(shù)的“所有者”對象。
接下我們做些奇怪的事情:
- varname="FordRanger";vargetCarName=carDetails.getName;console.log(getCarName());//FordRanger
上面打印Ford Ranger,這很奇怪,因為在第一個console.log語句中打印的是Ford Mustang。這樣做的原因是getCarName方法有一個不同的“所有者”對象,即window對象。在全局作用域中使用var關(guān)鍵字聲明變量會在window對象中附加與變量名稱相同的屬性。請記住,當沒有使用“use strict”時,在全局作用域中this指的是window對象。
- console.log(getCarName===window.getCarName);//trueconsole.log(getCarName===this.getCarName);//true
本例中的this和window引用同一個對象。
解決這個問題的一種方法是在函數(shù)中使用apply和call方法。
- console.log(getCarName.apply(carDetails));//FordMustangconsole.log(getCarName.call(carDetails));//FordMustang
apply和call方法期望第一個參數(shù)是一個對象,該對象是函數(shù)內(nèi)部this的值。
IIFE或立即執(zhí)行的函數(shù)表達式,在全局作用域內(nèi)聲明的函數(shù),對象內(nèi)部方法中的匿名函數(shù)和內(nèi)部函數(shù)的this具有默認值,該值指向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。它復制了這個封閉的詞法作用域中this值,在這個例子中,this值在getName內(nèi)部函數(shù)之外,也就是myFavoriteObj對象。
25. 對象的 prototype(原型) 是什么?
簡單地說,原型就是對象的藍圖。如果它存在當前對象中,則將其用作屬性和方法的回退。它是在對象之間共享屬性和功能的方法,這也是JavaScript實現(xiàn)繼承的核心。
- consto={};console.log(o.toString());//logs[objectObject]
即使o對象中不存在o.toString方法,它也不會引發(fā)錯誤,而是返回字符串[object Object]。當對象中不存在屬性時,它將查看其原型,如果仍然不存在,則將其查找到原型的原型,依此類推,直到在原型鏈中找到具有相同屬性的屬性為止。原型鏈的末尾是Object.prototype。
- console.log(o.toString===Object.prototype.toString);//logstrue