1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > js高频面试题

js高频面试题

时间:2024-02-27 12:52:21

相关推荐

js高频面试题

点击查看HTML+CSS高频面试题

点击查看Vue高频面试题

js高频面试题

初级js面试题给dom对象绑定事件都有那些方法解释一下什么是事件流删除事件有哪些方法事件委托与事件冒泡讲一下常见的事件对象经常用到的鼠标,键盘事件Bom知识点概要Window常见的事件对象鼠标位置三大系列:offset,page与screen系列这三元素的位置和大小-三大系列:offset,cilent与scroll系列e.target与this的区别定时器实现动画的最佳时间:16.6msmouseover与mouseenter的区别获取元素对象的注意点js数据类型null与undefined的区别axios与ajax的区别及优缺点ajax、axios与fetch的区别及优缺点DOM节点操作window.onload 和 $(document).ready()的区别截取字符串用什么方法文本框中文字改变的时候 触发的是什么事件下拉菜单中,用户更改表单元素select的值时,就会触发什么事件?javascript中的全局对象有哪些如何提高网站的速度一道判断输入结果的题目如何获取时间戳拖拽会用到哪些事件document.write和innerHTML的区别:如何将伪数组变为真正的数组请使用原生js模拟栈的进出如何检测某一个值是否为nulljs内置构造函数new与不new的区别`==`与`===`的区别什么情况下会返回undefined如何获取时间戳列举三种强制类型转换和两种隐式类型转换浏览器的渲染过程,DOM树和渲染树的区别forEach和map的区别如何终止foreach循环几道API考查的题什么是任务队列栈与队列的区别为什么将 CSS 放在文件头部,JavaScript 文件放在底部暂停死区如何制作一个循环的动画一个单机小游戏卡顿原因分析什么是按需加载文档碎片写一个函数,第一秒打印 1,第二秒打印 2如何获取对象上的所有的属性什么是 window 对象? 什么是 document 对象渐进增强与优雅降级高级js面试题深拷贝与浅拷贝闭包JavaScript的执行机制继承什么是原形、型原型链作用域的理解介绍一下执行上下文作用域与执行上下文的区别创建对象的5种的方式使用Function() 构造函数创建函数for in与for of的区别Object构造函数常用的属性与方法instance of不同进制的表示方式NaN是什么 NaN == NaN 的结果是什么?为什么?typeof NaN 的结果是什么?一道关于for循环的题模块化开发Js中常见的内存泄漏String常用APIJSON的增加与删除什么是面向对象请简述普通函数和构造函数的区别Promise的理解请简述async的用法为什么js是弱类型语言将less转为css的方式箭头函数与普通函数的区别伪数组介绍一下你对浏览器内核的理解?常见浏览器的内核是什么?如何让事件先冒泡后捕获JS中的垃圾回收机制this的指向哪几种如何检测是不是数组什么是立即执行函数,怎么用,有什么好处如何检测空对象js比较运算符如何实现数组去重

初级js面试题

给dom对象绑定事件都有那些方法

方式一:普通方式(这里的事件前面需要加上“on”)在标签上直接写具体的事件,记得把标签名也念上。

方式二:同一个元素的同一个事件只可以使用一个处理函数,

方式三:Ie9以上可以使用addEventListener监听方式,态绑定方式(需要先获取dom元素再绑定事件,动态事件绑定存在内存泄露,所以务必要注意回收,同一个元素的同一个事件可以注册多个监听器,移除时传入的参数与添加处理程序时使用的参数相同。通过addEventListener()添加的匿名函数无法移除)

方式四:Ie9以前可以使用attachevent

Btn.attachevent(‘click’,function(){

console.log(‘我被执行了’)

})

解释一下什么是事件流

事件流描述的是从页面中接收事件的顺序。事件发生时会在元素节点与根节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即DOM事件流

实际上DOM事件流有三个阶段:

事件捕获阶段:捕获阶段是从外到内传播即从根节点向最内侧节点传播,

事件目标阶段:目标阶段就是事件到达我们具体点击的那个元素时的阶段,

事件冒泡阶段:从内向外传播直到根节点结束

删除事件有哪些方法

注意btn称为元素对象

方式一:Btn.οnclick=null

方式二:btn.removeEventLister(‘事件类型’,函数)

方式三:btn.detachEvent(‘事件类型’,函数)

事件委托与事件冒泡讲一下

事件冒泡:子级元素的某个事件被触发,它的上级元素的该事件也被递归触发。

事件委托:使用了事件冒泡的原理,从触发某事件的元素开始,递归地向上级元素传播事件。

常见的事件对象

经常用到的鼠标,键盘事件

鼠标事件三件套:按下(click,mousedown),移动(mousemove),抬起(mouseup),常见的事件对象有以下几个:

e.clientX e.clientY 返回鼠标相对于浏览器可视窗口区域的X与Y坐标

e.pageX e.pageY 返回鼠标相对于整个文档页面的X与Y坐标

e.screenX e.screenY 返回鼠标相对于电脑屏幕的X与Y坐标

键盘事件:按下(keydown),抬起(keyup),常见的事件对象有以下几个:

e.keyCode返回该键的ASCII值

Bom知识点概要Window常见的事件对象

Onload等到所有的所有资源全部加载完毕,再去执行

DOMContentLoaded,仅仅当dom加载完毕就可以执行,不用等待样式表,图片,flash等到。

因此,如果script src写到了head标签里面,就必须要加上这个方法,不然无法获取dom。要么就写到最下面。

Location对象

Window对象给我们提供了一个location属性用于获取或设置窗体的url,因为这个属性返回的是一个对象,因此我们也将这个属性成为location对象。

location对象的常用属性有:

location.href获取或设置整个url

location.host设置或返回主机名和当前 URL 的端口号。

location.port返回端口号

location.pathname设置或返回当前 URL 的路径部分。

location.search设置或返回从问号 (?) 开始的 URL(查询部分)

location.hash返回#后面的,常见于锚点连接,如果地址里没有“#”,则返回空字符串。

navigator对象

这里面包含了有关浏览器的信息,最常用的是userAgent属性,返回当前浏览器的用户代理字符串。

history对象

back后退

foward前进

go(参数)正数代表前进,负数代表后退

鼠标位置三大系列:offset,page与screen系列这三

这三大系列都是通过鼠标事件(click,mousedown,mousemove,mouseup等等)的事件对象中直接获取到的

client获取到的鼠标距离当前浏览器可视区域的距离(这个用的次数非常多)page获取到的鼠标距离当前浏览器整个文档区域(包含了文档卷去的高度)的距离(这个用的很少)screen获取到的鼠标距离当前屏幕的距离(这个几乎用不到)

元素的位置和大小-三大系列:offset,cilent与scroll系列

注意一下几个系列都是在box-sizing为content-box的情况下讲解的

这三大系列都是通过元素对象直接获取到的,或者通过事件对象中的target属性获取到的。

①offset系列:

如果没有带有定位的父元素,就返回距离浏览器的宽度

②cilent系列:

③scroll系列:

e.target与this的区别

e.target表示触发事件的对象;

this表示的是注册事件,也就是当时绑定事件的对象

this 等于 e.currentTarget

就比如说一个ul里面有很多li,只给ul注册一个点击事件,当我们点击li后,时间会冒跑到ul上,此时在ul上分别打印e.target与this就会看出差别。

定时器实现动画的最佳时间:16.6ms

大多数电脑显示器的刷新频率是60HZ,大概相当于每秒钟重绘60次。因此,最平滑的动画效的最佳循环间隔是1000ms/60,约等于16.6ms。

mouseover与mouseenter的区别

首先他们的共同点都是鼠标经过元素都会触发鼠标经过事件

不论鼠标经过自身元素还是其子元素,都会触发 mouseover 事件。对应mouseout

只有在鼠标经过自身元素时,才会触发 mouseenter 事件。对应mouseleave

之所以会这样,是因为mouseenter不会冒泡。

获取元素对象的注意点

通过queryselator,getelementById获取的都是单个的元素对象

通过className,tagName,queryselatorAll都是获取的伪数组。

js数据类型

基本数据类型(6种):Number、String、Boolean、Null、 Undefined、Symbol(ES6),它们被存放在栈中,存的是具体的值。因为原始数据类型占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

引用数据类型:Object(在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象),引用类型是同时保存在栈区中和堆区中的,引用类型的存储需要在内存的栈区和堆区中共同完成,栈区保存变量标识符和指向堆内存的地址,他们的值存放在堆(heap)内存的。因为占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

null与undefined的区别

首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。

null是一个表示"无"的对象,null表示一个值被定义了,但是这个值是空值。理解null最好的方式是把它当作对象的占位符。

undefined表示不存在该值的定义。变量被声明了还没有赋值,表现为undefined,表示这个变量等待被赋值.

当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。

当我们使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。

axios与ajax的区别及优缺点

1.区别

axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。

简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。

axios是ajax ajax不止axios。

下面列出代码来对比一下:

2.优缺点:

ajax:

本身是针对MVC的编程,不符合现在前端MVVM的浪潮

基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案

JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务

axios:

从 node.js 创建 http 请求

支持 Promise API

客户端支持防止CSRF

提供了一些并发请求的接口(重要,方便了很多的操作)

ajax、axios与fetch的区别及优缺点

Ajax与axios都是基于原生XHR封装过来的

ajax:axios实现了对原生XHR的封装,支持jsonp,使用起来非常方便,可以在success中获取返回的数据,不过也有缺点:1.本身是针对MVC框架的编程,不符合现在前端MVVM框架的浪潮。2.基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案。3.JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务

axios:1.从 node.js 创建 http 请求2.自动转换JSON数据3.支持 Promise API4.客户端支持防止CSRF5.提供了一些并发请求的接口(重要,方便了很多的操作)如:axios.all([axios.get(‘/qq’),axios.get(‘/aa’)])6.可以进行请求与响应拦截7.体积小,不支持jsonp8.客户端支持CSRF:Cross-site request forgery:攻击者盗用了你的身份,以你的名义发送恶意请求

fetch:fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。fetch是一个低层次的API,你可以把它考虑成原生的XHR,所以使用起来并不是那么舒服,需要进行封装。他有很多问题,比如:1)fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。2)fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: ‘include’})3)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费4)fetch没有办法原生监测请求的进度,而XHR可以

DOM节点操作

增:添加节点:

创建节点:document.createElement(‘tagName’)创建的是元素节点

如:let p = document.createElement(‘p’)

添加节点:node.appendChild(child)将一个节点添加到指定节点的子节点的末尾

如:document.querySelector(‘.b’).appendChild§

删:删除节点

node.removeChild(child):如:

document.querySelector(‘.b’).removeChild(document.querySelector(‘p’))

改:修改节点

通过innerText只能获取和设置文本内容,不能获取和设置html代码

innerHTML可以获取和设置html代码

value获取的是表单元素的值

查:获取节点:

在DOM中,标签,属性,文本,注释等都可以看做节点,都是可以通过js来获取的。元素节点为1,属性节点为2,文本节点为3

1.获取父元素节点:node.parentNode(不包括文本节点,这个用的多)如console.log(document.querySelector(‘.b’).

parentNode);

2.获取子节点:

node.children 一个即时的HTMLCollection(不包括文本节点,这个用的多)

3.获取兄弟节点:

node.previousElementSibling 返回前一个元素节点,这个用的多

node.nextElementSibling 返回下一个元素节点,这个用的多

window.onload 和 $(document).ready()的区别

1.在执行时间上:window.onload 必须等到页面内(包括图片的)所有元素加载到浏览器中后才能执行。(document).ready(function())是DOM结构加载完毕后就会执行。2.编写的个数不相同:window.onload,如果有多个最后一个会执行,它会把前面的都覆盖掉。(document).ready(function(){}) 是DOM结构加载完毕后就会执行。 2.编写的个数不相同:window.onload ,如果有多个最后一个会执行,它会把前面的都覆盖掉。(document).ready(function())是DOM结构加载完毕后就会执行。2.编写的个数不相同:window.onload,如果有多个最后一个会执行,它会把前面的都覆盖掉。(document).ready(function(){}) 可以写多个。

3.简写方法:window.onload 没有简写的方法。

$ (document).ready(function(){}) 可以简写为 $(function(){})。

截取字符串用什么方法

substring(3,6) //从索引3的位置开始截取 截取6个长度 也就是截取到索引是9的位置

substr(3,6) //从索引3的位置开始截取 截取到索引是6的位置

文本框中文字改变的时候 触发的是什么事件

oninput只要一输入内容就会触发

onchange只要一失去焦点就会触发

下拉菜单中,用户更改表单元素select的值时,就会触发什么事件?

onchange与onselect

javascript中的全局对象有哪些

new Date //日期

new Object //对象

new String //字符串

new Array //数组

new RegExp //正则表达式

如何提高网站的速度

将CSS的代码压缩成一行,并且减少注释,将多个js合并,并且压缩打包后再引入页面!能使用css3实现的动画,尽量不要去使用js

使用cdn加速,gzip压缩,使用精灵图,图片文件过大可以使用其他类型的图片格式,图片懒加载,少使用全局变量,数据缓存。

一道判断输入结果的题目

console.log(3 && 2);//2

console.log(3 || 2);//3

console.log(4399 < 0 || typeof (4399 + ‘’))//string

如何获取时间戳

1.Date.now()

2. new Date().getTime()相等,都可以得到1970 年 1 月 1 日至今的毫秒数。

拖拽会用到哪些事件

在H5之前,拖拽会用到onmousedown,onmousemove,onmouseup,实现的思路就是先获取元素距离页面顶部和左部的距离(目标元素要使用定位),然后在获取鼠标在页面的坐标,然后相减,获取鼠标在盒子内的坐标,然后再在拖拽的时候,用鼠标的最新的坐标值减去鼠标在盒子内的坐标,最后把他赋值给元素距离页面顶部和左部的距离就可以了。

在H5出现后,新增了拖拽的api,

document.write和innerHTML的区别:

document.write是直接写入到页面的内容流,在onload事件中,如果在写之前没有调用 document.open, 浏览器会自动调open。每次写完关闭之后重新调用该函数,他会导致页面被重写

innerHTML则是将内容改写入到指定的DOM节点,它代表该元素的html内容,他可以进行读取,也可以进行写入。

document.write的使用:

1.他可以接收一个js表达式

结果为:aa11

2.他可以接受一个字符串

document.write(‘

1’)

结果为:

3.他可以配合eval函数一块使用:

document.write(“

” + eval(“2+2”));

document.querySelectorAll(‘li’)返回的结果类型

如何将伪数组变为真正的数组

方法一:使用数组的API

Array.from() 方法可以通过给定的对象中创建一个新的数组

方法二:使用[].slice.call(list) 或Array.prototype.slice.call(list)

说明:list是为元素,不可枚举,本身也没有slice方法,但是通过call,将调用slice的对象指向list后,list便可以使用slice,这就是call的强大之处。同时arr已经是真正的数组,可枚举。

方法三:使用伪数组的forEach方法遍历,并逐一添加。

请使用原生js模拟栈的进出

结果:

如何检测某一个值是否为null

js内置构造函数new与不new的区别

=====的区别

==:两个等号我们称为等值符,当等号两边的值为相同类型时比较值是否相同,类型不同时会发生类型的自动转换,转换为相同的类型后再作比较。也就是说两个等号只要值相等,就可以。

===:三个等号既要判断值也要判断类型是否相等。

对于简单类型来说:

// 双等于对进行将值类型转换,全等于则不会

console.log(1 == ‘1’);//true

console.log(1 == true);//true

console.log(null == undefined);//true

console.log(1 === ‘1’);//false

console.log(1 === true);//false

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

对于复杂类型来说比较的是地址值:

比如函数

什么情况下会返回undefined

1.定义了一个变量,没有赋初始值或赋予他的值为undefined

2.在函数中打印没有传递过来的参数

3.在函数中return的值为空或者没有return值,打印了函数的调用

##如何区分数组和对象?

如何获取时间戳

1.通过instanceof

2.通过Array.isArray()

3.通过arr.constructor==Array

4.通过Object.prototype.toString.call();

列举三种强制类型转换和两种隐式类型转换

显式转换:

1.toString()转为字符串(null与undefined不行)

2.Number()转为数值(true会变成1false会变成0)

3.parseInt()转为整数、parsefloat()转为浮点数

隐式转换:

1.num+’’为字符串

2.str-0变为数值

浏览器的渲染过程,DOM树和渲染树的区别

渲染过程分为五部:

1.浏览器将获取的HTML文档解析成DOM树。

2.处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)。

3.将DOM和CSSOM合并为渲染树(rendering tree),代表一系列将被渲染的对象。

4.布局(Layout):计算出每个节的位置

5.将渲染树的各个节点绘制到屏幕上

区别:

DOM 树与 HTML 标签一一对应,包括 head 和隐藏元素

渲染树不包括 head 和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的 css 属性

forEach和map的区别

他们都可以接收两个参数,第一个参数是一个函数,第二个是一个对象(可以是简单数据类型也可以是复杂数据类型),一个函数中,接收三个参数,分别是每一项,索引,当前的数组对象。

1、forEach()返回值是undefined,不可以链式调用。

2、map()返回一个新数组,原数组不会改变。

3、正常情况下没有办法终止或者跳出forEach()循环,除非抛出异常,所以想执行一个数组是否满足什么条件,返回布尔值,可以用一般的for循环实现,或者用Array.every()或者Array.some();

4、forEach() 对于空数组是不会执行回调函数的。

如何终止foreach循环

1.使用try catch内部throw new Error()

2.可以使用for循环来代替forEach

3.可以使用Array.every()或者Array.some()去代替他们;

几道API考查的题

1.冒泡排序

首先需要找规律,找到规律之后在写代码

第一个for是控制趟数的,第二个for是进行比大小的

比如说数组[3,2,1]没在比较的时候,一共要比较两趟,多以第一个for就出来了,然后第一趟比较两次,第二趟比较一次,然后第二个for就出来了,最后进行比大小就可以了。

for (i = 0; i < arr.length - 1; i++) {for (j = 0; j < arr.length - i - 1; j++) {if (arr[j] > arr[j + 1]) {let a = arr[j + 1]arr[j + 1] = arr[j]arr[j] = a}}

}

2.字符串变为驼峰式写法

什么是任务队列

任务队列(task queue)主要分两种:

1、宏任务(macrotask):在新标准中叫 task

1.1主要包括:script(整体代码),setTimeout,setInterval,setImmediate,I/O,ui rendering

2、微任务(microtask):在新标准中叫 jobs

2.1主要包括:process.nextTick,Promise,MutationObserver(html5 新特性)

3、扩展:

3.1同步任务:在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执

3.2异步任务:不进入主线程,而进入“任务队列”的任务,只有“任务队列”

通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

栈与队列的区别

栈(Stack)是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

队列先进先出,栈先进后出

为什么将 CSS 放在文件头部,JavaScript 文件放在底部

CSS 执行会阻塞渲染,阻止 JS 执行

JS 加载和执行会阻塞 HTML 解析,阻止 CSSOM 构建

如果这些 CSS、JS 标签放在 HEAD 标签里,并且需要加载和解析很久的话,那么页面就空白了。所以 JS 文件要放在底部(不阻止 DOM 解析,但会阻塞渲染),等 HTML 解析完了再加载 JS 文件,尽早向用户呈现页面的内容。

那为什么 CSS 文件还要放在头部呢?

因为先加载 HTML 再加载 CSS,会让用户第一时间看到的页面是没有样式的、“丑陋”的,为了避免这种情况发生,就要将 CSS 文件放在头部了。

另外,JS 文件也不是不可以放在头部,只要给 script 标签加上 defer 属性就可以了,异步下载,延迟执行。

暂停死区

在代码块内,使用 let、const 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

如何制作一个循环的动画

一个单机小游戏卡顿原因分析

有一个游戏叫做 Flappy Bird,就是一只小鸟在飞,前面是无尽的沙漠,上下不断有钢管生成,你要躲避钢管。然后小明在玩这个游戏时候老是卡顿甚至崩溃,说出原因(3-5个)以及解决办法(3-5 个)

原因可能是:

1.内存溢出问题。

2.资源过大问题。

3.资源加载问题。

4.canvas 绘制频率问题

解决办法:

1.针对内存溢出问题,我们应该在钢管离开可视区域后,销毁钢管,让垃圾收集

器回收钢管,因为不断生成的钢管不及时清理容易导致内存溢出游戏崩溃。

2.针对资源过大问题,我们应该选择图片文件大小更小的图片格式,比如使用webp、png 格式的图片,因为绘制图片需要较大计算量。

3.针对资源加载问题,我们应该在可视区域之前就预加载好资源,如果在可视区域生成钢管的话,用户的体验就认为钢管是卡顿后才生成的,不流畅。

4.针对 canvas 绘制频率问题,我们应该需要知道大部分显示器刷新频率为 60次/s,因此游戏的每一帧绘制间隔时间需要小于 1000/60=16.7ms,才能让用户觉得不卡顿。(注意因为这是单机游戏,所以回答与网络无关)

什么是按需加载

当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件,可以是 JS、图片、CSS、HTML 等。

文档碎片

由于每次对dom的操作都会触发"重排",这严重影响到能耗,一般通常采取的做法是尽可能的减少dom操作来减少"重排",才去的措施就是使用文档碎片,它是一个容器,用于暂时存放创建的dom元素,当操作完成之后再把它插入到文档中。

使用方式如下

写一个函数,第一秒打印 1,第二秒打印 2

如何获取对象上的所有的属性

1.for(let I in obj)该方法依次访问一个对象及其原型链中所有可枚举的类型

2.Object.keys:返回一个数组,包括所有可枚举的属性名称

3.Object.getOwnPropertyNames:返回一个数组包含不可枚举的属性

4.使用Reflect.ownKeys(obj)

什么是 window 对象? 什么是 document 对象

window 对象代表浏览器中打开的一个窗口。document 对象代表整个 html 文档。实际上,document 对象是 window 对象的一个属性。

渐进增强与优雅降级

渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进,达到更好的用户体验。

优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

高级js面试题

深拷贝与浅拷贝

浅拷贝:

Object.assign(target, source),无法对复杂数据类型进行深拷贝。

深拷贝:

JSON.stringify()以及JSON.parse():它是不可以拷贝 undefined , function等类型的

他可以实现深拷贝:

function deepClone(obj) {

var newObj = obj instanceof Array ? [] : {};

//obj属于基本数据类型,直接返回obj

if (typeof obj !== ‘object’) {

return obj;

} else {

//obj属于数组或对象,遍历它们

for (var i in obj) {

newObj[i] = typeof obj[i] === ‘object’ ? deepClone(obj[i]) : obj[i];

}

}

return newObj;

}

我们还可以使用一些插件,来完成拷贝,如jquery的$.extend,lodash等

闭包

先说定义,再具体解释定义,然后说她的好处与坏处,还要会常见的的面试题

定义:闭包是有权限访问其他函数作用域内的变量的一个函数。

简单说:其实闭包就是一个函数,在一个函数内部定义了一些变量和一个函数,这里的内部函数就是一个闭包函数,此时在内部函数中是可以访问到外部函数的变量的。

在函数执行后作用域就会被清理、内存也随之回收。但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数——也就是闭包,便拥有了访问上级作用域中的变量的权限,即使上级函数执行完后作用域内的值也不会被销毁。

闭包是指可以访问另一个函数中的变量的函数,也就是函数嵌套关系。

每次外部函数执行的时候,外部函数的引用地址都不会相同,都会重新创建一个新的地址

闭包的特点

1、让外部访问函数内部变量成为可能

2、局部变量会常驻在内存中

3、可以避免使用全局变量,防止全局变量污染

4、会造成内存泄漏(局部变量一直保存到内存中,这一块内存空间被长期占用,而不被释放)

JavaScript的执行机制

先说同步与异步的由来,然后同步于异步的区别,解释他们是什么,然后再解释他们的执行顺序,再说微任务与宏任务都有什么

同步和异步的由来——JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。单线程导致的问题就是后面的任务等待前面任务完成,如果前面任务很耗时(比如读取网络数据),后面任务不得不一直等待!!为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。于是,JS 中出现了同步任务和异步任务。

同步任务指的是: 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务; 异步任务指的是: 不进入主线程、而进入”任务队列”的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。

同步,不是多件事情同时去做,而是先做一件事情,然后再做另一件事情。他是单线程,一个人依次做多件事情

异步,是多事情同时去做,他是多线程,多个人同时做多个事情

宏任务、微任务有哪些

宏任务包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

微任务包括: Promise.then catch finally, process.nextTick, MutationObserver

宏任务、微任务的执行顺序:

执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。

经典的面试题如下:

遇到了await就直接去执行,但是后面的代码就会跳过(也就是第六第七个输出),这两行代码会变成微任务,等待执行。

继承

方式一:通过原型链实现继承

通过 new 关键字,将构造函数的实例对象作为子类函数的原型对象,子构造函数的prototype上就有了父亲的属性和方法。

Son.prototype = new Father()

缺点:

对于引用数据类型数据会被子类共享,也就是改一个其他都会改

方式二:通过构造函数call实现继承

子构造函数的对象本身上就有了父亲的属性和方法。

function Son() { Father.call(this) }

只能继承父类的实例属性和方法,不能继承父类的原型属性和方法,父类方法无法复用。每次实例化子类,都要执行父类函数。重新声明父类所定义的方法,无法复用

方式三:组合继承

将前面的两种方式结合

注意画横线的必须要写到前面,不然无效

缺点:调用了两次的父类函数,有性能问题,由于两次调用,会造成实例和原型上有相同的属性或方法

方式四:使用es6的class继承

通过extends与supper实现继承

什么是原形、型原型链

点我查看答案

作用域的理解

作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

常见的作用域主要分为几个类型:全局作用域、函数作用域、块状作用域、动态作用域(指的是call,aply等修改this指向)。

作用域链:当我们想要访问某一个变量或者函数时,它回去当前作用于中去查找,如果当前作用域中没有,它就会去它父级作用域中查找,在没有就再向上找,如果一直没有找到就报错,我们把这种通过作用域采取链式查找的方式称为作用域链。

介绍一下执行上下文

定义:执行上下文就是当前 JavaScript 代码被解析和执行时所在的环境, JavaScript 中运行任何的代码都是在执行上下文中运行。

执行上下文总共有三种类型:

1.全局执行上下文: 这是默认的、最基础的执行上下文。不在任何函数中的代码都位于全局执行上下文中。它做了两件事:1. 创建一个全局对象,在浏览器中这个全局对象就是 window 对象。2. 将 this 指针指向这个全局对象。一个程序中只能存在一个全局执行上下文。

2.函数执行上下文: 每次调用函数时,都会为该函数创建一个新的执行上下文。每个函数都拥有自己的执行上下文,但是只有在函数被调用的时候才会被创建。一个程序中可以存在任意数量的函数执行上下文。每当一个新的执行上下文被创建,它都会按照特定的顺序执行一系列步骤,具体过程将在本文后面讨论。

3.Eval 函数执行上下文: 运行在 eval 函数中的代码也获得了自己的执行上下文,但由于 Javascript 开发人员不常用 eval 函数,所以在这里不再讨论。

作用域与执行上下文的区别

作用域和执行上下文是两个不同的的概念

JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:解释阶段:词法分析,语法分析,作用域规则确定。执行阶段:创建执行上下文,执行函数代码,垃圾回收。

他们最大的区别是:执行上下文在运行阶段确定的,随时可能改变;作用域在解释阶段确定的,并且不会改变。

创建对象的5种的方式

<script>//内置函数var obj1=new Object();obj1.name="zhuyu";obj1.age=21;obj1.uni=function(){console.log(this.name+this.age);}console.log(obj1);obj1.uni();//字面量var obj2={name:"zhuyu2",age:21,uni2:function(){console.log(this.name+this.age)}}console.log(obj2);obj2.uni2();// 自定义函数function Obj(name,age){this.name=name;this.age=age;this.uni3=function(){console.log(this.name+this.age)}}var obj3=new Obj("zhuyu3",21);console.log(obj3);obj3.uni3();// Es6类class Obj2{constructor(name,age){this.name=name;this.age=age;}uni4(){console.log(this.name+this.age)}}var obj4=new Obj2("zhuyu4",21);console.log(obj4);obj4.uni4();//使用Object.create()var person={image:"true",uni5:function(){console.log(`名字是${this.name},年龄是${this.age}`);}}var obj5=Object.create(person);obj5.name="zhuyu5";obj5.age=21;obj5.image=false;obj5.uni5();console.log(obj5)</script>

补充:hasOwnProperty()方法用于检测一个对象是否含有特定的自身属性,返回一个布尔值

使用Function() 构造函数创建函数

var f = new Function(“x”, “y”, “return x*y”);

console.log(f(1, 2));

函数是Function的实例对象,也就是说函数也是对象

创建函数还有其他方式,如使用function声明,函数表达式,

使用function构造函数。

for in与for of的区别

for … in即可以遍历一个对象(包括数组对象字符串等)的可枚举属性(除Symbol以外),它会遍历对象的整个原型链可遍历的属性,性能非常差不推荐使用,而for … of只能遍历实现只遍历iterator接口的数据,它不会遍历原型链,实现了iterator接口的有set map array arguments string等等

for … in 获取的是对象的键名,for … of遍历获取的是对象的键值

// 例一:let obj = {a: 1,b: 2,}obj.__proto__.only = "hello"for (const item in obj) {console.log(item) //a,b,only}console.log(obj instanceof Object) //true// 例二:let arr = [1, 3, 2]for (const item in arr) {console.log(item) //0,1,2,only}for (const item of arr) {console.log(item) //1,3,2}

结果如下:

Object构造函数常用的属性与方法

1.Object.defineProperty(obj,prop,desc)在对象 obj 上定义新的属性,或者修改对象 obj 中的属性

2.Object.getOwnPropertyNames(obj)该方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 作为键名的属性)组成的数组。

3.Object.getOwnPropertySymbols(obj)返回一个给定对象自身的所有 Symbol 属性的数组。

4.Object.assign(target,source1,source2,…)该方法主要用于对象的合并,将源对象source的所有可枚举属性合并到目标对象target上,此方法只拷贝源对象的自身属性,不拷贝继承的属性,它是浅拷贝。

5.Object.keys(obj) 返回一个由一个给定对象的自身可枚举属性组成的数组(既无法获得不可枚举属性也无法获得 Symbol 作为键名的属性,他与getOwnPropertyNames最大的区别就是他无法遍历不可枚举的属性),数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)

6.Object.values(target)返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。可以传入数组,字符串,字符串等等。

7.obj.hasOwnProperty(prop)方法用于检测一个对象是否含有特定的自身属性,返回一个布尔值

8.Object.is()以与严格相等运算符相同的方式检查相等性的参数,但有两个区别。除了+0 不等于 -0 和 NaN 等于 NaN 之外,其他表现与 === 一致

// 双等于对进行将值类型转换,全等于则不会

console.log(1 == ‘1’);//true

console.log(1 == true);//true

console.log(null == undefined);//true

console.log(1 === ‘1’);//false

console.log(1 === true);//false

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

9.Object.creat(proto[, propertiesObject])

第一个参数是必须的,是新建对象的原型对象

第二个参数是可选的,值必须是一个对象,他的每一项都有value默认值为undefined、configurable表示是否可以被删除、enumerable是否可枚举 、writable是否可以被修改(这几项的值默认都是false),是新创建的实例对象上的属性。

instance of

可以判断一个实例是否属于它的父类型,其实他们比较的是对象的__proto__是否和构造函数的prototype相等。

不同进制的表示方式

1.以 0X、0x 开头的表示为十六进制。2.八进制数以数字0开头。3.以 0B、0b 开头的表示为二进制格式。

NaN是什么 NaN == NaN 的结果是什么?为什么?typeof NaN 的结果是什么?

定义:

1.not a number 不是一个数值

2.表示存储数据的数据类型,不是一个数值

3.如果有非数值进行运算,结果就是NaN

结果:

console.log( NaN == NaN ); //false

有NaN参与运算结果都是NaN

注意:如果是带"+"号的运算 , 有一侧是NaN、字符串,就执行字符串拼接操作,如:console.log(‘a’ + NaN);//aNaN

检测类型:

console.log(typeof NaN);//number

如何判断:

1.利用 NaN 是唯一一个不等于自身的特点

a !== a ? console.log(‘a为NaN’) : console.log(‘a不是NaN’);

2.利用 ES6 中提供的Object.is() 方法

console.log(Object.is(‘asd11’ / 11, NaN));//true这是一种特例,其实NaN是不等于NaN的

一道关于for循环的题

对于第一道:他会先去判断,判断成功之后,再去输出,再然后去i加一,当i为5的时候,还会继续执行,只要是满足条件(i<=5)就会去加一

模块化开发

优点:模块化开发可以使得复杂的系统变成若干个模块,每个模块之间相互独立,可以大大减少重复的代码,非常方便用于维护,减少了命名冲突的情况。

缺点:模块之间的通信会非常消耗性能。

对于ES6的模块化:

1.如果使用的是export导出的,必须要使用结构赋值,不然就会报错

2.如果使用的是export default导出的,当导出的是唯一一个属性的时候,我们接收的就是唯一一个属性;当导出的是对象的时候,我们接收的就是对象。

第一种导出方式:

export default { aa }//导出的是一个对象,可以使用结构赋值

第二种导出方式:

export default aa //导出的就是一个变量,不是对象无法使用结构赋值

3.使用export default中无法直接声明,如:

export default const a=11//这是错误的

Js中常见的内存泄漏

1.意外的全局变量

2.被遗忘的定时器或回调函数

3.脱离DOM的引用:如果把DOM 存成字典(JSON 键值对)或者数组,此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。那么将来需要把两个引用都清除。

4.闭包中重复创建的变量

String常用API

自己的博客上有

1.split:把一个字符串分割成字符串数组,这里卖弄必须要传参数,当传入的值为空的时候,如:str.split(‘’),他会把每一个字符都分割,当传入其它的字符的时候他会按照指定的字符进行分割,他不会改变原数组。

2.substr:从起始索引号提取字符串中指定数目的字符。可以接受两个参数,第一个为索引,第二个为个数。包括索引

3.substring:提取字符串中介于两个指定下标之间的字符。有头无尾。

还有其他的如concat,tostring,tolowercase,charat等等

JSON的增加与删除

let obj = {}obj.name = 'lisi' //给JSON添加数据的方式一obj['sex'] = 'man' //给JSON添加数据的方式二delete obj['sex'] //删除JSON的数据console.log(obj);

什么是面向对象请简述

面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想是将数据作为第一位,这是对数据一种优化,操作起来更加的方便,简化了过程。

Js本身是没有class类型的,但是每个函数都有一个prototype属性,prototype指向一个对象,当函数作为构造函数时,prototype就起到类似于class的作用。

面向对象有三个特点:封装(隐藏对象的属性和实现细节,对外提供公共访问方式),继承(提高代码复用性,继承是多态的前提),多态(是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象)

普通函数和构造函数的区别

1.构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数习惯上首字母大写

2.调用方式不一样,普通函数直接调用,构造函数要用关键字new来调用

3.调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创建新对象

4.构造函数内部的this指向实例,普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)

5.构造函数默认的返回值是创建的对象(也就是实例),普通函数的返回值由return语句决定

6.构造函数的函数名与类名相同

Promise的理解

Promise 是一种解决异步编程的方案,我们可以获取异步操作的消息自己身上有all:(该方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后并且执行结果都是成功的时候才执行回调,参数是一个数组,里面的每一项都要是Pomise实例对象)、race(谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调,参数与all相同)、reject、resolve这几个方法,原型上有then、catch等方法。

1.对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

2.一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

3.一旦新建了Promise就会立即执行,因此我们一般都是用一个函数去包住Promise,如:

我们并没有调用它,他就知执行了

请简述async的用法

ES7 标准引入了 async 函数,这是一个用同步的思维来解决异步问题的方案,当前端接口调用需要等到接口返回值以后渲染页面时。async的用法,它作为一个关键字放到函数前面,用来表示此函数是一个异步函数, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行,async 函数返回的是一个promise 对象,可以使用then方法添加回调函数。await的含义为等待。意思就是代码需要等待await后面的函数运行完并且有了返回结果之后,才继续执行下面的代码。这正是同步的效果。一个函数中如果有了await,那么必须要有async,不然就会报错。

1、async函数返回一个 Promise 对象

2、async函数内部return语句返回的值,会成为then方法回调函数的参数

3、async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态,抛出的错误对象会被catch方法回调函数接收到

或者:

正常情况下,await命令后面是一个Promise对象。如果不是,会被转成一个立即resolve的Promise对象。

为什么js是弱类型语言

弱类型语言实现相对于强类型语言来说的,在强类型语言中,变量类型有多种,比如int char float Boolean 不同类型相互转换有时需要强制转换,而jacascript只有一种类型var,为变量赋值时会自动判断类型并转换,所以是弱类型语言。

将less转为css的方式

1.使用vscode插件的Easy Less,保存自动生成css、

2.先全局安装less,然后使用命令lessc aaa.less main.less就可以了

3.使用webpack去进行配置,安装less,less-loader

箭头函数与普通函数的区别

该内容已单独列出来

伪数组

拥有length属性,其它属性(索引)为非负整数,不具有数组所具有的push,pop等方法,常见于arguments,Dom元素对象

介绍一下你对浏览器内核的理解?常见浏览器的内核是什么?

浏览器引擎主要分成两部分:渲染引擎和JS引擎

渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核

JS引擎则:解析和执行javascript来实现网页的动态效果

IE:trident内核(IE内核)/traidnt/(三叉戟)

Chrome:Blink内核(照眼睛)

FireFox:Gecko内核/gekou/(壁虎)

Safari:webkit内核/sefari/(猎手旅行)

如何让事件先冒泡后捕获

根据w3c标准,应先捕获再冒泡。若要实现先冒泡后捕获,给一个元素绑定两个addEventListener,其中一个第三个参数设置为false(即冒泡),另一个第三个参数设置为true(即捕获),调整它们的代码顺序,将设置为false的监听事件放在设置为true的监听事件前面即可。

JS中的垃圾回收机制

有时间了最好了解一下

this的指向哪几种

其实this到底指向谁,我们只需要看哪个对象调用的函数可以了,具体的立体会在另一个文件中体现出来。

可以从以下几个方面回答:1.在普通函数中2.在对象中的函数3.在构造函数中4.使用call aply bind修改的this指向

如何检测是不是数组

1.console.log(Array.isArray(arr));

2.console.log(arr instanceof Array);

3.console.log(arr.constructor == Array);

什么是立即执行函数,怎么用,有什么好处

(function(){

//function body

}())

(function (){

//function body

})()

如何检测空对象

先定义一下被监测的对象

总共有五种方法

js比较运算符

双等于比较的时候会自动进行类型转换,比说说数值1与字符串1相等,null与undefined相等;全等=对于简单数据类型不仅会比较值,还不比较数据类型,对于复杂数据类型,比较的是地址值。

如何实现数组去重

可以使用第三方库如:lodash或者uniquecore

let arr = [2, 5, 2, 6]

第一种

console.log(_.uniq(arr)); //2 5 6

第二种

console.log([…new Set(arr)]); //2 5 6

第三种

let newArr = []

arr.forEach((item) => {

if (newArr.indexOf(item) === -1) {

newArr.push(item)

}

})

console.log(newArr);//2 5 6

第四种

const mynewArr = arr.filter((item, index) => {

return arr.indexOf(item) === index

})

console.log(mynewArr);//2 5 6

第五种

const mynewArr = arr.reduce((pre, item) => {

return pre.includes(item) ? pre : […pre, item]

}, [])

console.log(mynewArr); //2 5 6

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。