使用过哪些ES6语法 let、const、箭头函数、计算属性名、拓展运算符、解构赋值、export、import、export default、Promise、Reflect
1 Promise 表示异步操作最终完成或失败,及其结果 成功调用resolve、失败reject 在Permise声明后可以使用.then进行代码处理。 第一个函数表示成功,第二个函数表示失败。 因为.then和.catch都是返回promise,所以可以被链式调用
promise.all 当所有成功是才调用触发成功,否则触发失败
promise.allSettled 当所有premise完成后,返回一个对象,对象里面有每个的结果,无论成功还是失败
promise.any 接受一个promise集合,当其中一个成功,就返回那个成功的promise值
promise.race 其中一个成功或失败,则返回该成功或失败的值
1.1 实现简易Promise 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Promise2 { queue1 = [] queue2 = [] constructor (fn ) { const resolve = data => { setTimeout (() => { for (let i = 0 ; i < this .queue1 .length ; i++) { this .queue1 [i](data) } }) } const reject = reason => { setTimeout (() => { for (let i = 0 ; i < this .queue2 .length ; i++) { this .queue2 [i](reason) } }) } fn (resolve, reject) } then (s, e ) { this .queue1 .push (s) this .queue2 .push (e) return this } } p1 = new Promise2 ((resolve, reject ) => { if (Math .random () > 0.5 ) { resolve ('成功' ) } else { reject ('失败' ) } }) p1.then (res =>console .log (res), err =>console .log (err))
1.2 实现 Promise.allSettled 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 const p1 = new Promise ((resolve, reject )=> { setTimeout (()=> { resolve ("成功了" ) },2000 ) }) const p2 = new Promise ((resolve, reject )=> { setTimeout (()=> { reject ("失败了" ) },3000 ) }) const myAllSettled = (list )=>{ const resArr = new Array (list.length ) let num = 0 return new Promise ((resolve, reject )=> { list.forEach ((item, key )=> { let obj = {} item.then (res => { obj['status' ] = 'fulfilled' obj['value' ] = res resArr[key] = obj num++ if (num===list.length )resolve (resArr) },err => { obj['status' ] = 'rejected' obj['reason' ] = err resArr[key] = obj num++ if (num===list.length )resolve (resArr) }) }) }) } myAllSettled ([p1,p2]).then (res => { console .log (res) })
2 Reflect
Reflect.has(对象名, 属性名) 查看指定对象是否含有该属性
Reflect.ownKeys(对象名) 列出该对象所有属性
Reflect.set(对象名, 属性名, 值) 设置该对象的属性和值
3. Proxy TODO: 补充完整
防抖和节流 1 节流 相当于有冷却时间,冷却时间到了才允许执行下一次操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function throttle (fn, delay ){ let useCan = true return function ( ){ if (useCan){ fn.apply (this , arguments ) useCan=false setTimeout (()=> useCan=true , delay) } } }
2 防抖 如果短时间执行多次,那么我们带着上一个内容一起执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function debounce (fn, delay ){ let timerId = null return function ( ){ const context = this if (timerId){window .clearTimeout (timerId)} timerId = setTimeout (()=> { fn.apply (context, arguments ) timerId = null },delay) } }
手写AJAX 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var request = new XMLHttpRequest ()request.open ('GET' , '/a/b/c?name=layouwen' , true ) request.onreadystatechange = function ( ){ if (request.readyState === 4 && request.status === 200 ) { console .log (request.responseText ) } } request.send () var request = new XMLHttpRequest ()request.open ('GET' ,'a/b/c?name=layouwen' , true ) request.onload = ()=> console .log (request.responseText ) request.send ()
this指向问题
fn() —— windows
obj.fn() —— obj
fn.call(xxx) —— xxx
fn.apply(xxx) —— xxx
fn.bind(xxx) —— xxx
new Fn() —— new出来后的对象
fn = ()=>{} —— 箭头函数外的一层
什么是闭包,它有什么作用 外层函数调用后,外层作用域对象被内层函数的作用域引用,无法释放。 作用: 隐藏变量,私有变量 闭包可以使其不会被垃圾回收。 清除闭包: 将引用着作用域链的变量设置为 null
什么是立即执行函数 声明一个匿名函数,接着马上执行它
1 2 3 4 5 6 7 8 ( function ( ){console .log (1 )} )() ( function ( ){console .log (2 )}() ) !function ( ){console .log (3 )}() +function ( ){console .log (4 )}() -function ( ){console .log (5 )}() ~function ( ){console .log (6 )}() void function ( ){console .log (7 )}()new function ( ){console .log (8 )}()
作用: 创建一个局部的作用域,外面访问不到防止变量污染
async/await async 本质是 Promise 语法糖 await 会暂停 async 函数 await 后面的代码会进入微任务队列
深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 function deepClone (obj = {} ) { if (typeof obj !== 'object' || obj === null ) return obj let result if (obj instanceof Array ) result = [] else result = {} for (let key in obj) { if (obj.hasOwnProperty (key)) { result[key] = deepClone (obj[key]) } } return result }
使用正则实现trim方法 1 2 3 function trim (str ){ return str.replace (/^\s+|\s+$/g , '' ) }
继承 1 原型方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function People (name, age ){ this .name = name this .age = age } People .prototype .move = function ( ){ console .log ("我动了" ) } function User (sex, name, age ){ People .call (this , name, age) this .sex = sex } function temp ( ){}temp.prototype = People .prototype User .prototype = new temp ()User .prototype .say = function ( ){ console .log ('我说话了' ) }
2 class方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class People { constructor (name, age ){ this .name = name this .age = age } say ( ){ console .log (this .name + '今年' + this .age + '岁了' ) } } class User extends People { constructor (height, name, age ){ super (name, age) this .height = height } jump ( ){ console .log (this .name + "跳了一下" ) } }
数组去重 1 计数排序变形 2 set去重 1 2 3 4 5 function unique (arr ){ return Array .from (new Set (arr)) } [...new Set (arr)]
3 for循环使用splice去重 1 2 3 4 5 6 7 8 9 10 11 function unique (arr ){ for (let i=0 ; i < arr.length ; i++) { for (let j = i + 1 ; j < arr.length ; j++) { if (arr[i] === arr[j]) { arr.splice (j, 1 ) j-- } } } return arr }
4 Map 1 2 3 4 function unique (arr ) { const map = new Map () return arr.filter (value => !map.has (value) && map.set (value)) }
原型与原型链的理解 每个实例都有其__proto__,他对应其构造函数的prototype。 在js中,没有java中类的说法。js为了模拟new使用了原型来实现。 实例对象的__proto__ === 其构造函数的.prototype 每个实例出来的对象__proto__中的constructor都指向它的构造函数
1 2 const demo1 = new Demo1 ()demo1.__proto__ === Demo1 .prototype
1 demo1.__proto__ .constructor === Demo1
1 proto 与 prototype 的区别
__proto__为隐式原型,prototype为显示原型。
每个对象都有他的__proto__指向其构造函数的原型对象。
每个方法除了有__proto__外,还会有prototype指向其方法的原型对象。
2 公式 xxx的原型===其构造函数的prototype
1 对象.__proto__ === 其构造函数.prototype
所有构造函数都是由Function构造的
1 构造函数.__proto__ === Function .prototype
Object.prototype原型是null
1 Object .prototype .__proto__ === null
如何实现类 方法一:构造函数法 1 2 3 4 5 6 function Cat ( ) { this .name = '小猫' } let cat1 = new Cat ()console .log (cat1.name )
类的属性和方法,可以定义在构造函数的prototype对象上
1 2 3 Cat .prototype .say = function ( ){ console .log ('喵喵' ) }
方法二:Object.create()法 1 2 3 4 5 6 7 8 9 10 let Cat = { name : '小猫' , say ( ) { alert ('喵喵' ) } } let cat1 = Object .create (Cat )console .log (cat1.name )cat1.say ()
方法三:极简主义法 1 2 3 4 5 6 7 8 9 10 let Animal = { createNew ( ){ let animal = {} animal.sleep = ()=> alert ('我是动物,我要睡觉' ) return animal } } let animal = Animal .createNew ()animal.sleep ()
继承的话可以直接在内部调用前者的createNew即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let Cat = { createNew ( ){ let cat = Animal .createNew () cat.name = '小猫咪' cat.age = '20岁' cat.say = ()=> alert ('我是猫咪' ) return cat } } let cat = Cat .createNew ()console .log (cat.name , cat.age )cat.say () cat.sleep ()
私有属性,只要将变量不挂载的对象上即可
1 2 3 4 5 6 7 8 9 10 11 12 13 let Cat = { createNew ( ){ let cat = {} let realName = '猫神' cat.name = '小猫' cat.sayRealName =()=> alert (realName) return cat } } let cat1 = Cat .createNew ()console .log (cat1.name )cat1.sayRealName ()
数据共享,只需要把变量声明在createNew外面即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let Cat = { shareData : '我被共享了' , createNew ( ){ let cat = {} cat.showShare =()=> alert (Cat .shareData ) cat.changeShare =()=> Cat .shareData ='共享数据被更改了' return cat } } let cat1 = Cat .createNew ()let cat2 = Cat .createNew ()cat1.showShare () cat2.showShare () cat1.changeShare () cat2.showShare () console .log (Cat .shareData )
Js 基本类型和复杂类型 1 区别
内存分配不同
基本数据类型存储在栈中
复杂数据类型存储在堆中,栈中存储的变量指向堆中的引用地址
访问机制不同
赋值时不同
基本数据类型:a=b是将b的值给a,a和b完全独立
复杂数据类型:a=b将b保存的内存引用地址赋值给新的变量,a和b的地址是一样的,互相影响
传参不同
基本数据类型:把对应的值传递
复杂数据类型:把内存引用地址传递
2 基本数据类型
string
number
bigint
boolean
symbol
undefined
null
3 复杂数据类型
Object
Array
Regexp
Data
Math
Function
数组方法
pop() 弹出最后一个值,并返回出来
push() 在最后面添加一个或多个值
shift() 弹出第一个值,并返回出来
unshift() 在最前面添加一个或多个值
1 改变原数组 pop、push、reverse、shift、sort、splice、unshift
2 不改变原数组 concat、join、slice
17、bind/call/apply的区别
首先他们都是用来改变函数的this上下文。
bind是返回改变后的函数,call和apply是改变上下文后执行该函数。
bind和call第一个参数为需要改变上下文的对象,从第二个开始以参数列表显示。
apply第一个参数同样是改变上下文的对象,第二个是一个参数数组
简单实现一个call 1 2 3 4 5 6 7 8 Function .prototype .myCall = function (context, ...args ) { if (typeof this !== 'function' ) { console .error ('not function' ) } context = context || window context.__fn = this return context.__fn (...args) }
简单实现一个bind 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Function .prototype .bind2 = function (obj, ...args ) { const that = this return function (...back ) { that.call (obj, ...args, ...back) } } const a = { test : 2 } function b ( ) { console .log (arguments ) console .log (this === a) } b.bind2 (a, 1 , 2 , 3 , 4 )()
同步和异步 同步和异步是一种消息通知机制
同步阻塞:需要等待上一个代码执行完毕,下一个代码才可执行
异步非阻塞:在上一个代码执行后,不需要等待其返回结果。可以继续往后执行,回头在获得该结果。
回调的问题 回调就是函数指针的调用,可以简单理解为一个回头调用的函数。
回调容易造成回调地狱,造成代码维护性差。可以使用观察者模式、promise、async/await来处理。
如何中断 ajax 请求 使用 XMLHttpRequest 对象的 abort 方法中断ajax请求。
不能阻止向服务器发送请求,只能停止当前的 ajax 请求
1 2 3 4 5 6 7 8 9 10 11 12 13 const xhr = new XMLHttpRequest ()postBtn.onclick = ()=> { xhr.open ('get' , '/api' , true ) xhr.onload = ()=> { console .log (xhr.requestText ) } xhr.send () } stopBtn.onclick = ()=> { console .log ('停止请求' ) xhr.abort () }
模块化 CommonJs、AMD、CMD、ESM
CommonJS 是执行时编译
ESM 是静态编译
TODO: 重点补充
ESM
CommonJS
Tree-Shaking
Vite
1 好处
避免命名冲突(减少命名空间污染)
更好的分离, 按需加载
更高复用性
高可维护性
简单实现EventHub 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 let fnLists = {}let eventHub = { on (eventName, fn ) { if (!fnLists[eventName]) { fnLists[eventName] = [] } fnLists[eventName].push (fn) }, trigger (eventName, data ) { let fnList = fnLists[eventName] if (!fnList) return for (let i = 0 ; i < fnList.length ; i++) { fnList[i](data) } } }
23、适配服务端和浏览器 1 2 3 4 5 6 7 8 9 function getDefaultAdapter ( ) { let adapter if (typeof XMLHttpRequest !== 'undefined' ) { adapter = '浏览器' }else if (typeof process !== 'undefined' && Object .prototype .toString .call (process) === '[object process]' ) { adapter = '服务端' } return adapter }
24、那些操作会造成内存泄露
闭包
意外的全局变量
定时器 setTimeout setInterval
1 2 clearTimeout ()clearInterval ()
给 DOM 对象中添加属性是引用类型,如果 DOM 不销毁,则一直存在
1 2 3 window .onunload = function ( ){ 属性 = null }
25、如何延迟执行代码
script 标签添加 defer 属性,告诉浏览器立刻下载,但是延迟执行
1 <script src ='main.js' defer ="defer" > </script >
等执行性到