一、数据类型
1.JavaScript有哪些数据类型,它们的区别?
Null、Undefined、Boolean、String、Number、Object
基本数据类型:Boolean、String、Number,因为简单、数据量小、大小固定、频繁调用,所以保存在栈中
引用数据类型:Object、Array、Function,因为结构复杂、占据空间大、大小不固定,所以保存在堆中,栈中之保存了指针,指向数据在栈中的地址
2.数据类型检测的方式有哪些
- typeOf,只能检查基本数据类型和函数,null返回Object,因为null的类型标签为0,而Object的类型标签为000
- instanceOf,只能检查引用类型,用来检测当前对象在其原型链中是否存在一个构造函数prototype属性
- constructor,能判断全部类型,因为是通过对象的原型判断,所以当原型被修改,就不能判断了
- Object.prototype.toString.call(),能判断全部类型
为什么不适用被检测对象的
toString()
?因为Array、Function等作为Objec的实例,都重写了toString()
方法
3.判断数组的方式有哪些
-
Object.prototype.toString.call()
-
obj.__proto__ === Array.prototype
-
Array.isArray
-
obj instanceof Array
4.null和undefined区别
null表示空对象
undefined表示未定义
5.typeof null 的结果是什么,为什么?
6.intanceof 操作符的实现原理及实现
使用Object.getPrototypeOf()
与while(true)
去判断构造函数的prototype
是否在被检测对象的原型链上
7.为什么0.1+0.2 ! == 0.3,如何让其相等
8.如何获取安全的 undefined 值?
9.typeof NaN 的结果是什么?
10.isNaN 和 Number.isNaN 函数的区别?
11.==
操作符的强制类型转换规则?
12.其他值到字符串的转换规则?
13.其他值到数字值的转换规则?
14.其他值到布尔类型的值的转换规则?
15.|| 和 && 操作符的返回值?
16.Object.is() 与比较操作符 ===
==
的区别?
Object.is()
一般情况下与===
相同,但它处理了一些特殊情况,比如Object.is(-0,+0) // false
和Object.is(NaN,NaN) // true
1 |
|
1 | // ES5,由 Babel 转译 |
6.扩展运算符的作用及使用场景
7.Proxy 可以实现什么功能?
8.对对象与数组的解构的理解
9.如何提取高度嵌套的对象里的指定属性?
10.对 rest 参数的理解
11.ES6中模板语法与字符串处理
三、JavaScript基础
1.new操作符的实现原理
创建一个对象
设置当前对象的原型,将对象的原型设置为函数的prototype
对象
修改函数的this指向当前对象
判断函数的返回值类型,如果是值,返回创建的对象。如果是引用类型,返回引用类型的对象
1 | function myNew() { |
2.map和Object的区别
Map | Object | |
---|---|---|
键的命名 | 默认情况不包含任何键,只包含显式插入的键 | 有一个原型,原型链上的键名可能会与在对象设置的键名冲突 |
键的类型 | 任意值,包括函数、对象或任意基本类型 | 必须是String 或Symbol |
键的顺序 | 有序,当迭代时,以插入的顺序返回键值 | 无序 |
size | 通过size 属性获取 |
只能手动计算 |
迭代 | 可以直接被迭代 | 需要先获取键然后才能迭代 |
性能 | 适用于频繁增删键值对的情况 |
3.map和weakMap的区别
4.JavaScript有哪些内置对象
全局的对象(global objects)
- 值属性:
- 函数属性
- 基本对象
- 数字和日期
- 字符串
- 可索引的集合对象
- 使用键的集合对象
- 矢量集合
- 结构化数据
- 控制抽象对象
- 反射
- 国际化
- WebAssembly
- 其他
5.常用的正则表达式有哪些?
6.对JSON的理解
7.JavaScript脚本延迟加载的方式有哪些?
- defer,让脚本的加载和文档的解析同步解析,然后在文档解析完成后再执行这个脚本,保证页面渲染不被阻塞,执行顺序可测,但在一些浏览器中不会
- async,使脚本异步加载,不会阻塞页面的渲染进程,但是当脚本加载完成后会立即执行,这个时候如果文档没有解析完成会阻塞,渲染顺序不可测
- 动态创建DOM
- setTimeout
- 脚本最后加载
8.JavaScript 类数组对象的定义?
9.数组有哪些原生方法?
10.Unicode、UTF-8、UTF-16、UTF-32的区别?
11.常见的位运算符有哪些?其计算规则是什么?
12.为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组?
13.什么是 DOM 和 BOM?
14.对类数组对象的理解,如何转化为数组
15.escape、encodeURI、encodeURIComponent 的区别
16.对AJAX的理解,实现一个AJAX请求
1 | let xhr = new XMLHttpRequest(); |
17.
18.什么是尾调用,使用尾调用有什么好处?
19.ES6模块与CommonJS模块有什么异同?
20.常见的DOM操作有哪些
21.use strict是什么意思 ? 使用它区别是什么?
22.如何判断一个对象是否属于某个类?
23.强类型语言和弱类型语言的区别
24.解释性语言和编译型语言的区别
(1)解释型语言 使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。是代码在执行时才被解释器一行行动态翻译和执行,而不是在执行之前就完成翻译。解释型语言不需要事先编译,其直接将源代码解释成机器码并立即执行,所以只要某一平台提供了相应的解释器即可运行该程序。其特点总结如下
- 解释型语言每次运行都需要将源代码解释称机器码并执行,效率较低;
- 只要平台提供相应的解释器,就可以运行源代码,所以可以方便源程序移植;
- JavaScript、Python等属于解释型语言。
(2)编译型语言 使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。在编译型语言写的程序执行之前,需要一个专门的编译过程,把源代码编译成机器语言的文件,如exe格式的文件,以后要再运行时,直接使用编译结果即可,如直接运行exe文件。因为只需编译一次,以后运行时不需要编译,所以编译型语言执行效率高。其特点总结如下:
- 一次性的编译成平台相关的机器语言文件,运行时脱离开发环境,运行效率高;
- 与特定平台相关,一般无法移植到其他平台;
- C、C++等属于编译型语言。
两者主要区别在于: 前者源程序编译后即可在该平台运行,后者是在运行期间才编译。所以前者运行速度快,后者跨平台性好。
25.for…in和for…of的区别
for…in遍历的是对象的键名,for…of遍历的是对象的键值
1 | var list = [1,2,3] |
for…in会遍历对象的整个原型链
对于数组for…in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…in只会返回数组的下标对应的属性值
for...in
主要用于遍历对象,不适用于数组,for...of
可以用来遍历所有包含迭代器接口的数据结构,返回各项的值
26.如何使用for…of遍历对象
类数组对象
1 | var obj = { |
非类数组对象
1 | //方法一: |
27.ajax、axios、fetch的区别
AJAX:是一种创建交互式网页应用的网页开发技术,是在一种无需重新加载整个页面的情况下,能够更新部分网页的技术,通过后台与服务器进行少量数据交换,可以是网页实现一步更新
缺点:1)本身针对MVC编程,不符合MVVM规范;2)基于远程XHR开发,XHR本身架构不够清晰;3)不符合关注分离的原则;4)配置和调用方式非常混乱,而且基于时间的一步模型不友好
Fetch:基于ES6的promise对象设计,是原生js
优点:1)语法简洁,更加语义化;2)基于标准Promise实现,支持async/await;3)更加底层,提供丰富的API;4)脱离了XHR,是基于ES6的新规范实现的
缺点:1)只对网络请求报错,第400、500都当做成功的请求,不会reject,只有网络请求错误导致请求不能完成是,才会reject;2)默认不带cookie,需要添加配置项;3)不支持abort,不支持超时控制,setTimeout和Promise.reject的实现的超时控制并不能阻止请求过程在后台的运行;4)没办法原生监测请求的进度,XHR可以
Axios:是基于Promise封装的http请求客户端
1)不同端发起不同请求(浏览器端发起XMLHttpRequest请求,Node端发起http请求;2)支持Promise的API;3)监听请求和返回;4)对请求和返回进行转化;5)取消请求;6)自动转换json数据;7)客户端支持抵御XSRF攻击
28.数组的遍历方法有哪些
29.forEach和map方法有什么区别
四、原型与原型链
1.对原型、原型链的理解
js中是使用构造函数来新建一个对象,在每个构造函数内部都有一个prototype属性,它的属性值是一个对象,包含了可以由该创造函数的所有实例共享的属性和方法。当使用构造函数创建一个新的对象后,在这个对象内部将包含一个指针,指向构造函数的prototype属性对应的值,这个指针被称为原型,可以使用Object.getPrototype()
来获取
当访问一个对象的属性时,如果当前对象内部不存在这个属性,那么就会去它的原型对象中去寻找这个属性,而这个原型对象又会有自己的原型,一直找下去的链路成为 原型链。原型链的尽头一般都是Object.prototype.__proto__
为null
js对象是通过引用来传递的,创建的每个对象实体中并没有一份属于自己的原型副本,因此当修改原型时,与之相关的对象也会继承这一改变
2.原型修改、重写
3.原型链指向
4.原型链的终点是什么?如何打印出原型链的终点?
Object.prototype.__proto__
5.如何获得对象非原型链上的属性?
``hasOwnProperty```
五、执行上下文/作用域链/闭包
1.对闭包的理解
有权访问另一函数作用域中变量的函数
用途:
- 在函数外部能够访问函数内部的变量,创建私有变量,vue的判断是否是html标签
- 将已经结束的函数上下文中的变量对象继续保存在内存中
2.对作用域、作用域链的理解
全局作用域
最外层函数和最外层函数外面定义的变量拥有全局作用域
所有未定义直接赋值的变量自动声明为全局作用域
所有window对象的属性拥有全局作用域
全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突。
函数作用域
函数作用域声明在函数内部的变零,一般只有固定的代码片段可以访问到
作用域是分层的,内层作用域可以访问外层作用域,反之不行
块级作用域
使用ES6中新增的let和const指令可以声明块级作用域,块级作用域可以在函数中创建也可以在一个代码块中的创建(由{ }
包裹的代码片段)
let和const声明的变量不会有变量提升,也不可以重复声明
在循环中比较适合绑定块级作用域,这样就可以把声明的计数器变量限制在循环内部。
作用域链: 在当前作用域中查找所需变量,但是该作用域没有这个变量,那这个变量就是自由变量。如果在自己作用域找不到该变量就去父级作用域查找,依次向上级作用域查找,直到访问到window对象就被终止,这一层层的关系就是作用域链。
作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,可以访问到外层环境的变量和函数。
作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。
3.对执行上下文的理解
执行上下文分为 全局执行上下文和 函数执行上下文,
全局执行上下文:js执行时,会创建一个全局的window对象,并且设置this的值等于这个全局对象,任何不在函数内部的都是全局执行上下文,一个程序中只会存在一个全局执行上下文
函数执行上下文:函数执行时,会创建一个新的执行上下文
执行上下栈:js引擎使用执行上下栈来管理执行上下文
当执行代码时,首先遇到全局代码,会创建一个全局执行上下文并且压入执行栈中,每当遇到一个函数调用,就会为该函数创建一个新的执行上下文并且压入栈顶,引擎会执行位于上下文栈顶的函数,当函数执行完毕后,执行上下文从栈中弹出,继续执行下一个上下文,当所有代码都执行完毕后,从栈中弹出全局执行上下文
创建执行上下文:
创建执行上下文有两个阶段:创建阶段和执行阶段
1)创建阶段
(1)this绑定
- 在全局执行上下文中,this指向全局对象(window对象)
- 在函数执行上下文中,this指向取决于函数如何调用。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined
(2)创建词法环境组件
- 词法环境是一种有标识符——变量映射的数据结构,标识符是指变量/函数名,变量是对实际对象或原始数据的引用。
- 词法环境的内部有两个组件:加粗样式:环境记录器:用来储存变量个函数声明的实际位置外部环境的引用:可以访问父级作用域
(3)创建变量环境组件
- 变量环境也是一个词法环境,其环境记录器持有变量声明语句在执行上下文中创建的绑定关系。
2)执行阶段 此阶段会完成对变量的分配,最后执行完代码。
简单来说执行上下文就是指:
在执行一点JS代码之前,需要先解析代码。解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来,变量先赋值为undefined,函数先声明好可使用。这一步执行完了,才开始正式的执行程序。
在一个函数执行之前,也会创建一个函数执行上下文环境,跟全局执行上下文类似,不过函数执行上下文会多出this、arguments和函数的参数。
- 全局上下文:变量定义,函数声明
- 函数上下文:变量定义,函数声明,
this
,arguments
六、this/call/apply/bind
1.对this对象的理解
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
- 函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
2.call() 和 apply() 的区别?
1 |
|
七、异步编程
1.异步编程的实现方式?
2.setTimeout、Promise、Async/Await 的区别
3.对Promise的理解
Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调,它比传统的解决方案回调函数和事件更合理和更强大。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
4.Promise的基本用法
5.Promise解决了什么问题
6.Promise.all和Promise.race的区别的使用场景
7.对async/await 的理解
8.await 到底在等啥?
9.async/await的优势
10.async/await对比Promise的优势
代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担
Promise传递中间值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅
错误处理友好,async/await可以⽤成熟的try/catch,Promise的错误捕获⾮常冗余
调试友好,Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的每⼀步。
11.async/await 如何捕获异常
12.并发与并行的区别?
- 并发是宏观概念,我分别有任务 A 和任务 B,在一段时间内通过任务间的切换完成了这两个任务,这种情况就可以称之为并发。
- 并行是微观概念,假设 CPU 中存在两个核心,那么我就可以同时完成任务 A、B。同时完成多个任务的情况就可以称之为并行。
13.什么是回调函数?回调函数有什么缺点?如何解决回调地狱问题?
14.setTimeout、setInterval、requestAnimationFrame 各有什么特点?
八、面向对象
1.对象创建的方式有哪些?
- 工厂模式
- 构造函数模式
- 原型模式
- 组合使用构造函数和原型模式
- 动态原型模式
- 寄生构造函数模式
2.对象继承的方式有哪些?
- 原型链继承,但因为原型被所有实例对象共享,在修改引用类型的数据时,会造成污染
- 构造函数继承,通过在子类型的函数中调用超类型的构造函数来实现
- 组合继承,将原型继承和构造函数继承组合起来
- 原型式继承
- 寄生式继承
- 寄生式组合继承
九、垃圾回收与内存泄漏
1.浏览器的垃圾回收机制
2.哪些情况会导致内存泄漏
基础
1.null 和 undefined 的区别
null
表示无
的对象,也就是此处不应该有值;而undefined
表示未定义。- 在转换数字的时候,
Number(null)
为0
,而Number(undefined)
为NaN
null
:
- 作为函数的参数,表示该函数的参数不是对象。
- 作为对象原型链的终点。
Object.prototype.__proto__ === null
undefined
:
- 变量被声明但是没有赋值,等于
undefined
。 - 调用函数时,对应的参数没有提供,也是
undefined
。 - 对象没有赋值,这个属性的值为
undefined
。 - 函数没有返回值,默认返回
undefined
。
new关键字
新建一个对象obj
把obj的和构造函数通过原型链连接起来
将构造函数的this指向obj
如果该函数没有返回对象,则返回this
2.事件流
3.typeof 和 instanceof 的区别
typeof
:对某个变量类型的检测,基本类型除了null
之外,都能正常地显示为对应的类型,引用类型除了函数会显示为function
,其他都显示为object
。instanceof
主要用于检测某个构造函数的原型对象在不在某个对象的原型链上。
为什么"object"
?
因为 JavaScript 早起版本是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000
开头代表是对象然而 null
表示为全零,所以它错误判断为 object
4.一句话描述this
对于函数而言,指向最后调用函数的那个对象,是函数运行时内部自动生成的一个内部对象,只能在函数内部使用;对于全局而言,this
指向 window
- 普通函数中
this
的指向,是this
执行时的上下文 - 箭头函数中
this
的指向,是this
定义时的上下文
改变this的指向?
1)保存指针let that = this
2)使用箭头函数:因为箭头函数不会创建其自身的执行上下文,函数中的this取决于外部函数,即谁调用它this
就继承谁
3)call/apply/bind
5.执行上下文
闭包
是什么?
在JavaScript中,根据作用域规则,内部函数总是可以访问外部函数声明的变量。
当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数应用外部函数的变量依旧保存在内存中,这些变量的集合称为闭包
函数B在函数A中调用了A中的变量,B就称之为A的闭包
优缺点
- 优点
1)缓存持久化
2)实现柯里化
- 缺点
1)内存消耗:闭包产生的变量无法被销毁
2)性能问题:闭包内部变量优先级高于外部变量,所以需要作用域链要多查找一级,影响查找速度
柯里化
是什么?
通过将接收多个参数换成一个参数,每次调用返回新函数的技术
- 通过闭包管理
- 支持链式调用
- 每次运行返回一个
function
为什么?
- 参数复用
1 | // 校验数字 |
- 提前确认
- 延迟运行
作用域与作用域链
- 作用域:
- 作用域链:本质上是一个指向变量对象的指针列表
变量提升与函数提升
函数提升是为了解决相互递归的问题,大体上可以解决自上而下的顺序问题
原型与原型链
是什么?
- 原型:在JavaScript中,每当定义一个对象时,对象中都会包含一些预定义的属性。
其中每个函数对象都有一个prototype
属性,这个属性的指向被称为这个函数对象的原型对象(简称原型)
- 原型链:查找原型上存在的某个属性的的链式路径
如果某个实例对象不存在的某个属性,那么JavaScript会去改构造函数的原型上去找;如果原型上没有找到,那么会继续往Object
的原型上,如果Object
的原型上还是没有,会返回undefined
为什么?
为了节约内存空间
__proto__
与prototype
的关系
每个 JavaScript 对象(普通对象和函数对象)都具有一个属性 __proto__
,这个属性会指向该对象的原型。
堆与栈
Event Loop(事件循环)
为什么?
因为JavaScript是单线程语言,一次只能执行一件程序。单线程在程序执行的时候,所走的程序路径按照连续顺序排下来,依次执行,只有前面的程序执行结束才会执行接下来的程序。
如果遇到一些需要等待的程序,比如说setTimeOut
就会造成延迟
因此,JavaScript为了协调时间、用户交互、脚本、渲染、网络等,就使用事件循环(Event Loop)
是什么?
从script
开始读取,不断循环,从“任务队列”中读取执行事件的过程。
- 执行过程
- 一开始整个脚本
script
作为一个宏任务执行 - 执行过程中,同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当前宏任务执行出对,检查微任务列表是否存在,有则一次执行,直至全部执行完毕
- 执行浏览器的UI线程的渲染工作
- 检查是否有
web worker
任务,有则执行 - 执行完本轮的宏任务,回到步骤2,依次循环,知道宏任务和微任务为空
宏任务、微任务
是什么?
事件循环的异步队列,是为了解决单线程阻塞的问题
宏任务队列可以有多个,但是微任务队列只有一个
宏任务:
script
、setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
微任务:
MutationObserver
、Promise.then()/catch()
、fetch/axios(以Promise为基础开发)、V8垃圾回收过程、process.nextTick
(Node独有)
babel 编译原理
babylon
将ES6/ES7
代码解析成AST
babel-traverse
对AST
进行遍历转译,得到新的AST
- 新
AST
通过babel-generator
转换成ES5
浏览器渲染过程
1)创建DOM树
HTML解析器解析HTML元素,创建DOM树
2)创建CSS规则树
CSS解析器解析CSS文件和内联与行内样式,生成页面的样式表
3)创建Render树
将DOM树与CSS规则树关联起来,创建Render树
4)布局Layout
根据Render树,浏览器开始布局,为每个Render树上的节点确定一个在显示器出现的精确坐标
5)绘制Painting
在Render树和节点显示坐标的基础上,调用每个节点的paint
方法,绘制出来
- 重绘:当元素样式的改变不影响布局时,只会对元素进行更新,此时只需要UI层面的额像素绘制,损耗较少
触发条件:background、color、visibility
- 回流:当元素尺寸、结构或者触发某些属性时,浏览器会重新渲染页面
触发条件:1)添加删除DOM元素;2)修改边框、边距、宽高等影响元素定位 的属性;3)浏览器窗口resize
回流必定会发生重绘,重绘不一定会引发回流。
1 | function a() { |
最后更新: 2021年11月18日 14:00