数据数据类型七种基本数据类型,一种复杂数据类型:number:+-(2^53-1) 范围内的数字string:字符串boolean:true 和 falsenull:未知的值undefined:未定义的值bigint:任意长度的整数symbol:唯一标识符object:复杂的数据结构数据存储:栈:基本类型和引用类型地址堆:引用类型地址实际指向的数据类型检查typeof 运算符:typeof (()=>{}) 会返回 "function"typeof [] 会返回 "object"typeof null 会返回 "object",但实际上它并不是一个对象instanceof 运算符:支持继承的对象检测方法Object.prototype.toString.call([]):Object.prototype.toString.call([]) === '[object Array]'Object.prototype.toString.call(()=>{}) === '[object Function]'类型转换window.isNaN('abc'):trueNumber.isNaN('abc'):此方法不进行类型转换,因此为 false[0] == true:数组调用 toString 再变为数字 0,因此为 false执行上下文和变量提升区别 let 和 var:let 拥有块级作用域let 声明的全局变量不是全局对象的属性let 用于循环中可以正常创建副本let 不允许重定义执行上下文可以理解为当前代码的执行环境,它会形成一个作用域:全局环境函数环境eval变量提升被认为是对执行上下文工作方式的一种认识,例如:function test() { console.log(foo); console.log(bar); var foo = 'Hello'; console.log(foo); var bar = function () { return 'world'; }; function foo() { return 'hello'; } } test(); 等价于:function test() { // 函数声明提升,且优先于变量声明 function foo() { return 'hello'; } // 变量声明提升,但 foo 已经存在,忽略 var 定义 // var foo = undefined; // 变量声明提升,函数表达式和其他变量赋值一致不提升 var bar = undefined; // 执行阶段 console.log(foo); // [Function] console.log(bar); // undefined foo = 'Hello'; console.log(foo); // Hello bar = function () { return 'world'; }; } test(); 闭包闭包简单来说就是函数中的函数;闭包可以通过一个函数去访问原本在外层无法直接访问到的数据,并且保证数据不被回收。暂时性死区和直接使用 var 定义不同,由于没有变量提升,let、const 定义的变量在声明之前使用会报错。Event Loop 事件循环首先将事件分为宏任务与微任务:宏任务:主线 JS 代码、事件、setTimeout 和 setInterval 等微任务:process.nextTick 和 Promise 回调等事件循环流程:首先执行主线同步任务当遇到异步任务时将任务搁置一边独立执行,当异步任务有了结果将其放入对应的异步任务队列主线同步任务执行完毕,检查微任务队列是否有内容,若有则一直执行至清空进入下一轮循环,检查宏任务队列是否有内容,若有则一直执行至清空继续执行主线同步任务代码例:console.log('Start'); const timer1 = setTimeout(() => { console.log('Timer 1'); }, 0); const promise1 = new Promise((resolve, reject) => { console.log('Promise 1'); resolve('Promise 1 Fulfilled'); }); promise1 .then((val) => { console.log(val); return 'Promise 1 Then Fulfilled'; }) .then((val) => { console.log(val); const timer3 = setTimeout(() => { console.log('Timer 3 in Promise 1'); }); }); const promise2 = new Promise((resolve, reject) => { const timer2 = setTimeout(() => { console.log('Timer 2 in Promise 2'); resolve(); }); }); promise2 .then(() => { console.log('Promise 2 Then 1'); }) .then(() => { console.log('Promise 2 Then 2'); }); console.log('End'); /* 执行结果 * Start * Promise 1 * End * Promise 1 Fulfilled * Promise 1 Then Fulfilled * Timer 1 * Timer 2 in Promise 2 * Promise 2 Then 1 * Promise 2 Then 2 * Timer 3 in Promise 1 */ 解析:首先执行主线同步任务,输出 Start、Promise 1 和 End此时宏任务队列中有 timer1 和 timer2;微任务队列中有 promise1 的第一个 then检查微任务队列,输出 Promise 1 Fulfilled 和 Promise 1 Then Fulfilled此时宏任务队列中有 timer1、timer2 和 timer3;微任务队列中为空下一轮循环,输出 Timer 1;微任务队列仍为空下一轮循环,输出 Timer 2 in Promise 2;微任务队列中加入 promise2 的第一个 then优先微任务队列,输出 Promise 2 Then 1 和 Promise 2 Then 2下一轮循环,输出 Timer 3 in Promise 1注意:在 Promise 同步构造函数中,在没有返回值的情况下,resolve() 后的代码依旧会被执行,只是无法再使用 reject() 改变该 Promise 的状态。Map 与 WeakMapWeakMap 的 key 只接受使用对象WeakMap 不支持迭代,keys() 等方法无法使用在 Map 中,若将某对象设置为 key,则后期将对该对象的直接引用置为 null,GC 不会回收,通过 Map.keys() 可以找到该对象;而在 WeakMap 中,该对象会被回收,但无法保证 GC 何时工作。遍历和迭代for...in...:遍历对象及其原型链上可枚举属性遍历数组元素及自定义属性返回 string 类型的 keyObject.keys():遍历对象本身的可枚举属性遍历数组元素及自定义属性返回 string 类型的 keyfor...of...:遍历任何可迭代对象 (普通对象不支持)返回 any 类型的 valuePromisePromise.all():所有成功则成功;任一失败则失败Promise.any():任一成功则成功;所有失败则失败Promise.race():任一成功则成功;任一失败则失败Promise.allSettled():所有都成功或失败最后返回结果This 指向绑定对于 this 指向在函数执行时才能确定,创建时无法确定。对于一般函数,this 指向调用者:var x = 1; var obj = { x: 2, say: function () { console.log(this.x); }, }; obj.say(); // 2 (obj.x) var x = 1; function Obj() { this.x = 2; const say = function () { console.log(this.x); }; say(); } const obj = new Obj(); // 1 (window.x) 对于箭头函数,this 指向其父级执行上下文:var x = 1; var obj = { x: 2, say: () => { console.log(this.x); }, }; obj.say(); // 1 (window.x) var x = 1; function Obj() { this.x = 2; const say = () => { console.log(this.x); }; say(); } const obj = new Obj(); // 2 (obj.x) call、apply、bind 都用于绑定 this 指向:call:第一参数为 this 指向,剩余参数为参数列表,临时改变 this 并立即执行apply:第一参数为 this 指向,第二参数为参数数组,临时改变 this 并立即执行bind:第一参数为 this 指向,剩余参数为参数列表,返回 this 指向确定的函数,同时在调用返回的函数时还可以添加剩余参数继承与原型链每个实例对象都有一个私有属性 __proto__ 指向它的原型对象,该原型对象也有原型,一直到 null 为止。__proto__:常见的浏览器原型链实现getPrototypeOf() 获取 [[Prototype]]:等价于 __proto__prototype 函数才有,指向原型对象;原型对象也有 constructor 属性与之对应function Dog [[Prototype]] prototype ==> <== constructor __proto__ ==> Object.prototype dog1 ^^ __proto__ ==> null __proto__ ===========|| 模拟实现 newfunction genInstance(Constructor, ...args) { // const obj = {}; // obj.__proto__ = Constructor.prototype; const obj = Object.create(Constructor.prototype); const ret = Constructor.call(obj, ...args); if (ret && typeof ret === 'object') { return ret; } return obj; } 原型链继承function Parent() { this.names = []; } Parent.prototype.getNames = function () { console.log(this.names); }; function Child() {} Child.prototype = new Parent(); const child1 = new Child(); const child2 = new Child(); child1.names.push('1'); console.log(child2.getName()); // ['1'] 引用类型的属性会被所有子实例共享无法向 Parent 传参组合继承引入经典继承 (借用构造函数):function Parent() { this.names = []; } Parent.prototype.getNames = function () { console.log(this.names); }; function Child() { Parent.call(this, arguments); // 借用构造函数 } Child.prototype = new Parent(); Child.prototype.constructor = Child; const child1 = new Child(); const child2 = new Child(); child1.names.push('1'); console.log(child2.getName()); // [] 调用了两次父构造函数,导致 Child.prototype 和 child1 中有重复的 names寄生组合继承引入寄生式继承 (使用父原型而不是父实例作为 Child.prototype):function Parent() { this.names = []; } Parent.prototype.getNames = function () { console.log(this.names); }; function Child() { Parent.call(this, arguments); // 借用构造函数 } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; DOM APIElement 与 NodeNode 是一个基类,DOM 中的 Element、Text 和 Comment 都继承于它Node 包含了其内部的 Element 结点,除此之外还有直接插入的文本,注释等内容NodeList 和 ElementCollcetion 都不是真正的数组元素相关属性clientHeight:content + paddingoffsetHeight:content + padding + border + scrollbarscrollHeight:滚动部分总高度,包括当前不可见部分scrollTop:滚动部分顶端距离可见部分顶端的高度offsetTop:当前元素顶部距离最近父元素顶部的距离事件、冒泡和捕获事件传播的三个阶段:捕获阶段目标阶段冒泡阶段当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序;几乎所有事件都会冒泡,但也有例如 focus 的事件不会冒泡。event.target:是引发事件的目标元素,冒泡过程中不会发生变化this:当前元素,其中有一个当前正在运行的处理程序ESM 与 CommonJS 模块加载与解析CJS 模块同步加载,输出的是值的拷贝;对于基本类型,一旦输出,模块内部的变化影响不到这个值;对于引用类型,效果同引用类型的赋值操作;通过导出 getter 函数可以获取模块内部的变化;ES 模块是动态引用,并且不会缓存值。由于 CJS 模块是同步的,因此可以放在代码段的任何位置;而 ESM 只是静态定义,在代码解析阶段就会被执行:import 会被提升到头部执行,export 与 var 定义的变量提升有类似的效果。两种模块都不会重复执行。循环依赖CJS 模块当遇到 require() 语句时,会执行模块中的代码,得到的是已经执行部分的结果。ES6+ 新要素let 和 const扩展运算符箭头函数classMap 和 Set箭头函数特性没有自己的 this,内部 this 指向父级执行上下文无法作为构造函数,没有 prototype 属性无法使用 arguments,需要 rest 参数无法作为 generator 函数使用ES 标准流程Stage 0:开放提交,提议、想法Stage 1:正式建议,初步 demo 和标准Stage 2:草案,标准语言解释和实验性实现Stage 3:接近完成,等待测试、审核和用户反馈Stage 4:确认会被包含到将来的标准中