看完这几道JavaScript面试题,让你与考官对答如流(上)

1.undefined 和 null 有什么区别?

目前成都创新互联公司已为千余家的企业提供了网站建设、域名、网络空间、网站托管、企业网站设计、兰溪网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

在理解undefined和null之间的差异之前,我们先来看看它们的相似类。

它们属于 JavaScript 的 7 种基本类型。

 
 
 
 
  1. letprimitiveTypes=['string','number','null','undefined','boolean','symbol','bigint']; 

它们是属于虚值,可以使用Boolean(value)或!!value将其转换为布尔值时,值为false。

 
 
 
 
  1. console.log(!!null);//falseconsole.log(!!undefined);//falseconsole.log(Boolean(null));//falseconsole.log(Boolean(undefined));//false 

接着来看看它们的区别。

undefined是未指定特定值的变量的默认值,或者没有显式返回值的函数,如:console.log(1),还包括对象中不存在的属性,这些 JS 引擎都会为其分配 undefined 值。

 
 
 
 
  1. 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方法未引发错误时,我们将获得null值。

 
 
 
 
  1. fs.readFile('path/to/file',(e,data)=>{console.log(e);//当没有错误发生时,打印nullif(e){console.log(e);}console.log(data);}); 

在比较null和undefined时,我们使用==时得到true,使用===时得到false:

 
 
 
 
  1. console.log(null==undefined);//trueconsole.log(null===undefined);//false 

2. && 运算符能做什么

&& 也可以叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。

 
 
 
 
  1. console.log(false&&1&&[]);//falseconsole.log(""&&true&&5);//5 

使用if语句

 
 
 
 
  1. constrouter:Router=Router();router.get('/endpoint',(req:Request,res:Response)=>{letconMobile:PoolConnection;try{//dosomedboperations}catch(e){if(conMobile){conMobile.release();}}}); 

使用&&操作符

 
 
 
 
  1. constrouter:Router=Router();router.get('/endpoint',(req:Request,res:Response)=>{letconMobile:PoolConnection;try{//dosomedboperations}catch(e){conMobile&&conMobile.release()}}); 

3. || 运算符能做什么

||也叫或逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。

 
 
 
 
  1. console.log(null||1||undefined);//1functionlogName(name){varn=name||"Mark";console.log(n);}logName();//"Mark" 

4. 使用 + 或一元加运算符是将字符串转换为数字的最快方法吗?

根据MDN文档,+是将字符串转换为数字的最快方法,因为如果值已经是数字,它不会执行任何操作。

5. DOM 是什么?

DOM 代表文档对象模型,是 HTML 和 XML 文档的接口(API)。当浏览器第一次读取(解析)HTML文档时,它会创建一个大对象,一个基于 HTM L文档的非常大的对象,这就是DOM。它是一个从 HTML 文档中建模的树状结构。DOM 用于交互和修改DOM结构或特定元素或节点。

假设我们有这样的 HTML 结构:

 
 
 
 
  1. DocumentObjectModel

     

等价的DOM是这样的:

JS 中的document对象表示DOM。它为我们提供了许多方法,我们可以使用这些方法来选择元素来更新元素内容,等等。

6. 什么是事件传播?

当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在“冒泡阶段”中,事件冒泡或向上传播至父级,祖父母,祖父母或父级,直到到达window为止;而在“捕获阶段”中,事件从window开始向下触发元素 事件或event.target。

事件传播有三个阶段:

  • 捕获阶段–事件从 window 开始,然后向下到每个元素,直到到达目标元素。
  • 目标阶段–事件已达到目标元素。
  • 冒泡阶段–事件从目标元素冒泡,然后上升到每个元素,直到到达 window。

7. 什么是事件冒泡?

当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,祖父母的父代,直到到达window为止。

假设有如下的 HTML 结构:

 
 
 
 
  1. 1
 

对应的 JS 代码:

 
 
 
 
  1. 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方法具有第三个可选参数useCapture,其默认值为false,事件将在冒泡阶段中发生,如果为true,则事件将在捕获阶段中发生。如果单击child元素,它将分别在控制台上记录child,parent,grandparent,html,document和window,这就是事件冒泡。

8. 什么是事件捕获?

当事件发生在 DOM 元素上时,该事件并不完全发生在那个元素上。在捕获阶段,事件从window开始,一直到触发事件的元素。

假设有如下的 HTML 结构:

 
 
 
 

对应的 JS 代码:

 
 
 
 
  1. 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方法具有第三个可选参数useCapture,其默认值为false,事件将在冒泡阶段中发生,如果为true,则事件将在捕获阶段中发生。如果单击child元素,它将分别在控制台上打印window,document,html,grandparent和parent,这就是事件捕获。

9. event.preventDefault() 和 event.stopPropagation()方法之间有什么区别?

event.preventDefault() 方法可防止元素的默认行为。如果在表单元素中使用,它将阻止其提交。如果在锚元素中使用,它将阻止其导航。如果在上下文菜单中使用,它将阻止其显示或显示。event.stopPropagation()方法用于阻止捕获和冒泡阶段中当前事件的进一步传播。

10. 如何知道是否在元素中使用了`event.preventDefault()`方法?

我们可以在事件对象中使用event.defaultPrevented属性。它返回一个布尔值用来表明是否在特定元素中调用了event.preventDefault()。

11. 为什么此代码 `obj.someprop.x` 会引发错误?

 
 
 
 
  1. constobj={};console.log(obj.someprop.x); 

显然,由于我们尝试访问someprop属性中的x属性,而 someprop 并没有在对象中,所以值为 undefined。记住对象本身不存在的属性,并且其原型的默认值为undefined。因为undefined没有属性x,所以试图访问将会报错。

12. 什么是 event.target ?

简单来说,event.target是发生事件的元素或触发事件的元素。

假设有如下的 HTML 结构:

 
 
 
 
  1. Button 

JS 代码如下:

 
 
 
 
  1. functionclickFunc(event){console.log(event.target);} 

如果单击 button,即使我们将事件附加在最外面的div上,它也将打印 button 标签,因此我们可以得出结论event.target是触发事件的元素。

13. 什么是 event.currentTarget??

event.currentTarget是我们在其上显式附加事件处理程序的元素。

假设有如下的 HTML 结构:

 
 
 
 
  1. Button 

JS 代码如下:

 
 
 
 
  1. functionclickFunc(event){console.log(event.currentTarget);} 

如果单击 button,即使我们单击该 button,它也会打印最外面的div标签。在此示例中,我们可以得出结论,event.currentTarget是附加事件处理程序的元素。

14. == 和 === 有什么区别?

==用于一般比较,===用于严格比较,==在比较的时候可以转换数据类型,===严格比较,只要类型不匹配就返回flase。

先来看看 == 这兄弟:

强制是将值转换为另一种类型的过程。在这种情况下,==会执行隐式强制。在比较两个值之前,==需要执行一些规则。

假设我们要比较x == y的值。

注意:toPrimitive首先在对象中使用valueOf方法,然后使用toString方法来获取该对象的原始值。

举个例子。

 
 
 
 
  1. xyx == y55true1'1'truenullundefinedtrue0falsetrue'1,2'[1,2]true'[object Object]'{}true 

这些例子都返回true。

第一个示例符合条件1,因为x和y具有相同的类型和值。

第二个示例符合条件4,在比较之前将y转换为数字。

第三个例子符合条件2。

第四个例子符合条件7,因为y是boolean类型。

第五个示例符合条件8。使用toString()方法将数组转换为字符串,该方法返回1,2。

最后一个示例符合条件8。使用toString()方法将对象转换为字符串,该方法返回[object Object]。

 
 
 
 
  1. xyx === y55true1'1'falsenullundefinedfalse0falsefalse'1,2'[1,2]false'[object Object]'{}false 

如果使用===运算符,则第一个示例以外的所有比较将返回false,因为它们的类型不同,而第一个示例将返回true,因为两者的类型和值相同。

15. 为什么在 JS 中比较两个相似的对象时返回 false?

先看下面的例子:

 
 
 
 
  1. leta={a:1};letb={a:1};letc=a;console.log(a===b);//打印false,即使它们有相同的属性console.log(a===c);//true 

JS 以不同的方式比较对象和基本类型。在基本类型中,JS 通过值对它们进行比较,而在对象中,JS 通过引用或存储变量的内存中的地址对它们进行比较。这就是为什么第一个console.log语句返回false,而第二个console.log语句返回true。a和c有相同的引用地址,而a和b没有。

16. !! 运算符能做什么?

!!运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。

 
 
 
 
  1. 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. 如何在一行中计算多个表达式的值?

可以使用逗号运算符在一行中计算多个表达式。它从左到右求值,并返回右边最后一个项目或最后一个操作数的值。

 
 
 
 
  1. letx=5;x=(x++,x=addFive(x),x*=2,x-=5,x+=10);functionaddFive(num){returnnum+5;} 

上面的结果最后得到x的值为27。首先,我们将x的值增加到6,然后调用函数addFive(6)并将6作为参数传递并将结果重新分配给x,此时x的值为11。之后,将x的当前值乘以2并将其分配给x,x的更新值为22。然后,将x的当前值减去5并将结果分配给x x更新后的值为17。最后,我们将x的值增加10,然后将更新的值分配给x,最终x的值为27。

18. 什么是提升?

提升是用来描述变量和函数移动到其(全局或函数)作用域顶部的术语。

为了理解提升,需要来了解一下执行上下文。执行上下文是当前正在执行的“代码环境”。执行上下文有两个阶段:编译和执行。

注意:只有使用var声明的变量,或者函数声明才会被提升,相反,函数表达式或箭头函数,let和const声明的变量,这些都不会被提升。

假设在全局使用域,有如下的代码:

 
 
 
 
  1. console.log(y);y=1;console.log(y);console.log(greet("Mark"));functiongreet(name){return'Hello'+name+'!';}vary; 

上面分别打印:undefined,1, Hello Mark!。

上面代码在编译阶段其实是这样的:

 
 
 
 
  1. functiongreet(name){return'Hello'+name+'!';}vary;//默认值undefined//等待“编译”阶段完成,然后开始“执行”阶段/*console.log(y);y=1;console.log(y);console.log(greet("Mark"));*/ 

编译阶段完成后,它将启动执行阶段调用方法,并将值分配给变量。

 
 
 
 
  1. functiongreet(name){return'Hello'+name+'!';}vary;//start"execution"phaseconsole.log(y);y=1;console.log(y);console.log(greet("Mark")); 

19. 什么是作用域?

JavaScript 中的作用域是我们可以有效访问变量或函数的区域。JS 有三种类型的作用域:全局作用域、函数作用域和块作用域(ES6)。

20. 什么是闭包?

这可能是所有问题中最难的一个问题,因为闭包是一个有争议的话题,这里从个人角度来谈谈,如果不妥,多多海涵。

闭包就是一个函数在声明时能够记住当前作用域、父函数作用域、及父函数作用域上的变量和参数的引用,直至通过作用域链上全局作用域,基本上闭包是在声明函数时创建的作用域。

看看小例子:

 
 
 
 
  1. //全局作用域varglobalVar="abc";functiona(){console.log(globalVar);}a();//"abc" 

在此示例中,当我们声明a函数时,全局作用域是a闭包的一部分。

变量globalVar在图中没有值的原因是该变量的值可以根据调用函数a的位置和时间而改变。但是在上面的示例中,globalVar变量的值为abc。

来看一个更复杂的例子:

 
 
 
 
  1. varglobalVar="global";varouterVar="outer"functionouterFunc(outerParam){functioninnerFunc(innerParam){console.log(globalVar,outerParam,innerParam);}returninnerFunc;}constx=outerFunc(outerVar);outerVar="outer-2";globalVar="guess"x("inner"); 

上面打印结果是 guess outer inner。

当我们调用outerFunc函数并将返回值innerFunc函数分配给变量x时,即使我们为outerVar变量分配了新值outer-2,outerParam也继续保留outer值,因为重新分配是在调用outerFunc之后发生的,并且当我们调用outerFunc函数时,它会在作用域链中查找outerVar的值,此时的outerVar的值将为 "outer"。

现在,当我们调用引用了innerFunc的x变量时,innerParam将具有一个inner值,因为这是我们在调用中传递的值,而globalVar变量值为guess,因为在调用x变量之前,我们将一个新值分配给globalVar。

下面这个示例演示没有理解好闭包所犯的错误:

 
 
 
 
  1. constarrFuncs=[];for(vari=0;i<5;i++){arrFuncs.push(function(){returni;});}console.log(i);//iis5for(leti=0;i

由于闭包,此代码无法正常运行。var关键字创建一个全局变量,当我们 push 一个函数时,这里返回的全局变量i。因此,当我们在循环后在该数组中调用其中一个函数时,它会打印5,因为我们得到i的当前值为5,我们可以访问它,因为它是全局变量。

因为闭包在创建变量时会保留该变量的引用而不是其值。我们可以使用IIFES或使用 let 来代替 var 的声明。

21. JavaScript 中的虚值是什么?

 
 
 
 
  1. constfalsyValues=['',0,null,undefined,NaN,false]; 

简单的来说虚值就是是在转换为布尔值时变为 false 的值。

22. 如何检查值是否虚值?

使用 Boolean 函数或者 !! 运算符。

23. 'use strict' 是干嘛用的?

"use strict" 是 ES5 特性,它使我们的代码在函数或整个脚本中处于严格模式。严格模式帮助我们在代码的早期避免 bug,并为其添加限制。

严格模式的一些限制:

设立”严格模式”的目的,主要有以下几个:

24. JavaScript 中 `this` 值是什么?

基本上,this指的是当前正在执行或调用该函数的对象的值。this值的变化取决于我们使用它的上下文和我们在哪里使用它。

 
 
 
 
  1. constcarDetails={name:"FordMustang",yearBought:2005,getName(){returnthis.name;},isRegistered:true};console.log(carDetails.getName());//FordMustang 

这通常是我们期望结果的,因为在getName方法中我们返回this.name,在此上下文中,this指向的是carDetails对象,该对象当前是执行函数的“所有者”对象。

接下我们做些奇怪的事情:

 
 
 
 
  1. varname="FordRanger";vargetCarName=carDetails.getName;console.log(getCarName());//FordRanger 

上面打印Ford Ranger,这很奇怪,因为在第一个console.log语句中打印的是Ford Mustang。这样做的原因是getCarName方法有一个不同的“所有者”对象,即window对象。在全局作用域中使用var关键字声明变量会在window对象中附加与变量名称相同的属性。请记住,当没有使用“use strict”时,在全局作用域中this指的是window对象。

 
 
 
 
  1. console.log(getCarName===window.getCarName);//trueconsole.log(getCarName===this.getCarName);//true 

本例中的this和window引用同一个对象。

解决这个问题的一种方法是在函数中使用apply和call方法。

 
 
 
 
  1. console.log(getCarName.apply(carDetails));//FordMustangconsole.log(getCarName.call(carDetails));//FordMustang 

apply和call方法期望第一个参数是一个对象,该对象是函数内部this的值。

IIFE或立即执行的函数表达式,在全局作用域内声明的函数,对象内部方法中的匿名函数和内部函数的this具有默认值,该值指向window对象。

 
 
 
 
  1. (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 值保存在变量中。

 
 
 
 
  1. constmyFavoriteObj={guessThis(){constself=this;//把this值保存在self变量中functiongetName(){console.log(self.name);}getName();},name:'MarkoPolo',thisIsAnnoying(callback){callback();}}; 

第二种方式是使用箭头函数

 
 
 
 
  1. constmyFavoriteObj={guessThis(){constgetName=()=>{console.log(this.name);}getName();},name:'MarkoPolo',thisIsAnnoying(callback){callback();}}; 

箭头函数没有自己的 this。它复制了这个封闭的词法作用域中this值,在这个例子中,this值在getName内部函数之外,也就是myFavoriteObj对象。

25. 对象的 prototype(原型) 是什么?

简单地说,原型就是对象的蓝图。如果它存在当前对象中,则将其用作属性和方法的回退。它是在对象之间共享属性和功能的方法,这也是JavaScript实现继承的核心。

 
 
 
 
  1. consto={};console.log(o.toString());//logs[objectObject] 

即使o对象中不存在o.toString方法,它也不会引发错误,而是返回字符串[object Object]。当对象中不存在属性时,它将查看其原型,如果仍然不存在,则将其查找到原型的原型,依此类推,直到在原型链中找到具有相同属性的属性为止。原型链的末尾是Object.prototype。

 
 
 
 
  1. console.log(o.toString===Object.prototype.toString);//logstrue 

当前标题:看完这几道JavaScript面试题,让你与考官对答如流(上)
标题来源:http://www.gawzjz.com/qtweb/news25/183325.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

猜你还喜欢下面的内容

定制开发知识

分类信息网