-
一、JavaScript\
-
✔1.1 数据类型 (9种)\
- 基本数据类型(栈):Number,String,Boolean,null,undefined,symbol(创建唯一值),bigint(比Number数据类型支持的范围更大的整数值)(后两个为ES6新增)\
- 引用数据类型(堆):object,function\
-
✔var、let、const\
-
let 和 const 的优点?\
- let 和 const 有了块级作用域,变量声明不会提升相比于 var\
-
var、let、const的区别\
- var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。\
- let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。(有变量提升,但不赋值)\
- const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。(除非是数组或对象才能修改)\
-
-
✔1.2 普通函数、匿名函数、箭头函数\
- 普通函数:function main(){}\
- 匿名函数:function(){}\
- 箭头函数:()=>{}(一种特殊的匿名函数)\
-
箭头函数与普通函数的区别\
-
箭头函数不能用new来创建构造函数的实例,箭头函数没有构造能力\
- 箭头函数创建的时候不会为这个箭头函数创建[[Construct]]方法\
- 箭头函数是匿名的,用完就扔了\
- 箭头函数内部不会自动生成prototype属性\
- 箭头函数内部没有生成arguments对象\
- 箭头函数不能通过call,apple和bind来改变this的值(可以调用这些方法但this的值不受控制)\
-
this的指向\
- this在普通函数里是动态的,谁调用就指向谁\
- 箭头函数的this值取决于外部普通函数里的this值\
-
-
函数内部有一个特殊的对象:arguments(是一个对象object,是类数组)\
- 获取函数的参数使用\
- 给函数传参时,可以像数组一样调用数组的元素或者属性\
-
总结:\
-
普通函数:\
- 有new和原型\
- 有arguments,super,new.target\
- this指向是动态的\
- call,apply和bind能修改this的值\
-
箭头函数:\
- 没有new和原型\
- 没有arguments,super,new.target,只能调用外围的\
- this指向是被普通函数包含的上一层this\
- call,apply和bind不可修改this的值\
-
-
✔函数柯里化\
- 柯里化技术主要体现在函数里面返回函数\
- 使用柯里化技术的函数可以很好的进行复用,大大减少代码量\
-
柯里化可以进行兼容性的检测\
- 常见的是判断事件监听addEventListener(主流浏览器的方法)和attachEvent(IE的方法)\
-
柯里化还有一个很重要的作用是延迟执行,对参数复用功能进行改进\
-
***参数复用案例:\
- const curring = name => element => element[name]\
- const name_profession = curring('profession')\
- console.log(nameList1.map(name_profession));\
- console.log(nameList2.map(name_profession));\
-
注释:\
-
nameList1.map( name => element => element[name ] )\
-
转化nameList1.map( 'profession'=> element => element['profession'] )\
- 转化nameList1.map( 'profession'=> { nameList1=> nameList1['profession'] } )\
-
-
-
-
✔new操作符具体干了什么呢\
- (1) 创建一个空对象 son {}\
- (2) 为 son 准备原型链连接son.proto = Father.prototype\
- (3) 重新绑定this,使构造函数的this指向新对象Father.call(this)\
- (4) 为新对象属性赋值son.name\
- (5) 返回this return this,此时的新对象就拥有了构造函数的方法和属性了\
-
✔1.3 call, apply(数组), bind\
- call,apply和bind的作用都是改变this的指向\
-
call方法(this指向,...)\
- steven.charge.call(becky, 95)\
- call可以传递多个形参\
-
apply方法(this指向,数组[])\
- steven.charge2.apply(becky, [30, 40])\
- apply只能传递一个数组形参(接收的参数类型是数组)\
-
bind方法(this指向)\
- const beckyCharge = steven.charge.bind(becky)\
- bind不会被立即调用,而是返回一个函数对象,用变量保存,稍后再用(开发中常用)\
- 为了进行this的硬绑定\
- 手写call,apply,bind\
-
✔1.4 eventloop, 宏任务和微任务\
-
eventloop概念:\
- 检查当前调用栈为空的时候,从消息队列头部弹出一个回调放入调用栈执行\
- 在执行过程中遇到微任务和宏任务,分别添加到微队列和宏队列中去\
- 面试题输出判断:同步任务先执行 → 微任务执行 → 宏任务执行 → 宏任务产生的微任务执行...\
-
-
✔1.5 ***闭包 (概念, 用途, 手写)\
-
概念:\
- 闭包:一个函数和它的周围状态的引用捆绑在一起的组合(返回值为函数的函数)\
- 闭包就是通过某种方式获取函数内部的变量,然后暴露到函数外部,并在将来的某个节点还可以继续操作这个变量\
- 闭包 是指内部函数总是可以访问其所在的外部函数中声明的变量和参数,即使其外部函数被return(结束生命周期)之后\
- 闭包就是嵌套函数,内部函数引用外部函数值就能形成闭包,闭包可以延长函数的生命周期\
- 闭包会产生一种幽灵变量,感觉不到变量但变量确实存在着\
- 总结:闭包函数内部的参数在外部不能访问\
-
用途:\
- 闭包可以延长函数的生命周期\
- 优点:可以防止污染全局作用域\
- 常用场景:settimeout\
-
缺点:\
- 可能会导致内存泄漏的问题(低版本IE中)\
- 手写闭包\
-
-
✔1.7 继承 (extends继承, 原型链继承, 构造函数继承, 组合继承, 寄生组合继承)\
-
extends关键字作用:类继承\
- 语法:class 子类函数 extends 父类函数\
- 底层:替换原型继承(替换的是原型的原型,不会覆盖自身原型Child.prototype.proto = Parent.prototype)\
- 原型链继承实现了共享\
- 构造函数继承不能实现共享\
- 组合继承既可以实现共享,又可以解决原型链继承的一些问题\
-
-
✔原型链\
-
概述:\
- 如果const stu1=new Student('张三')\
- 则Student.prototype === stu1.proto\
-
每个函数function都有一个prototype,即显式原型(属性)\
- 在定义函数时自动添加的, 默认值是一个空Object对象\
-
每个实例对象都有一个__proto__,可称为隐式原型(属性)\
- 创建对象时自动添加的, 默认值为构造函数的prototype属性值\
-
为什么会有原型链?\
- 1.为了实现继承,简化代码,实现代码重用\
- 2.只要是这个链条上的内容,都可以被访问和使用到\
- 原型链是怎么体现的?\
-
-
✔1.8深拷贝和浅拷贝(数组里的concat方法、slice方法、Array.from方法、展开运算符)\
- 深拷贝:对复制的数组操作不会影响原数据\
- 浅拷贝:对复制的数组操作会影响原数据\
- 实现浅拷贝的方法:const listCope = Object.assign({}, list),展开运算符\
-
属于浅拷贝【对于原始值是深拷贝,对于引用值是浅拷贝(对于数组是深拷贝,对于对象是浅拷贝)】\
-
concat方法\
- const listCope = [].concat(xxx)\
- 会创建一个拷贝当前数组的副本,然后把参数添加到副本末尾\
- 如果不添加参数,就相当于直接拷贝副本,返回新构建的数组\
- 对复制的数组操作不会影响原数组\
-
slice方法(与concat方法对应)\
- const listCope = list.slice()\
- 如果不传入参数,返回一个新的数组,并包含所有元素\
- 进行元素的添加不会影响原数组\
-
Array.from方法\
- const listCope = Array.from(list)\
- 进行元素的添加不会影响原数组,实现了拷贝\
-
展开运算符\
- const listCope = [...list]\
-
-
实现深拷贝\
-
使用JSON的方法:\
- const listCope = JSON.parse(JSON.stringify(list))\
- 先使用JSON.stringify方法,把JS对象变成JSON字符串\
- 在使用JSON.parse方法,把JSON字符串生成为JS对象\
- 数组还有其他方法,map,filter,reduce会造成浅拷贝,可以用JSON解决\
-
-
✔1.9 ***Promise\
- Promise对象可以将异步操作以同步的流程表达出来\
-
为什么要用promise(好处)\
- 支持链式调用, 可以解决回调地狱问题\
- 指定回调函数的方式更加灵活\
- 语法非常简洁/Promise 对象提供了简洁的API,使得异步操作更加容易\
-
关键问题\
- 如何修改对象的状态\
- 能否执行多个回调\
- 改变状态与指定回调的顺序问题\
- then方法返回结果由什么决定\
- 串联多个任务\
- 异常穿透\
- 如何中断promise链\
-
promise的方法\
- Promise.all()\
- Promise.race()\
- Promise.allSettled()\
- Promise.any()\
- 手写promise的all方法、race方法\
- promise状态 PromiseState(pending 未决定的、resolved 成功、rejected 失败)\
-
✔async异步 与 await等待 与 defer推迟\
- 构造函数时,使用async和await实现异步任务\
-
script标签的defer和async的区别\
- 遇到script标签,就会停下来先执行script标签的内容,如果有外部文件,就必须等外部文件下载执行\
- async适合第三方脚本\
- defer适合于DOM有关联的脚本\
-
✔Ajax请求过程\
- AJAX是异步 JS 和 XML\
- Ajax可以使网页实现异步更新。在不重新加载整个网页的情况下,对网页的某部分进行更新\
- (1)创建 XMLHttpRequest 对象,也就是创建一个异步调用对象;(1.创建对象)\
- (2)创建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、 URL 以及验证信息;(2.初始化)\
- (3)设置响应 HTTP 请求状态变化的函数;\
- (4)发送 HTTP 请求;(3.发送)\
- (5)获取异步调用返回的数据;(4.处理响应结果)\
- (6)使用 JavaScript 和 DOM 实现局部刷新。\
- 手写Ajax\
-
✔Axios工具库\
- 基于 Promise 操作方式的一个AJAX的封装的包\
-
✔AMD、CMD、CommonJs、ES6的对比\
- 他们都是用于在模块化定义中使用的,AMD、CMD、CommonJs是ES5中提供的模块化编程的方案,import/export是ES6中定义新增的\
-
✔require,import和import()函数的区别\
- require是运行时加载模块,只有运行时才知道,同步动态加载\
- import命令会被 JavaScript 引擎静态编译,先于模块内的其他模块执行(叫做”连接“更合适)。\
- import()函数,完成动态加载,返回值是一个Promise类型的对象。异步动态加载\
-
不同之处:\
- 1.遵循的规范不同(import是es6的)\
- 2.调用时间不同(require运行时,import编译时)\
- 3.本质(require是赋值拷贝,import是解构引用)\
-
✔forEach方法和map方法\
-
共同点:\
- 1.都只能循环数组,都循环数组的每一项\
- 2.每次循环的匿名函数都有三个参数(item当前项,index索引,input原始数组)\
- 3.匿名函数中的this都指向window\
-
区别:\
- forEach没有返回值,map有返回值\
- forEach改变原数组,map原数组不变\
- forEach无法跳出循环(return),map返回新数组(当数组中元素是基本类型,map不会改变原数组;当是引用类型object/function,则可以改变原数组)\
-
用途:\
- forEach方法:不改变数据,只是用数据做事情,它return出来接收到是undefined\
- map方法:你需要返回新数组,可以正常return\
-
-
✔数组内置的方法\
- 1.push()从后面添加元素\
- 2.pop()从后面删除元素\
- 3.shift()从前面删除元素\
- 4.unshift()从前面添加元素\
- 5.【splice(i索引,n个数)】删除从i索引开始的之后的n个数据\
- 6.【arr1.concat(arr2)】连接两个数组,返回新数组\
- 7.【str.split()】把字符串转化为数组\
- 8.【forEach(callback)】遍历,无return\
- 9.【map(callback)】映射,有return返回新数组\
- 10.【indexOf()】查找某个元素的索引\
- 11.【find(callback)】找到第一个符合条件的数组成员\
- 【reverse()】反转数组\
- 【reduce()】累加器,返回的是一个单值,该单值是由每一项计算出来的\
-
【sort()】按照Unicode编码排序\
- 当数组中的元素为字符串时(arr1.sort())\
-
将数组中的数字按照从小到大排序,会导致[1,11,2,3,4]\
- 升序arr1.sort((a, b) => a - b)\
- 降序arr1.sort((a, b) => b - a)\
-
✔ES6新增\
- 概述:let/const、模板字符串、箭头函数、解构赋值{}、展开运算符...、字符串方法、proxy、Promise、Async和Await、Class、ES Modules\
-
***Proxy(代理:对象级别的拦截)\
-
通过Vue2 和 Vue3 对比\
- ES5中有一个 Object.defineProperty ,可以监视属性的读写过程,Vue2 就是通过这个实现数据双向绑定\
- ES6提供了 Proxy,可以监视对象的读写过程,Vue3.0 通过 Proxy 实现数据绑定\
- Vue2的响应式核心是通过defineProperty写的,Vue3的响应式核心是通过Proxy来写的(性能提升)\
-
Proxy 对比 Object.defineProperty(属性级别的拦截)\
- Proxy 能监视更多对象操作:delete\
- 使用Object.defineProperty定义getter和setter方法(需要循环进行监听的绑定)\
- ***Proxy当中不仅能使用get/set方法,也能使用apply方法,可以操作数组(例如封装负数组索引)\
-
Proxy 能解决的问题\
- 在原有函数上进行修改是很危险的操作,可能破环代码\
- 1.通过Proxy代理可以在不修改原有函数的情况下,实现附加功能\
- 2.可以使用Proxy代理实现负数组索引(array[-5]从后往前5位)\
- 缺点:兼容性差\
- 优点:性能比defineProperty高很多\
-
-
for-of循环(解决for-in的缺陷)\
-
for (const item of arr) { console.log(item); }\
- for-of 是新的遍历方法,规避了for-in循环缺陷,也可以正确响应 break、continue 和 return 语句\
- for-of 循环不仅支持数组,字符串遍历,还支持大多数类数组对\
- 实现Iterable结构就是for...of的前提\
-
for (const item in arr) { console.log(item); }\
- 遍历对象的属性\
-
两者差别:\
- for in遍历的是数组的索引(键名),而for of遍历的是数组的值(键值)\
- 所以for in更适合遍历对象,不适合for in数组\
-
-
Set、Map\
-
Set\
- Set是一系列无序、没有重复值的数据集合(类似没有重复成员的数组)\
- 重复元素会被忽略\
-
Set的属性和方法\
- size:获取元素数量\
- add(value):添加元素,返回 Set 实例本身\
- delete(value):删除元素,返回一个布尔值,表示删除是否成功\
- has(value):返回一个布尔值,表示该值是否是 Set 实例的元素\
- clear():清除所有元素,没有返回值\
-
Map\
-
Map的属性和方法\
- size:获取成员的数量\
- set:设置成员 key 和 value\
- get:获取成员属性值\
- has:判断成员是否存在\
- delete:删除成员\
- clear:清空所有\
-
-
-
Reflect\
- Reflect属于静态类(如 Math ),不能 new,只能调用静态方法:Reflect.get()\
- Reflect 成员方法就是 Proxy 处理对象的默认实现\
-
展开运算符(...)\
- console.log(...arr); // foo bar baz\
- console.log([...arr]); // 浅拷贝 ['foo', 'bar', 'baz']\
- console.log({ ...arr }); // {0: 'foo', 1: 'bar', 2: 'baz'}\
-
字符串方法\
- includes()返回布尔值,表示是否找到了参数字符串\
- startsWith()返回布尔值,表示参数字符串是否在原字符串的头部\
- endsWith()返回布尔值,表示参数字符串是否在原字符串的尾部\
- Promise\
- Async、Await\
-
Class\
- 新的语法形式class Name {...}\
-
ES Modules\
- import { fn1, fn2 } from './util2.js'\
-
Symbol\
- 主要作用就是为对象添加独一无二的属性名\
-
BigInt\
- 新的基础数据类型,表示大的整数(早期JS不能正确表示过大的数字)\
-
✔this\
- 构造函数内部this指向构造函数本身,可以直接this拿\
- 绑定规则:默认绑定、隐式绑定、硬绑定、构造函数绑定\
-
this的指向\
- this在普通函数里是动态的,谁调用就指向谁\
- 箭头函数的this值取决于外部普通函数里的this值\
-
✔防抖和节流\
- 防抖:一直触发事件不会执行,停止触发一段时间后执行\
- 节流:判断触发的事件是否在时间间隔内,如果在就不触发事件,否则触发\
-
总结:\
- 两者本质上都是控制事件执行的频率,但是触发事件的时机本质上有区别\
- 防抖是在用户多次触发事件,当用户停止触发事件,将事件执行一次\
- 节流是用户多次触发事件,会在多次触发的过程中,间隔执行事件\
- 手写防抖和节流\
-