该内容主要整理关于 JS 的相关面试题,包含从基础面试题到高级面试题总结,很多笔试题也囊括在内。其他内容面试题请移步至 2021 最新最全的前端面试题集锦 查看。
目录
一、JS 经典面试题(附答案)【字节最爱】
1、0.1 + 0.2 === 0.3 ?为什么?
JavaScirpt 使用 Number 类型来表示数字(整数或浮点数),遵循 IEEE 754 标准,通过 64 位来表示一个数字(1 + 11 + 52)
- 1 符号位,0 表示正数,1 表示负数 s
- 11 指数位(e)
- 52 尾数,小数部分(即有效数字)
最大安全数字:Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1
,转换成整数就是 16 位,所以 0.1 === 0.1,是因为通过 toPrecision(16)
去有效位之后,两者是相等的。
在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会发生无限循环,然后进行对阶运算,JS 引擎对二进制进行截断,所以造成精度丢失。
所以总结:精度丢失可能出现在进制转换和对阶运算中
2、JS 数据类型
-
基本类型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020)
-
引用类型:Object,对象子类型(Array,Function)
-
扩展:symbol 有什么用处?
- 可以用来表示一个独一无二的变量防止命名冲突。
- 还可以利用 symbol 不会被常规的方法(除了
Object.getOwnPropertySymbols
外)遍历到,所以可以用来模拟私有变量。 - 主要用来提供遍历接口,布置了
symbol.iterator
的对象才可以使用for···of
循环,可以统一处理数据结构。调用之后回返回一个遍历器对象,包含有一个next
方法,使用next
方法后有两个返回值value
和done
分别表示函数当前执行位置的值和是否遍历完毕。 Symbol.for()
可以在全局访问symbol
3、如何判断 JS 数据类型
参考文章:https://juejin.cn/post/6844903623231537159
-
typeof
typeof
返回一个表示数据类型的字符串,返回结果包括:number、string、boolean、object、undefined、function。typeof
可以对基本类型 number、string、boolean、undefined 做出准确的判断(null除外,typeof null === “object”
);而对于引用类型,除了 function 之外返回的都是 object。但当我们需要知道某个对象的具体类型时,typeof
就显得有些力不从心了。typeof 1; // number 有效 typeof ‘ ’; // string 有效 typeof true; // boolean 有效 typeof undefined; // undefined 有效 typeof null; // object 无效 typeof new Function(); // function 有效 typeof [] ; // object 无效 typeof new Date(); // object 无效 typeof new RegExp(); // object 无效 typeof NaN; // number Not a Number,表示非数字
-
instanceof
当我们需要知道某个对象的具体类型时,可以用运算符
instanceof
,instanceof
操作符判断左操作数对象的原型链上是否有右边这个构造函数的prototype
属性,也就是说指定对象是否是某个构造函数的实例,最后返回布尔值。 检测的我们用一段伪代码来模拟instanceof内部执行过程:instanceof (A,B) = { var L = A.__proto__; var R = B.prototype; if(L === R) { // A的内部属性__proto__指向B的原型对象 return true; } return false; }
从上述过程可以看出,当 A 的
__proto__
指向 B 的prototype
时,就认为A就是B的实例,我们再来看几个例子:[] instanceof Array; // true [] instanceof Object; // true new Date() instanceof Date;// true new Date() instanceof Object;// true function Person(){}; new Person() instanceof Person;// true new Person() instanceof Object;// true
-
constructor
constructor
属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。var f = new F(); f.constructor === F;// true
但是
constructor
属性易变,不可信赖,这个主要体现在自定义对象上,当开发者重写prototype
后,原有的constructor
会丢失。function F() {} F.prototype = { _name: 'Eric', }; var f = new F(); f.constructor === F; // false
因此,为了规范,在重写对象原型时一般都需要重新给
constructor
赋值,以保证实例对象的类型不被改写。 -
Object.prototype.toString
toString
是 Object 原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是toString
运行时this
指向的对象类型,返回的类型格式为[object,xxx]
,xxx是具体的数据类型,其中包括:
String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上所有对象的类型都可以通过这个方法获取到。Object.prototype.toString.call('') ; // [object String] Object.prototype.toString.call(1) ; // [object Number] Object.prototype.toString.call(true) ; // [object Boolean] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call(new Date()) ; // [object Date] Object.prototype.toString.call([]) ; // [object Array] Object.prototype.toString.call(new RegExp()) ; // [object RegExp] Object.prototype.toString.call(new Error()) ; // [object Error] Object.prototype.toString.call(document) ; // [object HTMLDocument] Object.prototype.toString.call(window) ; //[object Window]
需要注意的是,必须通过
Object.prototype.toString.call
来获取,而不能直接new Date().toString()
,从原型链的角度讲,所有对象的原型链最终都指向了Object
,按照JS变量查找规则,其他对象应该也可以直接访问到Object
的toString
方法,而事实上,大部分的对象都实现了自身的toString
方法,这样就可能会导致Object
的toString
被终止查找,因此要用call
来强制执行Object
的toString
方法。 -
总结:
typeof
可以准确地判断出基本类型,但是对于引用类型除function
之外返回的都是object
;- 已知是引用类型的情况可以选用
instanceof
或constructor
方法进行具体类型的判断:instanceof
是基于原型链的;constructor
属性易变,不可信赖,为了规范,在重写对象原型时一般都需要重新给constructor
赋值,以保证实例对象的类型不被改写;
Object.prototype.toString.call()
通用但很繁琐。
4、事件如何实现的?事件流?
-
事件
基于发布订阅模式,就是在浏览器加载的时候会读取事件相关的代码,但是只有实际等到具体的事件触发的时候才会执行。
比如点击按钮,这是个事件(Event),而负责处理事件的代码段通常被称为事件处理程序(Event Handler),也就是「启动对话框的显示」这个动作。
在 Web 端,我们常见的就是 DOM 事件:DOM0
级事件,直接在 html 元素上绑定on-event
,比如onclick
,取消的话,dom.onclick = null
,同一个事件只能有一个处理程序,后面的会覆盖前面的。DOM2
级事件,通过addEventListener
注册事件,通过removeEventListener
来删除事件,一个事件可以有多个事件处理程序,按顺序执行,捕获事件和冒泡事件。DOM3
级事件,增加了事件类型,比如 UI 事件,焦点事件,鼠标事件等。
-
事件流
事件流是网页元素接收事件的顺序,"DOM2级事件"规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获,为截获事件提供机会。然后是实际的目标接受事件。最后一个阶段是事件冒泡阶段,可以在这个阶段对事件做出响应。虽然捕获阶段在规范中规定不允许响应事件,但是实际上还是会执行,所以有两次机会获取到目标对象。
5、new 一个函数发生了什么?
构造调用:
- 创造一个全新的对象
- 这个对象会被执行
[[Prototype]]
连接,将这个新对象的[[Prototype]]
链接到这个构造函数.prototype
所指向的对象 - 这个新对象会绑定到函数调用的
this
- 如果函数没有返回其他对象,那么
new
表达式中的函数调用会自动返回这个新对象
6、作业域?作用域链?
-
作业域
ES5 中只存在两种作用域:全局作用域和函数作用域。在 JavaScript 中,我们将作用域定义为一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套子作用域中根据标识符名称进行变量(变量名或者函数名)查找
-
作业域链
当访问一个变量时,编译器在执行这段代码时,会首先从当前的作用域中查找是否有这个标识符,如果没有找到,就会去父作用域查找,如果父作用域还没找到继续向上查找,直到全局作用域为止,而作用域链,就是有当前作用域与上层作用域的一系列变量对象组成,它保证了当前执行的作用域对符合访问权限的变量和函数的有序访问。
7、闭包?
参考文章:闭包
-
什么是闭包?
闭包是指有权访问另外一个函数作用域中的变量的函数
闭包是一种特殊的对象,它由两部分组成:执行上下文(代号 A),以及在该执行上下文中创建的函数 (代号 B),当 B 执行时,如果访问了 A 中变量对象的值,那么闭包就会产生,且在 Chrome 中使用这个执行上下文 A 的函数名代指闭包。
-
闭包产生的本质
当前环境中存在指向父级作用域的引用
-
如何产生闭包
- 返回函数
- 函数当做参数传递
-
闭包的应用场景
- 封装变量:用闭包定义能访问私有函数和私有变量的公有函数。
- 模块
-
闭包的优缺点
- 优点:
- 避免全局变量的污染
- 能够读取函数内部的变量
- 可以在内存中维护一个变量
- 缺点:
- 闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
- 优点:
8、了解 this 嘛,bind,call,apply 具体指什么?
浏览器 JavaScript 中的 this 的使用
call、apply、bind三者之间的用法和区别,并手写实现
9、手写 Promise ?
10、js脚本加载问题,async、defer?
-
正常加载模式
这种情况下 JS 会阻塞浏览器,浏览器必须等待前面所有的 js 加载和执行完毕才能去做其它事情。<script src="index.js"></script>
-
async(异步) 模式
async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。<script async src="index.js"></script>
-
defer(延缓) 模式
defer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行。<script defer src="index.js"></script>
从应用的角度来说,一般当我们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,我们会选用 async;
当脚本依赖于 DOM 元素和其它脚本的执行结果时,我们会选用 defer。
11、原型链和原型继承?
-
原型
所有的函数默认都会拥有一个名为prototype
的共有且不可枚举的属性,它会指向另外一个对象,这个对象通常被称为函数的原型。 -
原型链
-
原型链继承