三年前端开发人员这样背面试题 迟早进大厂系列 玩转 JavaScript 。点赞、收藏、评论、关注、三连支持!
文章目录
- 一、闭包
- 二、作用域链的理解
- 三、JavaScript原型,原型链?特点?
- 四、事件代理
- 五、JavaScript如何实现继承
- 六、new操作符具体干了什么
- 七、Ajax原理
- 八、如何解决跨域
- 九、异步加载js方法
- 十、哪些操作会造成内存泄漏
- 十一、XML和JSON的区别
- 十二、设计模式
- 十三、js定义对象的方法
- 十四、js的基本数据类型与引用数据类型
- 十五、JavaScript基本规范
- 十六、null跟undefined区别
- 十七、js延迟加载方法
- 十八、let和var的区别
- 十九、map与forEach的区别
- 二十、函数式编程
- 二十一、this的指向
- 二十二、异步编程的实现方式
- 二十三、对原生JavaScript了解程度
- 二十四、js动画与css动画区别即响应实现
- 二十五、js数组和对象的遍历
- 二十六、gulp是什么
- 二十七、vue的双向绑定数据的原理
- 二十八、事件
- 二十九、let 跟 var 跟 const
- 三十、如何渲染几万条数据并不卡住页面
- 三十一、添加、移出、移动、复制查找节点
- 三十二、正则表达式
- 三十三、JavaScript中callee和caller的作用
- 三十四、window.onload和$(document).ready
- 三十五、addEventListener和attachEvent的区别
- 三十六、数组去重
- 三十七、判断两个对象相等
- 三十八、项目做过的性能优化
- 三十九、变量提升
- 四十、MVVM
- 四十一、事件流
- 四十二、JavaScript对象生命周期
- 四十三、caller和callee
- 四十四、JavaScript的组成
- 四十五、js内置对象
- 四十六、JavaScript的基本规范
- 四十七、如何编写高性能JavaScript
- 四十八、实现一个对页面某个节点的拖拽(原生JS)
- 四十九、判断两个对象相等
- 五十、typeof与instanceof
- 五十一、`==`和`===`
一、闭包
- 什么是闭包
- 闭包就是在一个函数里创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用域链
- 闭包特性
- 函数内部在嵌套函数
- 内部函数可以引用外层的参数和变量
- 参数和变量不会被垃圾回收机制回收
- 我对闭包的理解
- 使用闭包主要是为了设置私有方法和变量,优点是避免全局变量污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄漏。
- 闭包的两个用处是:一是可以读取函数内部的变量,二是让这些变量始终保持在内存中
- 封装对象的私有属性和私有方法
- 闭包的好处与坏处
- 好处:能够实现封装和缓存等
- 坏处:消耗内存,不正当使用造成内存泄漏
- 好处:能够实现封装和缓存等
- 使用闭包注意点
- 不能滥用闭包
- 在退出函数之前,将不使用的局部变量全部删除
二、作用域链的理解
- 他的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,不能向下访问,访问到window对象即被终止
- 作用域就是变量与函数的可访问范围,作用域控制着变量与函数的可见性与生命周期
三、JavaScript原型,原型链?特点?
- 原型:每个对象都会在内部初始化一个
prototype
属性,即原型javascript
的所有对象都有一个__proto__
内部属性,这个属性对应的就是这个对象的原型- 除了原型
__proto__
之外,还预置了prototype
属性
- 原型链:如果这个对象不存在这个属性,那么就会去
prototype
里找这个属性,这个prototype
又会有自己的prototype
,一直下去,这就是原型链的概念 - 关系:
instance.constructor.prototype = instance.__proto__
- 原型特点:
javascript
对象通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
四、事件代理
- 又称事件委托,就是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务
- 事件代理的原理是DOM元素的事件冒泡,使用事件代理好处是可以提高性能
- 可以大量节省内存占用,减少事件注册
- 可以实现新增子元素时无需再次对其绑定
五、JavaScript如何实现继承
- 构造继承
- 原型继承
- 实例继承
- 拷贝继承
- 原型机制或
apply
和call
方法实现(建议使用构造函数与原型混合方式)
六、new操作符具体干了什么
- 创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型
- 属性方法被加到this引用的对象中
- 新创建的对象由this所引用,并且最后隐式的返回this
七、Ajax原理
-
原理
- 在用户和服务器之间加了一个中间层(Ajax引擎),通过XmlHttpRequest对象向服务器发异步请求,从服务器获取数据,然后用JavaScript操作DOM,更新页面。
-
代码
-
var xhr = null xhr = new XMLHttpRequest() xhr.open('get',url,true) xhr.send(null) xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { if(xhr.status == 200){ success(xhr.responseText) }else { fail && fail(xhr.status) } } }
-
-
优缺点
- 优点:
- 通过异步模式,提升用户体验
- 优化了浏览器服务器之间的传输,减少了不必要的数据往返,减少了带宽占用
- ajax可实现动态不刷新
- 缺点
- 安全问题
- 对搜索引擎支持比较弱
- 不容易调试
- 优点:
八、如何解决跨域
-
跨域是什么
- 浏览器同源策略
SOP
是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心,最基本的安全功能,如果没有他,浏览器会受到xss、CSFR等攻击。 - 同源:协议+端口+域名 完全相同
- 浏览器同源策略
-
解决方法
-
jsonp(使用DOM里的script标签)
var script = document.createElement('script'); script.type = 'text/javascript'; // 传参并指定回调执行函数为onBack script.src = 'http://www.....:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回调执行函数 function onBack(res) { alert(JSON.stringify(res)); }
-
nginx代理
-
nodejs中间件代理
-
后端在头部信息里设置安全域名
-
九、异步加载js方法
- 设置
script
属性async="async"
- 动态创建
script DOM
XMLHttpRequest
脚本注入- 异步加载库
LABjs
- 模块加载器
Seajs
十、哪些操作会造成内存泄漏
- 什么是JavaScript内存泄漏:指对象不需要还存在时,导致占用的内存不能使用或回收
- 未使用var 声明的全局变量
- 闭包函数
- 循环引用
- 控制台日志(console.log)
setTimeout
的第一个参数使用字符串而不是函数的话,会引发内存泄漏
十一、XML和JSON的区别
- 数据体积方面
- json相对xml来说,数据的体积小,传输速度更快
- 数据交互方面
- json与JavaScript的交互更加方便
- 数据描述方面
- json对数据的描述性比xml较差
- 传输速度方面
- json的速度远大于xml
十二、设计模式
一个抽象的概念,是一种编写代码的方式,可以让人更加容易阅读、维护以及复用
- 工厂模式
- 单例模式
- 比如全局缓存、全局管理状态,只需一个对象即可
- 适配器模式
- 解决两个接口不兼容的情况
- 装饰模式
- 给对象添加功能
- 代理模式
- 发布-订阅者模式
- 外观模式
十三、js定义对象的方法
- 对象字面量:
var obj = {}
- 构造函数:
var obj = new Object()
Object.create()
:var obj = Object.create(Object.prototype)
十四、js的基本数据类型与引用数据类型
- 基本数据类型:
undefined
null
boolean
number
string
symbol
- 引用数据类型:
object
array
function
十五、JavaScript基本规范
- 不在一行声明多个变量
- 使用
===/!==
来比较true/false
或者数值 - 使用对象字面量来替代
new Array
这种形式 - 不使用全局函数
switch
语句必须带有default
分支if
语句必须使用大括号fon-in
循环中的变量应该使用var
关键字明确限定作用域
十六、null跟undefined区别
- undefined 表示不存在的这个值,表示缺少值,还没有定义
- 例如变量被声明了,但是没有赋值,就等于undefined
- null表示一个对象被定义了,值为空值,是一个空对象,没有任何属性方法
- 验证null时,一定要用
===
十七、js延迟加载方法
- 设置script属性
defer="defer"
- 动态创建script DOM:
document.createElement('script')
xmlHttpRequest
脚本注入- 延迟加载工具
lazyLoad
十八、let和var的区别
- let命令不存在变量提升,如果在let前使用,会导致报错
- 如果块区中存在let和const命令,就会形成封闭作用域
- 不允许重复声明,因此,不能在函数内部重新声明参数
十九、map与forEach的区别
- forEach方法,是最基本的方法,就是遍历循环,默认3个传参,分别是遍历的数组内容item,数组索引index,和当前遍历数组array
- map方法,与forEach方法一致,不同的是,它会返回一个新的数组,所以在callback需要有return值,如果没有返回undefined
二十、函数式编程
- 他是一种编程范式
- 具有以下特性:闭包和高阶函数、惰性计算、递归、函数是第一等公民、只用表达式
二十一、this的指向
- this的指向在函数定义时确定不了的,只有在函数执行的时候才能确定this到底指谁,实际上this的最终指向的是那个调用它的对象
- 默认绑定:全局环境指向window(浏览器环境),nodejs环境指向global
- 隐式绑定:被直接对象所包含的函数调用时,this就隐式绑定到了该直接对象
- 隐式丢失:隐式丢失是指被隐式绑定包含的函数丢失了绑定对象,从而绑定到了window
- 显示绑定:通过call、apply、bind把对象绑定到this上
- 箭头函数没有this指向,所以要通过作用域链来查找this指向
二十二、异步编程的实现方式
- 回调函数
- 简单、容易理解
- 不利于维护、代码耦合性高
- 事件监听
- 容易理解、可以绑定多个事件
- 事件驱动型、流程不够清晰
- 发布/订阅者模式
- 类似事件监听、但是可以通过消息中心了解现在的发布者、订阅者
- Promise对象
- 可以利用then方法,进行链式写法、可以书写错误时的回调函数
- 编写和理解相对比较困难
- generator函数
- 函数体内外的数据交换、错误处理机制
- 流程管理不方便
- async函数
- 内置执行器、更好的语义、更广的实用性、返回的是promise、结构清晰
- 错误处理机制没有
二十三、对原生JavaScript了解程度
- 数据类型、运行、对象、Function、继承、闭包、作用域、原型链、事件、json、RegExp、ajax、DOM、BOM、内存泄漏、跨域、异步加载、模板引擎、前端MVC、路由、模块化、canvas、ECMAScript
二十四、js动画与css动画区别即响应实现
- css动画在性能上稍微好一点,浏览器会对css3动画做一些优化,代码相对简单,但是不够灵活、兼容性低
- js动画可以控制单帧动画,同时可以兼容IE6,对于一些复杂的动画,使用JavaScript会比较靠谱,对于一些小动画使用css
二十五、js数组和对象的遍历
循环遍历数组导致js性能问题低
for in
循环- 需要分析出array的每个属性,这个操作性能开销很大。
for
循环forEach
- 参数:value、index
- 无法遍历对象
- IE不支持该方法
- forEach无法使用break
二十六、gulp是什么
- 前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器
- 流:建立在面向对象的基础上的一种抽象的处理数据的工具
- 特点:易于使用、构建快速、易于学习
二十七、vue的双向绑定数据的原理
- 采用的是数据劫持结合发布者-订阅者模式的方式,通过
Object.defineProperty()
来劫持各个属性的setter、getter
,在数据变动时发布消息给订阅者,触发相应的回调
二十八、事件
- addEventListener的第三个参数设置为
- true:表示该元素在事件的捕获阶段响应事件
- false:表示该元素在事件的冒泡阶段响应事件
二十九、let 跟 var 跟 const
- let
- 允许你声明一个作用域被限制在块级中的变量、语句或者表达式
- let绑定不受变量提升的约束
- 该变量处于从块开始到初始化处理的暂存死驱
- var
- 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的
- 由于变量声明总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效在代码开头声明
- const
- 声明创建一个值的只读引用
三十、如何渲染几万条数据并不卡住页面
通过
window.requestAnimationFrame()
来每16ms刷新一次
- 该方法传入一个回调函数作为参数
三十一、添加、移出、移动、复制查找节点
- 创建新节点
- createDocumentFragment() 创建一个DOM片段
- createElement() 创建一个具体的元素
- createTextNode() 创建一个文本节点
- 添加、移出、替换、插入
- appendChild()
- removeChild()
- replaceChild()
- insertBefore()
- 查找
- getElementsByTagName()
- getElementsByName()
- getElementById()
三十二、正则表达式
- 当使用
RegExp()
构造函数时,不仅需要转义引号(\
表示),并且还需要双反斜杠(\\
表示一个\
)。
三十三、JavaScript中callee和caller的作用
- caller是返回一个对函数的引用,该函数调用了当前函数
- callee是返回正在被执行的function函数,也就是指定的function对象的正文
三十四、window.onload和$(document).ready
- window.onload:必须等到页面内容包括图片的所有元素都渲染完毕,才能执行该方法
- $(document).ready:是DOM结构绘制完毕后就执行,不必等到加载完毕
三十五、addEventListener和attachEvent的区别
- 前者是符合w3c规范的标准方法,后者是IE低版本的非标准方法
- 前者支持事件冒泡和事件捕获,后者只支持事件吗冒泡
- 前者的第一个参数中事件类型不需要添加on,后者需要添加on
- 如果为同一个元素绑定多个事件,前者会按照事件绑定的顺序执行,后者会按照事件绑定的顺序倒序执行
三十六、数组去重
- 利用ES6 Set去重
Array.from(new Set(arr))
- splice去重
- 双for循环实现
- indexOf去重
- 看数组中第一个元素出现的位置
- 利用sort()
- 排序,遍历与相邻元素比较
- 利用includes
- 检测数组是否有某个值
- 利用hasOwnPropery() 判断是否存在对象属性
- 利用filter
- 利用递归
- 利用Map数据结构去重
三十七、判断两个对象相等
- 转换为字符串比较
JSON.stringify(obj1)==JSON.stringify(obj2)
三十八、项目做过的性能优化
- 减少HTTP请求
- 减少DNS查询
- 使用CND
- 图片懒加载
- 避免重定向
- 减少DOM元素数量
- 减少DOM操作
- 使用外部JavaScript与css
- 压缩JavaScript与css、图片等
- 减少iframe使用
- 避免图片src为空
- 把样式表反正link中
- 把JavaScript放在页面底部
三十九、变量提升
- 当执行js代码时,会生成执行环境,只要代码不是在函数里写的,就是在全局执行环境中,函数中的代码就会产生函数执行环境,至此两种执行方式
- 变量提升
- 将声明的代码移动到了底部
- 在执行环境中,会有两个阶段,第一个阶段是创建的阶段,JS解释器会找出需要提升的变量和函数,并给她们在内存中开辟空间,函数的话会将整个函数存入内存,变量只声明赋值为undefined,所以在第二个阶段,也就是代码执行阶段,我们可以提前使用
- 在提升的过程中,相同的函数覆盖上一个函数,并且函数优先与变量提升
四十、MVVM
-
MVVM模式
- MVVM是
model-view-viewModel
的简写,是一个软件架构设计模式
- MVVM是
-
MVVM原理
-
MVVM的出现促进了前后端分离,极大提高了开发效率
-
Model:数据模型,泛指后端进行的各种业务逻辑和数据操控
-
View:视图层,也就是用户界面。前端主要由HTML|CSS来构建,已经产生了各大MVVM框架,如VUE、React、Angular···
-
ViewModel:是由前端开发人员生成和维护的视图数据层。他是MVVM模式的核心,是连接view和model的桥梁。在这一层,前端开发人员从后端获取到Model数据进行转换处理,做二次封装,以生成符合view层使用预期的视图数据模型,需要注意的是viewModel所封装出来的数据模型包括视图的状态和行为两部分,而model层的数据只包含状态的,view视图层是用来展示行为的,这样的封装使得viewModel可以完整的描述view层,由于实现了双向绑定,ViewModel内容会实时展现在view层,开发者不用在低效的去操纵DOM更新视图,View层展现的不是model层的数据,而是ViewModel层的数据,由ViewModel负责与Model层交互,这就完全解耦了View层和Model层,这个解耦是前后端分离最重要的一点。
-
VIewModel层,就像是一个中转站,负责转换Model中的数据对象来让数据变得更加容易管理使用,该层向上与视图层进行双向数据绑定,向下与model数据层通过请求接口进行数据交互,起到呈上起下的作用
-
-
MVVM设计模式的优缺点
- 优点:双向数据绑定,单向绑定与双向绑定
- 单向绑定:ViewModel层变化时,自动更新View层
- 双向绑定:在单向绑定的基础上,View层变化时,自动更新ViewModel
- 缺点
- Bug很难调试。因为使用双向绑定模式,当看到界面异常了,就可能是view层代码有bug,有可能是model层代码有问题。另外,数据绑定的声明是指令式的写在View的模板中,这是无法打断点debug的
- 大模块中Model也会很大 ,虽然使用方便了也很容易保证了数据的一致性,但是长期使用不释放内存就造成了花费更多的内存
- 对于大型图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高
- 优点:双向数据绑定,单向绑定与双向绑定
四十一、事件流
事件流分为三个阶段,捕获阶段、处于目标节点阶段、冒泡阶段
- 捕获事件流
- 从根节点开始执行,一直往子节点查找执行,直到查找到目标节点
- 冒泡事件流
- 从目标节点开始执行,一直往父节点冒泡查找执行,直到查找到根节点
四十二、JavaScript对象生命周期
- 当创建一个JavaScript对象时,JavaScript会自动为该对象分配适当的内存
- 垃圾回收器定期扫描对象,并计算引用了该对象的其他对象的数量
- 如果被引用的数量为0,或唯一引用是循环的,那么该对象的内存即可回收
四十三、caller和callee
- caller
- 返回一个函数的引用,这个函数调用了当前的函数(谁在调用我)
- callee
- 返回正在执行的函数本身的引用(正在执行的函数)
四十四、JavaScript的组成
- ECMAScript:JavaScript语言基础
- BOM:浏览器对象模型:提供了浏览器窗口之间进行交互的对象方法
- DOM:文档对象模型:规定了访问HTML和XML的接口
四十五、js内置对象
- 数据封装类型对象:Object、Array、Boolean、Number、String
- 其他对象:Function、Arguments、Math、Date、RegExp、Error
- ES6新增对象:Symbol、Map、Set、Promise、Proxy
四十六、JavaScript的基本规范
- 代码缩进,4个空格
- 代码段使用{}包裹
- 语句结束使用分号;
- 变量和函数在使用前声明
- 以大写字母开头名构造函数,全大写命名常量
- 规范定义JSON对象,补全双引号
- 用{}和[]声明对象数组
四十七、如何编写高性能JavaScript
- 遵守严格模式
'use strict'
- 将js脚本放到页面底部
- 将js脚本成组打包,减少请求
- 进来使用局部变量来保存全局变量
- 尽量减少使用闭包
- 使用window对象属性方法时,省略window
- 尽量减少对象成员嵌套
- 缓存DOM节点访问
- 尽量使用直接量创建对象数组
- 最小化重绘和回流
四十八、实现一个对页面某个节点的拖拽(原生JS)
- 给需要绑定的元素节点绑定
mousedown
,mousemove
,mouseup
事件 mousedown
事件触发后,开始拖拽mousemove
时,需要通过event.clientX
和event.clientY
获取拖拽位置,并实时更新位置mouseup
时,拖拽结束
四十九、判断两个对象相等
- 转换 为字符串判断
JSON.stringify
五十、typeof与instanceof
两者是用来判断一个变量是否为空或者是什么类型的
- typeof:检测出除了null之外的原型类型(String、Number、Boolean、undefined),对于对象类型的能判断出function,对于Array、Null使用typeof判断都返回Object
- 判断一个值是否为数组,使用Array.isArray()
- instanceof:只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型
[] instanceof Array //true
[] instanceof Object //true
五十一、==
和===
- == :两者的类型不一样,就会进行类型转换,然后在比较
- ===:直接比较类型或者值是否相等