简答题1、什么是防抖和节流?有什么区别?如何实现?参考答案 防抖 触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间思路: 每次触发事件时都取消之前的延时调用方法functiondebounce(fn){创建一个标记用来存放定时器的返回值returnfunction(){clearTimeout(timeout);每当用户输入的时候把前一个setTimeoutclear掉timeoutsetTimeout((){然后又创建一个新的setTimeout,这样就能保证输入字符后的interval间隔内如果还有字符输入的话,就不会执行fn函数fn。apply(this,arguments);},500);};}functionsayHi(){console。log(防抖成功);}varinpdocument。getElementById(inp);inp。addEventListener(input,debounce(sayHi));防抖 节流 高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率思路: 每次触发事件时都判断当前是否有等待执行的延时函数functionthrottle(fn){letcanR通过闭包保存一个标记returnfunction(){if(!canRun)在函数开头判断标记是否为true,不为true则returncanR立即设置为falsesetTimeout((){将外部传入的函数的执行放在setTimeout中fn。apply(this,arguments);最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉canR},500);};}functionsayHi(e){console。log(e。target。innerWidth,e。target。innerHeight);}window。addEventListener(resize,throttle(sayHi));2、get请求传参长度的误区、get和post请求在缓存方面的区别 误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。参考答案 实际上HTTP协议从未规定GETPOST的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:HTTP协议未规定GET和POST的长度限制GET的最大长度显示是因为浏览器和web服务器限制了URI的长度不同的浏览器和WEB服务器,限制的最大长度不一样要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度8182byte 补充补充一个get和post在缓存方面的区别:get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。3、模块化发展历程 可从IIFE、AMD、CMD、CommonJS、UMD、webpack(require。ensure)、ESModule、li使用visibility替换display:none,因为前者只会引起重绘,后者会引发回流(改变了布局) 把DOM离线后修改,比如:先把DOM给display:none(有一次Reflow),然后你修改100次,然后再把它显示出来 不要把DOM结点的属性值放在一个循环里当成循环里的变量for(leti0;i1000;i){获取offsetTop会导致回流,因为需要去获取正确的值console。log(document。querySelector(。test)。style。offsetTop)}不要使用table布局,可能很小的一个小改动会造成整个table的重新布局动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用requestAnimationFrameCSS选择符从右往左匹配查找,避免DOM深度过深将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于video标签,浏览器会自动将该节点变为图层。ulreact、Vue1、写ReactVue项目时为什么要在列表组件中写key,其作用是什么?参考答案 vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个keyindex的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。vue部分源码如下:vue项目srccorevdompatch。js488行以下是为了阅读性进行格式化后的代码oldCh是一个旧虚拟节点数组if(isUndef(oldKeyToIdx)){oldKeyToIdxcreateKeyToOldIdx(oldCh,oldStartIdx,oldEndIdx)}if(isDef(newStartVnode。key)){map方式获取idxInOldoldKeyToIdx〔newStartVnode。key〕}else{遍历方式获取idxInOldfindIdxInOld(newStartVnode,oldCh,oldStartIdx,oldEndIdx)} 创建map函数functioncreateKeyToOldIdx(children,beginIdx,endIdx){leti,keyconstmap{}for(ibeginIiendIi){keychildren〔i〕。keyif(isDef(key))map〔key〕i}returnmap} 遍历寻找sameVnode是对比新旧节点是否相同的函数functionfindIdxInOld(node,oldCh,start,end){for(i){constcoldCh〔i〕if(isDef(c)sameVnode(node,c))returni}}2、React中setState什么时候是同步的,什么时候是异步的?参考答案 在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this。state,除此之外的setState调用会同步执行this。state。所谓除此之外,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeoutsetInterval产生的异步调用。 原因:在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this。state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this。state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this。state。3、下面输出什么classExampleextendsReact。Component{constructor(){super();this。state{val:0};}componentDidMount(){this。setState({val:this。state。val1});console。log(this。state。val);第1次logthis。setState({val:this。state。val1});console。log(this。state。val);第2次logsetTimeout((){this。setState({val:this。state。val1});console。log(this。state。val);第3次logthis。setState({val:this。state。val1});console。log(this。state。val);第4次log},0);}render(){}};1、第一次和第二次都是在react自身生命周期内,触发时isBatchingUpdates为true,所以并不会直接执行更新state,而是加入了dirtyComponents,所以打印时获取的都是更新前的状态0。2、两次setState时,获取到this。state。val都是0,所以执行时都是将0设置成1,在react内部会被合并掉,只执行一次。设置完成后state。val值为1。3、setTimeout中的代码,触发时isBatchingUpdates为false,所以能够直接进行更新,所以连着输出2,3。输出:00234、为什么虚拟dom会提高性能?参考答案 虚拟dom相当于在js和真实dom中间加了一个缓存,利用domdiff算法避免了没有必要的dom操作,从而提高性能。 具体实现步骤如下: 用JavaScript对象结构表示DOM树的结构;然后用这个树构建一个真正的DOM树,插到文档当中 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。css1、分析比较opacity:0、visibility:hidden、display:none优劣和适用场景参考答案 结构:display:none:会让元素完全从渲染树中消失,渲染的时候不占据任何空间,不能点击,visibility:hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击opacity:0:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击 继承:display:none:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。visibility:hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility:可以让子孙节点显式。 性能:displaynone:修改元素会造成文档回流,读屏器不会读取display:none元素内容,性能消耗较大visibility:hidden:修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility:hidden元素内容opacity:0:修改元素会造成重绘,性能消耗较少 联系:它们都能让元素不可见2、清除浮动的方式有哪些?比较好的是哪一种?参考答案 常用的一般为三种。clearfix,clear:both,overflow: 比较好是。clearfix,伪元素万金油版本,后两者有局限性。。clearfix:after{visibility:display:fontsize:0;content:;clear:height:0;}!为毛没有zoom,height这些,IE6,7这类需要csshack不再我们考虑之内了。clearfix还有另外一种写法,。clearfix:before,。clearfix:after{content:;display:}。clearfix:after{clear:overflow:}。clearfix{zoom:1;}!用display:table是为了避免外边距margin重叠导致的margin塌陷,内部元素默认会成为tablecell单元格的形式 clear:both:若是用在同一个容器内相邻元素上,那是贼好的,有时候在容器外就有些问题了,比如相邻容器的包裹层元素塌陷 overflow:hidden:这种若是用在同个容器内,可以形成BFC避免浮动造成的元素塌陷4、csssprite是什么,有什么优缺点参考答案 概念:将多个小图片拼接到一个图片中。通过backgroundposition和元素尺寸调节需要显示的背景图案。 优点:减少HTTP请求数,极大地提高页面加载速度增加图片信息重复度,提高压缩比,减少图片大小更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现 缺点:图片合并麻烦维护麻烦,修改一个图片可能需要重新布局整个图片,样式5、link与import的区别参考答案link是HTML方式,import是CSS方式link最大限度支持并行下载,import过多嵌套导致串行下载,出现FOUClink可以通过relalternatestylesheet指定候选样式浏览器对link支持早于import,可以使用import对老浏览器隐藏样式import必须在样式规则之前,可以在css文件中引用其他文件总体来说:link优于import6、display:和display:的区别参考答案 block元素特点: 1。处于常规流中时,如果width没有设置,会自动填充满父容器2。可以应用marginpadding3。在没有设置高度的情况下会扩展高度以包含常规流中的子元素4。处于常规流中时布局时在前后元素位置之间(独占一个水平空间)5。忽略verticalalign inline元素特点 1。水平方向上根据direction依次布局 2。不会在元素前后进行换行 3。受whitespace控制 4。marginpadding在竖直方向上无效,水平方向上有效 5。widthheight属性对非替换行内元素无效,宽度由元素内容决定 6。非替换行内元素的行框高由lineheight确定,替换行内元素的行框高由height,margin,padding,border决定7。浮动或绝对定位时会转换为block8。verticalalign属性生效7、容器包含若干浮动元素时如何清理浮动参考答案容器元素闭合标签前添加额外元素并设置clear:both父元素触发块级格式化上下文(见块级可视化上下文部分)设置容器元素伪元素进行清理推荐的清理浮动方法在标准浏览器下使用1content内容为空格用于修复opera下文档中出现contenteditable属性时在清理浮动元素上下的空白2使用display使用table而不是block:可以防止容器和子元素topmargin折叠,这样能使清理效果与BFC,IE67zoom:1;一致。clearfix:before,。clearfix:after{content:;1display:2}。clearfix:after{clear:}IE67下使用通过触发hasLayout实现包含浮动。clearfix{zoom:1;}8、PNG,GIF,JPG的区别及如何选参考答案 GIF:8位像素,256色无损压缩支持简单动画支持boolean透明适合简单动画 JPEG:颜色限于256有损压缩可控制压缩质量不支持透明适合照片 PNG:有PNG8和truecolorPNGPNG8类似GIF颜色上限为256,文件小,支持alpha透明度,无动画适合图标、背景、按钮9、display,float,position的关系参考答案如果display为none,那么position和float都不起作用,这种情况下元素不产生框否则,如果position值为absolute或者fixed,框就是绝对定位的,float的计算值为none,display根据下面的表格进行调整。否则,如果float不是none,框是浮动的,display根据下表进行调整否则,如果元素是根元素,display根据下表进行调整其他情况下display的值为指定值总结起来:绝对定位、浮动、根元素都需要调整display10、如何水平居中一个元素参考答案如果需要居中的元素为常规流中inline元素,为父元素设置textalign:即可实现如果需要居中的元素为常规流中block元素,1)为元素设置宽度,2)设置左右margin为auto。3)IE6下需在父元素上设置textalign:,再给子元素恢复需要的值 aaaaaaaaaaaaaaaaaaaa如果需要居中的元素为浮动元素,1)为元素设置宽度,2)position:,3)浮动方向偏移量(left或者right)设置为50,4)浮动方向上的margin设置为元素宽度一半乘以1 aaaaaaaaaaaaaaaaaaaa如果需要居中的元素为绝对定位元素,1)为元素设置宽度,2)偏移量设置为50,3)偏移方向外边距设置为元素宽度一半乘以1 aaaaaaaaaaaaaaaaaaaa如果需要居中的元素为绝对定位元素,1)为元素设置宽度,2)设置左右偏移量都为0,3)设置左右外边距都为auto aaaaaaaaaaaaaaaaaaaaJavaScript1、JS有几种数据类型,其中基本数据类型有哪些?参考答案 七种数据类型BooleanNullUndefinedNumberStringSymbol(ECMAScript6新定义)Object (ES6之前)其中5种为基本类型:string,number,boolean,null,undefined, ES6出来的Symbol也是原始数据类型,表示独一无二的值 Object为引用类型(范围挺大),也包括数组、函数,2、Promise构造函数是同步执行还是异步执行,那么then方法呢?参考答案constpromisenewPromise((resolve,reject){console。log(1)resolve()console。log(2)})promise。then((){console。log(3)})console。log(4) 输出结果是:1243promise构造函数是同步执行的,then方法是异步执行的Promisenew的时候会立即执行里面的代码then是微任务会在本次任务执行完的时候执行setTimeout是宏任务会在下次任务执行的时候执行3、JS的四种设计模式参考答案 工厂模式 简单的工厂模式可以理解为解决多个相似的问题;functionCreatePerson(name,age,sex){varobjnewObject();obj。obj。obj。obj。sayNamefunction(){returnthis。}}varp1newCreatePerson(longen,28,男);varp2newCreatePerson(tugenhua,27,女);console。log(p1。name);longenconsole。log(p1。age);28console。log(p1。sex);男console。log(p1。sayName());longenconsole。log(p2。name);tugenhuaconsole。log(p2。age);27console。log(p2。sex);女console。log(p2。sayName());tugenhua 单例模式 只能被实例化(构造函数给实例添加属性与方法)一次单体模式varSingletonfunction(name){this。};Singleton。prototype。getNamefunction(){returnthis。}获取实例对象vargetInstance(function(){returnfunction(name){if(!instance){相当于一个一次性阀门,只能实例化一次instancenewSingleton(name);}}})();测试单体模式的实例,所以abvaragetInstance(aa);varbgetInstance(bb); 沙箱模式 将一些函数放到自执行函数里面,但要用闭包暴露接口,用变量接收暴露的接口,再调用里面的值,否则无法使用里面的值letsandboxModel(function(){functionsayName(){};functionsayAge(){};return{sayName:sayName,sayAge:sayAge}})() 发布者订阅模式 就例如如我们关注了某一个公众号,然后他对应的有新的消息就会给你推送,发布者与订阅模式varshoeObj{};定义发布者shoeObj。list〔〕;缓存列表存放订阅者回调函数增加订阅者shoeObj。listenfunction(fn){shoeObj。list。push(fn);订阅消息添加到缓存列表}发布消息shoeObj。triggerfunction(){for(vari0,fnthis。list〔i〕;){fn。apply(this,arguments);第一个参数只是改变fn的this,}}小红订阅如下消息shoeObj。listen(function(color,size){console。log(颜色是:color);console。log(尺码是:size);});小花订阅如下消息shoeObj。listen(function(color,size){console。log(再次打印颜色是:color);console。log(再次打印尺码是:size);});shoeObj。trigger(红色,40);shoeObj。trigger(黑色,42); 代码实现逻辑是用数组存贮订阅者,发布者回调函数里面通知的方式是遍历订阅者数组,并将发布者内容传入订阅者数组4、列举出集中创建实例的方法参考答案 1。字面量letobj{name:张三} 2。Object构造函数创建letObjnewObject()Obj。name张三 3。使用工厂模式创建对象functioncreatePerson(name){varonewObject();o。};}varperson1createPerson(张三); 4。使用构造函数创建对象functionPerson(name){this。}varperson1newPerson(张三);5、简述一下前端事件流参考答案 HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下事件流的概念。 什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。事件捕获阶段处于目标阶段事件冒泡阶段 addEventListener:addEventListener是DOM2级事件新增的指定事件处理程序的操作,这个方法接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。 IE只支持事件冒泡。6、Function。proto(getPrototypeOf)是什么?参考答案 获取一个对象的原型,在chrome中可以通过proto的形式,或者在ES6中可以通过Object。getPrototypeOf的形式。 那么Function。proto是什么么?也就是说Function由什么对象继承而来,我们来做如下判别。Function。protoObject。prototypefalseFunction。protoFunction。prototypetrue 我们发现Function的原型也是Function。 我们用图可以来明确这个关系: 7、简述一下原型构造函数实例参考答案原型(prototype):一个简单的对象,用于实现对象的属性继承。可以简单的理解成对象的爹。在Firefox和Chrome中,每个JavaScript对象中都包含一个proto(非标准)的属性指向它爹(该对象的原型),可obj。proto进行访问。构造函数:可以通过new来新建一个对象的函数。实例:通过构造函数和new创建出来的对象,便是实例。实例通过proto指向原型,通过constructor指向构造函数。 这里来举个栗子,以Object为例,我们常用的Object便是一个构造函数,因此我们可以通过它构建实例。实例constinstancenewObject() 则此时,实例为instance,构造函数为Object,我们知道,构造函数拥有一个prototype的属性指向原型,因此原型为:原型constprototypeObject。prototype 这里我们可以来看出三者的关系:实例。proto原型原型。constructor构造函数构造函数。prototype原型这条线其实是是基于原型进行获取的,可以理解成一条基于原型的映射线例如:constonewObject()o。constructorObjecttrueo。o。constructorObjectfalse实例。constructor构造函数8、简述一下JS继承,并举例参考答案 在JS中,继承通常指的便是原型链继承,也就是通过指定原型,并可以通过原型链继承原型上的属性或者方法。最优化:圣杯模式 varinherit(function(c,p){varFfunction(){};returnfunction(c,p){F。prototypep。c。prototypenewF();c。uberp。c。prototype。}})();使用ES6的语法糖classextends9、函数柯里化参考答案 在函数式编程中,函数是一等公民。那么函数柯里化是怎样的呢? 函数柯里化指的是将能够接收多个参数的函数转化为接收单一参数的函数,并且返回接收余下参数且返回结果的新函数的技术。 函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。 在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数预置通用参数,供多次重复调用。constaddfunctionadd(x){returnfunction(y){returnxy}}constadd1add(1)add1(2)3add1(20)2110、说说bind、call、apply区别?参考答案 call和apply都是为了解决改变this的指向。作用都是相同的,只是传参的方式不同。 除了第一个参数外,call可以接收一个参数列表,apply只接受一个参数数组。leta{value:1}functiongetValue(name,age){console。log(name)console。log(age)console。log(this。value)}getValue。call(a,yck,24)getValue。apply(a,〔yck,24〕) bind和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过bind实现柯里化。(下面是对这三个方法的扩展介绍) 如何实现一个bind函数 对于实现以下几个函数,可以从几个方面思考不传入第一个参数,那么默认为window改变了this指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?Function。prototype。myBindfunction(context){if(typeofthis!function){thrownewTypeError(Error)}varthisthisvarargs〔。。。arguments〕。slice(1)返回一个函数returnfunctionF(){因为返回了一个函数,我们可以newF(),所以需要判断if(thisinstanceofF){returnnewthis(。。。args,。。。arguments)}returnthis。apply(context,args。concat(。。。arguments))}} 如何实现一个call函数Function。prototype。myCallfunction(context){varcontextcontextwindow给context添加一个属性getValue。call(a,yck,24)a。fngetValuecontext。fnthis将context后面的参数取出来varargs〔。。。arguments〕。slice(1)getValue。call(a,yck,24)a。fn(yck,24)varresultcontext。fn(。。。args)删除fndeletecontext。fnreturnresult} 如何实现一个apply函数Function。prototype。myApplyfunction(context){varcontextcontextwindowcontext。fnthisvarresult需要判断是否存储第二个参数如果存在,就将第二个参数展开if(arguments〔1〕){resultcontext。fn(。。。arguments〔1〕)}else{resultcontext。fn()}deletecontext。fnreturnresult}11、箭头函数的特点参考答案functiona(){return(){return(){console。log(this)}}}console。log(a()()()) 箭头函数其实是没有this的,这个函数中的this只取决于他外面的第一个不是箭头函数的函数的this。在这个例子中,因为调用a符合前面代码中的第一个情况,所以this是window。并且this一旦绑定了上下文,就不会被任何代码改变。程序阅读题1、下面程序输出的结果是什么?functionsayHi(){console。log(name);console。log(age);varnameLletage21;}sayHi();A:Lydia和undefinedB:Lydia和ReferenceErrorC:ReferenceError和21D:undefined和ReferenceError参考答案 在函数中,我们首先使用var关键字声明了name变量。这意味着变量在创建阶段会被提升(JavaScript会在创建变量创建阶段为其分配内存空间),默认值为undefined,直到我们实际执行到使用该变量的行。我们还没有为name变量赋值,所以它仍然保持undefined的值。 使用let关键字(和const)声明的变量也会存在变量提升,但与var不同,初始化没有被提升。在我们声明(初始化)它们之前,它们是不可访问的。这被称为暂时死区。当我们在声明变量之前尝试访问变量时,JavaScript会抛出一个ReferenceError。 关于let的是否存在变量提升,我们何以用下面的例子来验证:letnameConardLi{console。log(name)UncaughtReferenceError:nameisnotdefinedletnamecode秘密花园} let变量如果不存在变量提升,console。log(name)就会输出ConardLi,结果却抛出了ReferenceError,那么这很好的说明了,let也存在变量提升,但是它存在一个暂时死区,在变量未初始化或赋值前不允许访问。 变量的赋值可以分为三个阶段:创建变量,在内存中开辟空间初始化变量,将变量初始化为undefined真正赋值 关于let、var和function:let的创建过程被提升了,但是初始化没有提升。var的创建和初始化都被提升了。function的创建初始化和赋值都被提升了。2、下面代码输出什么vara10;(function(){console。log(a)a5console。log(window。a)vara20;console。log(a)})() 依次输出:undefined1020在立即执行函数中,vara20;语句定义了一个局部变量a,由于js的变量声明提升机制,局部变量a的声明会被提升至立即执行函数的函数体最上方,且由于这样的提升并不包括赋值,因此第一条打印语句会打印undefined,最后一条语句会打印20。由于变量声明提升,a5;这条语句执行时,局部的变量a已经声明,因此它产生的效果是对局部的变量a赋值,此时window。a依旧是最开始赋值的10,3、下面的输出结果是什么?classChameleon{staticcolorChange(newColor){this。newColornewC}constructor({newColorgreen}{}){this。newColornewC}}constfreddienewChameleon({newColor:purple});freddie。colorChange(orange);A:orangeB:purpleC:greenD:TypeError 答案:D colorChange方法是静态的。静态方法仅在创建它们的构造函数中存在,并且不能传递给任何子级。由于freddie是一个子级对象,函数不会传递,所以在freddie实例上不存在freddie方法:抛出TypeError。4、下面代码中什么时候会输出1?vara?;if(a1a2a3){conso。log(1);}参考答案 因为会进行隐式类型转换所以我们重写toString方法就可以了vara{i:1,toString(){returna。i;}}if(a1a2a3){console。log(1);}5、下面的输出结果是什么?varobj{2:3,3:4,length:2,splice:Array。prototype。splice,push:Array。prototype。push}obj。push(1)obj。push(2)console。log(obj)参考答案 1。使用第一次push,obj对象的push方法设置obj〔2〕1;obj。length12。使用第二次push,obj对象的push方法设置obj〔3〕2;obj。length13。使用console。log输出的时候,因为obj具有length属性和splice方法,故将其作为数组进行打印4。打印时因为数组未设置下标为01处的值,故打印为empty,主动obj〔0〕获取为undefined 6、下面代码输出的结果是什么?vara{n:1};a。xa{n:2};console。log(a。x)console。log(b。x)参考答案 undefined{n:2} 首先,a和b同时引用了{n:2}对象,接着执行到a。xa{n:2}语句,尽管赋值是从右到左的没错,但是。的优先级比要高,所以这里首先执行a。x,相当于为a(或者b)所指向的{n:1}对象新增了一个属性x,即此时对象将变为{n:1;x:undefined}。之后按正常情况,从右到左进行赋值,此时执行a{n:2}的时候,a的引用改变,指向了新对象{n:2},而b依然指向的是旧对象。之后执行a。x{n:2}的时候,并不会重新解析一遍a,而是沿用最初解析a。x时候的a,也即旧对象,故此时旧对象的x的值为{n:2},旧对象为{n:1;x:{n:2}},它被b引用着。后面输出a。x的时候,又要解析a了,此时的a是指向新对象的a,而这个新对象是没有x属性的,故访问时输出而访问b。x的时候,将输出旧对象的x的值,即{n:2}。7、下面代码的输出是什么?functioncheckAge(data){if(data{age:18}){console。log(Youareanadult!);}elseif(data{age:18}){console。log(Youarestillanadult。);}else{console。log(Hmm。。YoudonthaveanageIguess);}}checkAge({age:18});参考答案 Hmm。。YoudonthaveanageIguess 在比较相等性,原始类型通过它们的值进行比较,而对象通过它们的引用进行比较。JavaScript检查对象是否具有对内存中相同位置的引用。 我们作为参数传递的对象和我们用于检查相等性的对象在内存中位于不同位置,所以它们的引用是不同的。 这就是为什么{age:18}{age:18}和{age:18}{age:18}返回false的原因。8、下面代码的输出是什么?constobj{1:a,2:b,3:c};constsetnewSet(〔1,2,3,4,5〕);obj。hasOwnProperty(1);obj。hasOwnProperty(1);set。has(1);set。has(1);参考答案 truetruefalsetrue 所有对象键(不包括Symbols)都会被存储为字符串,即使你没有给定字符串类型的键。这就是为什么obj。hasOwnProperty(1)也返回true。 上面的说法不适用于Set。在我们的Set中没有1:set。has(1)返回false。它有数字类型1,set。has(1)返回true。9、下面代码的输出是什么?example1vara{},b123,c123;a〔b〕b;a〔c〕c;console。log(a〔b〕);example2vara{},bSymbol(123),cSymbol(123);a〔b〕b;a〔c〕c;console。log(a〔b〕);example3vara{},b{key:123},c{key:456};a〔b〕b;a〔c〕c;console。log(a〔b〕);参考答案 这题考察的是对象的键名的转换。对象的键名只能是字符串和Symbol类型。其他类型的键名会被转换成字符串类型。对象转字符串默认会调用toString方法。example1vara{},b123,c123;a〔b〕b;c的键名会被转换成字符串123,这里会把b覆盖掉。a〔c〕c;输出cconsole。log(a〔b〕);example2vara{},bSymbol(123),cSymbol(123);b是Symbol类型,不需要转换。a〔b〕b;c是Symbol类型,不需要转换。任何一个Symbol类型的值都是不相等的,所以不会覆盖掉b。a〔c〕c;输出bconsole。log(a〔b〕);example3vara{},b{key:123},c{key:456};b不是字符串也不是Symbol类型,需要转换成字符串。对象类型会调用toString方法转换成字符串〔objectObject〕。a〔b〕b;c不是字符串也不是Symbol类型,需要转换成字符串。对象类型会调用toString方法转换成字符串〔objectObject〕。这里会把b覆盖掉。a〔c〕c;输出cconsole。log(a〔b〕);10、下面代码的输出是什么?((){letx,y;try{thrownewError();}catch(x){(x1),(y2);console。log(x);}console。log(x);console。log(y);})();参考答案 1undefined2 catch块接收参数x。当我们传递参数时,这与变量的x不同。这个变量x是属于catch作用域的。 之后,我们将这个块级作用域的变量设置为1,并设置变量y的值。现在,我们打印块级作用域的变量x,它等于1。 在catch块之外,x仍然是undefined,而y是2。当我们想在catch块之外的console。log(x)时,它返回undefined,而y返回2。11、下面代码的输出结果是什么?functionFoo(){Foo。afunction(){console。log(1)}this。afunction(){console。log(2)}}Foo。prototype。afunction(){console。log(3)}Foo。afunction(){console。log(4)}Foo。a();letobjnewFoo();obj。a();Foo。a();参考答案 输出顺序是421functionFoo(){Foo。afunction(){console。log(1)}this。afunction(){console。log(2)}}以上只是Foo的构建方法,没有产生实例,此刻也没有执行Foo。prototype。afunction(){console。log(3)}现在在Foo上挂载了原型方法a,方法输出值为3Foo。afunction(){console。log(4)}现在在Foo上挂载了直接方法a,输出值为4Foo。a();立刻执行了Foo上的a方法,也就是刚刚定义的,所以输出4letobjnewFoo();这里调用了Foo的构建方法。Foo的构建方法主要做了两件事:1。将全局的Foo上的直接方法a替换为一个输出1的方法。2。在新对象上挂载直接方法a,输出值为2。obj。a();因为有直接方法a,不需要去访问原型链,所以使用的是构建方法里所定义的this。a,输出2Foo。a();构建方法里已经替换了全局Foo上的a方法,所以输出1