一、javaScript基础
概念题
1. 说一下数据类型(8个)
number, string, boolean, undefined, null, symbol, bigint, object
其中symbol是es6新增的
bigInt是es11新增的
2. 说一下标准内置对象(22个)
太多了,说常用的,全面一点的在这里
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects
NaN | undefined | globalThis | object | arguments | Json |
function | boolean | symbol | error | Proxy | Set |
number | bigInt | Math | Date | Reflect | Promise |
string | EegExp | Array | Map |
3. 说一下Json
3.1 JSON 是一种基于文本的轻量级的数据交换格式。它可以被任何的编程语言读取和作为数据格式来传递。
3.2 键值必须加双引号,值不能是函数,也不能是undefined/NaN
3.3 最简单的深拷贝,可以使用JSON.parse()和JSON.stringify()来嵌套使用
4. 说一下数组的原生方法
concat | fill | filter | find/findLast | findIndex/findLastIndex | forEach |
indexOf/lastIndexOf | isArray | join | map | pop | push |
reduce | reverse | shift | unshift | slice | sort |
splice | toString | values | some | Array.of | every |
5. 说一下DOM和BOM
dom文档对象模型,是一种以平台和语音无关的api,用于处理网页内容
bom浏览器对象模型,是描述对象与对象之间层次关系模型,其中window对象是bom的顶层对象,其他对象都是该对象的子对象,如:location, navigator, screen, document
6. 说一下this
6.1 this在全局作用域下指向的是window
6.2 this的绑定规则有4种
默认绑定
隐式绑定
显式绑定
new绑定
绑定优先级new > 显式 > 隐式 > 默认
https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA
7. 说一下var、let和const
var是函数作用域,let是块作用域
var可以声明提升,let不可以,“因为在解析代码之前,js引擎会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用为声明的变量。在let声明之前的执行瞬间被称为“暂时性死区”(其实let也存在提升,但是不允许访问)
var会成为window对象的属性,let则不会“使用let在全局作用域中声明的变量不会成为window对象属性”
在for循环的中var定义的变量会渗透到循环体外,let则不会
“js引擎会为for循环中let声明分别创建独立的变量实例,而且let和const很相似,不能用const来声明迭代变量(因为迭代变量会自增)”
let和const不存在变量提升
验证变量是否会被提升,声明的变量输出的undefined,不是变量提升(let)输出Reference Error
重复的var声明会被忽略,重复的let声明会抛出syntaxErrorr
重点:执行到let时,其实是提升了,已经被创建出来了,但是不给访问,也就是上面所说的暂时性死区
8. 说一下闭包
对闭包的定义大概就是,其实js每创建一个函数都可以说是闭包
但是大家对闭包的理解是,函数内部访问到了函数外的自由变量,那么就可以称为闭包
9. 说一下回流和重绘
什么是回流?(回流也称为重排)浏览器必须重新修改页面或者部分页面就是回流
例如:删除/新增dom节点,改变css的宽高等,这些都会引起回流
什么是重绘?可以理解改变background-color这些css样式,不影响dom或者布局就是重绘
这里附上一个链接,看浏览器渲染过程的1.3(回流)和1.4(重绘)
https://blog.csdn.net/qq_51657072/article/details/122985137
所以,回流一定会引起重绘,重绘不一定引起回流
10. 说一下宏任务和微任务
这个用语言描述有点抽象,建议看视频
宏任务包含script(整体代码) setTimeout setInterval
微任务包含Promise.then Object.observe MutationObserver
这里从操作系统说起,简单说
①说一下进程和线程的区别?
可以理解为:进程就行相当于每打开一个软件,就是一个进程,因为这个软件在内存中运行(在任务管理器中可查看),有些软件是多进程,例如浏览器,当打开n个网页,那么就是n个进程。
线程就是比进程还小的单位,顺序执行的过程。
②说一下浏览器的事件循环?
(js的事件循环?这里还有node的事件循环不讲)
2.1 在一个进程内执行(一个网站内)js代码,由于js是单线程的
2.2 当遇到网络请求,settimeout等代码,交给当前进程的其他线程执行,当其他线程执行结果返回到事件队列(微任务/宏任务),在执行任何一个宏任务时,都会查看微任务是否为空,不为空的话,先执行微任务在执行宏任务。
2.3 当事件队列执行结束后,会返回给js线程, 那么就这样就形成了一个闭环,就称为事件循环
js线程–>浏览器其他线程–>事件队列–>js线程
11. 说一下Cookie、localstorage、sessionstorage
cookie:cookie存储在客户端,可以和服务器建立连接,生命周期是如果设置了时间,那么就存储在硬盘cookie,默认是关闭浏览器自动删除(这个就是存储在内存上)
webStorage包含localstorage和sessionstorage
localstorage:存储在客户端,生命周期数据永久保存,需要手动清理缓存
sessionstorage:存储在客户端,生命周期是刷新页面还是会保存数据,当关闭浏览器,关闭页面,跳转页面,数据失效
12.说一下垃圾回收
①常用的是“标记清除”
即:当发现这块内存再也没有使用会被标记,然后等到下一次回收的时间,把这块清除
例子:let obj = {},执行一段代码后后,我把obj = null,这个过程就是我在内存中创建一个内存放{},用完这个{}后,我把obj变量赋值null,那么在内存中,就没有指向{},那么这个{}就会被标记上,下一次内存回收就把这个{}清除
②不常用的是“引用计数”
即:当创建一个变量并赋值的时候为1,当这个变量被赋值其他内容就减1,当这个变量变为0时,就清除
13. 说一下高阶函数
要么接收一个函数,要么返回一个函数
像setTimeout就可以说是一个高阶函数
14. 说一下错误类型(8个)
ReferenceError | 引用错误,使用了未声明的变量, 无效引用 |
RangeError | 内置错误, 数值变量或参数超出其有效范围 |
TypeError | 类型错误, 变量或参数不属于有效类型 |
SyntaxError | 语法错误 |
EvalError | 错误原因与 eval() 有关 |
URIError | 给 encodeURI() 或 decodeURI() 传递的参数无效 |
AggregateError | 创建一个 error 实例,其中包裹了由一个操作产生且需要报告的多个错误 |
InternalError | 创建一个代表 Javascript 引擎内部错误的异常抛出的实例。如:递归太多 |
15. 说一下作用域和作用域链
es5之前分为全局作用域和局部作用域
es6之后,var属于函数作用域,let属于块级作用域,块级指的是{}
作用域是指内部定义的变量,外部没有反问权限
作用域链指当前作用域内查找引用,如果没找到,会向上一层接着找,层层向上直到全局作用域,这一过程被称为作用域链
词法分析,语法分析
左查询和右查询(LHS和RHS)
例:let a = 2,a就是左查询,2就右查询
左查询就是赋值后引用的值,即如果a在别的地方用到,其实a就是引用了这个源值为2,a就是左查询(赋值操作的目标是谁就是LHS)
右查询查询就是源值(谁是赋值操作的源头就是RHS)
作用域链就是根据左右查询一步步向上找的
细节: 函数声明会被提升,但是函数表达式却不会被提升,函数会首先被提升,然后才是变量
区别题
1. null和undefined的区别
undefined表示一个变量被声明,被自动赋值
null表示空值,可以对一个对象的引用,可以用来释放一个对象
相同点
都是原始类型,保存在栈中变量本地
不同点
1.1 数据类型不一样,undefined是undefined,null是object
console.log(typeof undefined) // undefined
console.log(typeof null) // object
console.log(null == undefined) // true
console.log(null === undefined) // false
1.2 隐式转换不同
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN
console.log(22+null) // 22
console.log(22+undefined) // NaN
2. typeof和intanceof的区别
typeof 用于判断数据类型,返回值有number、string、boolean、function、undefined、object 六个
intanceof判断对象的实例
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
3. for…of和for…in的区别
3.1 都可以循环数组,for in 输出的是数组的index下标,而for of 输出的是数组的每一项的值
const arr = [1,2,3,4]
for (const key in arr){
console.log(key) // 输出 0,1,2,3
}
for (const key of arr){
console.log(key) // 输出 1,2,3,4
}
3.2 for in 可以遍历对象,for of 不能遍历对象,只能遍历带有iterator接口的,例如Set,Map,String,Array
const object = { name: ‘lx’, age: 23 }
for (const key in object) {
console.log(key) // name,age
console.log(object[key]) // lx,23
}
for (const key of object) {
console.log(key) // 报错 Uncaught TypeError: object is not iterable
}
for in适合遍历对象,for of适合遍历数组
4. forEach()和map()的区别
相同点
都可以传入三参,一参是当前元素,二参是索引,三参是整个数组
不同点
forEach()允许callback更改原始数组的元素,返回是undefined
map()会开辟新的内存空间,返回一个新的数组
5. bind()、call()和apply()的区别
相同点
都是动态修改this指向;都不会修改原先函数的this指向
不同点
5.1 执行方式不同
bind()是异步代码,改变后不会立即执行,而是返回一个新的函数
call()和apply()是同步代码,改变后立即执行
5.2 传参不同
call()和bind()是逐个逐个传入
apply()是传入一个数组
5.3 修改this的性质不同
bind()是永久修改
call()和apply()是临时修改一次,再次调用时还是指向原来的this
6. ==和===的区别
===叫全等,值和类型都是一样
==叫等于,先判断两者类型是否相等,如果不相等就进行转化,如果类型相等就判断值是否相等
7. es6中的class和es5的类的区别
7.1 ES6 class 内部所有定义的方法都是不可枚举的;
7.2 ES6 class 必须使用 new 调用;
7.3 ES6 class 不存在变量提升;
7.4 ES6 class 默认即是严格模式;
7.5 ES6 class 子类必须在构造函数中调用super(),这样才有this对象;ES5中类继承的关系是相反的,先有子类的this,然后用父类的方法应用在this上。
8. prototype和__proto__的区别
__proto__(隐式原型)与prototype(显式原型)
__proto__是每个实例都有的属性,可以访问 [[prototype]] 属性
prototype是函数独有的属性
显式原型的作用: 用来实现基于原型得继承与属性的共享。
隐式原型的作用:构成原型链,同样用于实现基于原型的继承。当我们访问obj这个对象中的属性时,如果在obj中找不到,就沿着_proto_依次查找。
9. 防抖和节流的区别
防抖debounce
在一段时间后,执行一次,若在这段时间内再次调用,则重新计算
如:设置一个按钮,设置200毫秒内执行一次,在这200毫秒如果没有再次点击则执行一次,如果点击了,那么从点击那刻开始再计算200毫秒
节流thorttle
在一定时间内,不论点击多少次,只会一次
如:英雄攻击野怪,平a速度,是一秒一个平a,你在一秒内按了10次,只会发出一个平a
10. 普通函数和箭头函数的区别
①语法不同
②箭头函数是匿名函数,普通函数可以匿名可以具名
③箭头函数没有this,不能构造,this是指向上下文
④不能使用new操作符
⑤没有原型
⑥箭头函数可以使用闭包,递归,三元
⑦箭头函数不能绑定this
11. Promise和async/await的区别
Promise解决了回调地狱问题,但promise的语法形成了一个then()形成了一个回调链,维护起来比较麻烦
而async/await的await在等的也是一个promise,等到了再执行下去,相对于promise的链式,简洁一些
两者都是非阻塞的
async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。
打断点不好调试,如果在一个promise的then()使用调试器的进步,调试器不会进入到后序的then(),因为调试器只能跟着同步代码
12. 深拷贝和浅拷贝的区别
深拷贝就是开辟一块新的内存空间,存放复制过来的对象
const a = {‘aaa’: 1}
const b = JSON.parse(JSON.stringify(a))
b.aaa = 2
console.log(a, b)// {‘aaa’: 1} {‘aaa’: 2}
浅拷贝就是指针还是指向同一块内存空间,就是b对象复制了a对象,然后b对象修改了,a对象也会修改
const a = {‘aaa’: 1}
const b = a
b.aaa = 2
console.log(a, b) // {‘aaa’: 2} {‘aaa’: 2}
其他题
1. new一个箭头函数会怎么样
箭头函数是es6提出的,它没有原型(prototype),没有自己的this指向,更没有arguments,所以不能new一个箭头函数
new操作符实现理论
1.1 创建一个对象
1.2 将对象的__proto__属性指向prototype
1.3 将构造函数的this指向创建的属性
1.4 返回新的对象
2. use strict是什么
use strict是es5新增的严格模式
目的是为了
2.11 消除js语法不合理,不严谨之处,减少怪异行为
2.12 消除代码的不安全之处
2.13 提高编译速度,为未来的新版本js做铺垫
在严格模式下
2.21 禁止使用with语句
2.22 禁止this指向全局对象
2.23 对象不能有重名属性
3. 事件冒泡/事件捕获
事件冒泡
指事件(从里到外),当一个元素接受到事件时,会把它接受的事件层层上传,直到document对象,事件冒泡值传递的事件而不是函数,即click,focus
如下图,当两个div都绑定了点击事件,点击内层的div时,内层的div会执行一次,然后外层的div跟着执行,如果点击外层的div,那么只会执行外层
事件捕获
指事件(从外到内),当外层的元素执行事件时,内层的元素会依次执行
如下图,当两个div都绑定了点击事件,点击外层div时,外层的点击事件执行一次,然后内层的点击事件跟着执行
4. 事件委托/事件代理
事件委托也叫事件代理,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown…)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
例子如图
不用事件委托,那么点击li的时候,先找到父级,再遍历li每个li都绑定了click,然后点击li的时候,才找到目标
委托事件就是在父级里绑定click,然后根据事件冒泡,被点击的那个li(事件源)传递到父级,这样就提高了性能
- 111
- 222
- 333
这文章讲的好
https://www.cnblogs.com/liugang-vip/p/5616484.html
5. 事件循环(EventLoop)
看概念第10题
js线程–>浏览器其他线程–>事件队列–>js线程
6. 回调函数/立即执行函数
回调函数
一句话概括,就是把函数作为参数传递,如图
6.11 不会立即执行
6.12 是一个闭包
https://blog.csdn.net/weixin_47075145/article/details/125752446
function add(num1, num2, callback) {
var sum = num1 + num2;
callback(sum);
}
function print(num) {
console.log(num);
}
add(1, 2, print); //3
立即执行函数
立即执行函数执行完就被销毁,不需要添加函数名
函数该有的东西,立即执行函数都有
(function () {
console.log(1)
})() // 写法一
(function () {
console.log(1)
}()) // 写法二
7. 全函数eval()
会将传入的字符串当做 JavaScript 代码进行执行
永远不要使用 eval!
eval() 是一个危险的函数,它使用与调用者相同的权限执行代码。如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function 就不容易被攻击。
eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。
此外,现代 JavaScript 解释器将 JavaScript 转换为机器代码。这意味着任何变量命名的概念都会被删除。因此,任意一个 eval 的使用都会强制浏览器进行冗长的变量名称查找,以确定变量在机器代码中的位置并设置其值。另外,新内容将会通过 eval() 引进给变量,比如更改该变量的类型,因此会强制浏览器重新执行所有已经生成的机器代码以进行补偿。但是(谢天谢地)存在一个非常好的 eval 替代方法:只需使用 window.Function。这有个例子方便你了解如何将eval()的使用转变为Function()。
8. symbol
用来表示一个独一无二的值
8.1 为什么需要Symbol” />为了避免第三方框架的同名属性被覆盖
8.2 特点
Symbol是基本数据类型,不要加new哦
后面括号可以传入一个字符串,只是一个标记,方便我们阅读,没有任何意义
类型转化的时候不可转化为数值
8.3 应用场景
在企业开发中如果需要对一些第三方的插件、框架进行自定义的时候
可能会因为添加了同名的属性或者方法, 将框架中原有的属性或者方法覆盖掉
为了避免这种情况的发生, 框架的作者或者我们就可以使用Symbol作为属性或者方法的名称
9. js的三大对象
9.1 本地对象 ( native object )
由 es 实现提供并独立于宿主环境的任何对象。
本地对象可以理解为 ECMA-262 定义的类(引用类型)。
这些引用类型在运行过程中需要通过new来创建所需的实例对象。
包含:Object、String、Array、Date、Number、RegExp、Function、Boolean、Error等。
9.2 内置对象 ( built-in object )
由 es 实现提供并独立于宿主环境的,在程序开始执行就出现的对象。
本身就是实例化对象,开发者无需再去实例化。
所有内置对象都是本地对象的子集。包含:Global和Math。es5中增添了JSON这个存在于全局的内置对象。
9.3 宿主对象 ( host object )
由 ECMAScript 实现的宿主环境(如某浏览器)提供的对象(如由浏览器提供的 Window 和 Document),包含两大类,一个是宿主提供,一个是自定义类对象。
所有非本地对象都属于宿主对象。
对于嵌入到网页中的JS来说,其宿主对象就是浏览器提供的对象,浏览器对象有很多,如Window和Document等。包含:DOM 、BOM和自定义对象。
10. 函数柯里化
函数柯里化就是部分求值
只传递给函数一部分参数来调用它,让它返回一个新函数去处理剩下的参数
好处:参数复用,延迟调用
function sum1(a, b, c) {
let res = a + b + c
console.log(res)
}
sum1(1, 2, 3) // 6
// 柯里化
function sum2(a) {
return function(b) {
return function(c) {
return a + b + c
}
}
}
sum2(1)(2)(3)// 6
11. 垃圾回收
这里只说两种
①常用的是“标记清除”
即:当发现这块内存再也没有使用会被标记,然后等到下一次回收的时间,把这块清除
例子:let obj = {},执行一段代码后后,我把obj = null,这个过程就是我在内存中创建一个内存放{},用完这个{}后,我把obj变量赋值null,那么在内存中,就没有指向{},那么这个{}就会被标记上,下一次内存回收就把这个{}清除
②不常用的是“引用计数”
即:当创建一个变量并赋值的时候为1,当这个变量被赋值其他内容就减1,当这个变量变为0时,就清除
12. 说一下原型链
当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。
①使用原型链继承的弊端(常见的)
1.1 打印子函数,继承的属性是看不到的,因为打印的时候,只打印当前的可枚举属性,原型链上面的属性不会打印
1.2 创建a和b对象,在原型链上某个属性是引用类型,修改了a对象在原型链上引用类型某个值,b对象跟着变,说明a和b对象使用的是同一个引用类型
1.3 原型链继承不好实现传参数
解决方案:借用构造函数实现继承
这个借用也有弊端
一是,父类会调用两次
二是,原型对象多出属性,其实是没有必要的
function person () {
this.age = 0
}
function studect() {
this.name = ‘abc’
}
studect.prototype = new person()
const obj = new studect()
console.log(obj.age, obj.name)// 0, abc
二、ECMAScript 6+
es6(2015)
1. 关键字let和const
2. 解构
3. 扩展运算符
4. set()/WeakSet(),Map()/WeakMap()
5. Array.from()浅拷贝数组
6. find()和findIndex()
7. 模板字面量,在es6之前叫模板字符串
8. 箭头函数
9. 规范二进制和八进制写法
10. symbol
11. class类
新增数组方法如map,filter,some,reduce
promise
es7(2016)
1. includes()判断是否包含某个值
2. 幂运算
es8(2017)
1. Object.values
2. Object.entries
3. padStart()padEnd()
4. getOwnPropertyDescriptors()
5. Async/await
es9(2018)
1. promise.prototype.finally()
2. Asynchronous Iteration 异步迭代器
es10(2019)
1. flat()/flatMap()指定一个深度递归数组
2. Object.fromEntries()把键值对列表转换为一个对象
3. trimRight()/trimStart()/trimEnd()删除空格
4. Symbol.prototype.description只读属性
es11(2020)
1. BigInt第七种数据类型
2. 空值合并操作符“??”双问号
3. 可选链,即obj.aaa?.bbb,如果aaa为空那么就停止搜索,不会报bbb的undefined
4. 规范全局this:globalThis
5. Promise.allSettled()
es12(2021)
1. 数字过长可以使用“_”连接符,例:100_000_000_000
2. WeakRef()弱引用
3. replaceAll()字符串替换
新增promise.any()
es13(2022)
class可以在最外层声明类成员,以前是在构造函数内声明
// es13之前
class Abc {
constructor() {
this.color = ‘red’
}
}
const obj = new Abc()
console.log(obj.color) // red
// es13
class Abc {
color = ‘red’
}
const obj = new Abc()
console.log(obj.color) // red
给类添加私有属性和变量操作符,在变量前面加#
支持在外层写await
支持定义静态成员和私有方法
class Person {
static #count = 0;
static getCount() {
return this.#count;
}
constructor() {
this.constructor.#incrementCount();
}
static #incrementCount() {
this.#count++;
}
}
const person1 = new Person();
const person2 = new Person();
console.log(Person.getCount()); // 2
使用in来判断某个对象是否拥有私有属性
使用at操作符来索引元素
// es13之前
const arr = [‘a’, ‘b’, ‘c’, ‘d’];
console.log(arr[arr.length – 1]); // d
// es13
console.log(arr.at(-1)); // d
Object.hasOwn()方法
三、异步
1. ajax、fetch、axios的区别
1.1 ajax
本身针对mvc编程,不符合前端的mvvm,现在有了fetch代替
配置和调用的方法有些混乱,对于事件的异步不太友好
1.2 fetch
可以说是ajax的替代品,在es6诞生的,使用了promise对象,fetch不是ajax的进一步封装,是原生js,没有使用xmlhttprequest对象
相对于ajax优点
语法简洁,基于标准的promise,支持async/await,脱离了xhr
缺点
没办法原生监测请求进度,而xhr可以
fetch不支持abort(拦截),不支持超时控制
1.3 axios
浏览器端发起xmlhttprequests请求
node端发起http请求
支持promise api
监听请求和返回
客户端支持抵御xsrf攻击
2. 说一下promise
promise属于js的一个基本内置对象,它的诞生是为了解决回调地狱的问题,在promise原型上绑定的then/catch方法对promise的使用形成了链式
promise实例三个状态,分别是pending(进行中),resolve(已完成),reject(已拒绝)
promise对象的常用方法9个
方法 | 描述 |
Promise.all() | 等待所有都完成,或者第一个失败就返回,不再执行后面 |
Promise.allSettled() | 不论有没报错,全部返回 |
Promise.any() | 有一个成功就算成功,失败的收集(实验性方法) |
Promise.race() | 有一个失败,就返回失败 |
Promise.reject() | 失败回调 |
Promise.resolve() | 成功回调 |
catch() | 捕抓失败 |
then() | 捕抓成功 |
finally() | 不论成功还是失败,都会执行该方法 |
3. 说一下async/await
async/await是es7提出,es8开始使用的一个新特性,当时的await只能写在async内
在es13(2022),await可以写在函数体外
async是函数前的一个修饰符,如果await不是等promise的话,跟普通函数没什么区别
async/await是为解决promise的链式调用,然代码更加简洁,增加它的可读可维护性
4. async/await的await等的是什么
await等待的是一个表达式,它等的是一个实际的返回值
所以await不仅仅可以用于等promise对象,还可以等任何的表达式结果
await表达式的运行结果取决于它等的是什么
如果等的不是一个promise对象,那么就直接根据表达式结果返回
如果等的是一个promise对象,那么就会阻塞后面的代码,就要等promise对象的resolve,然后返回resolve值作为await的运行结果
后面的有空再更新…
若有收获,就点个赞吧