JS 【精华速查版】2024最新版

JS 的特点

  • 单线程
    • JS 和 DOM 渲染共用同一个线程,因为 JS 可修改 DOM 结构
    • 同一时间只能做一件事,DOM 渲染会阻塞 JS 的执行
    • 浏览器和 nodeis 已支持 JS 启动进程,如 Web Worker (只能执行 js 计算,无法渲染 DOM)
  • 弱类型
  • 跨平台
  • 解释型语言
  • 以事件驱动为核心
  • 遵循 ES 标准

严格模式 的特点

  • 全局变量必须先声明
  • 禁止使用 with
  • 创建 eval 作用域
  • 禁止 this 指向 window
  • 函数参数不能重名
  • 禁止使用0开头的数字

数据类型有哪些

  • 值类型( 6 种):Number、String、Boolean、Null、Undefined、Symbol(ES6新增)
  • 引用类型(1 种):Object

解构赋值

【实战】互换赋值

let a=1, b=2;
[b,a] = [a,b]
console.log(a,b) //2 1

作用域

即变量的合法使用范围

  • 全局作用域——全局可使用
  • 函数作用域——函数内可使用
  • 块级作用域(ES6 新增)——代码块内能使用 (如 if、for 语句的 {} 内)

自由变量

  • 一个变量在当前作用域没有定义,但被使用了
  • 在变量定义的地方(不是执行的地方)向上级作用域一层一层寻找,直至找到为止
  • 如果到全局作用域都没找到,则报错 xx is not defined

函数

函数声明和函数表达式的区别

  • 函数声明 function fn() {…}

  • 函数表达式 const fn= function() {…}

  • 函数声明会在代码执行前预加载,而函数表达式不会

    函数在声明前调用,可以正常执行,改用函数表达式就不行,调用时变量 fn 还不是函数,会报错。

普通函数和箭头函数的区别

普通函数 箭头函数
声明方式 function , 可以是具名/ 匿名函数 => ,‌只能为匿名函数
this指向 运行时确定
可以根据调用方式不同而改变
定义时确定
总是指向定义时的上层作用域中的this
(call , bind , apply 无法改变 this 指向)
构造函数 可以用作构造函数 不能用作构造函数
arguments 对象 有自己的 arguments 对象 没有自己的 arguments 对象,继承父级作用域中的 arguments 对象

箭头函数不适用的场景

  • 构造函数不能使用箭头函数(箭头函数没有自己的this,所以不能作为构造函数使用,也不能通过new操作符调用)
  • 事件监听的回调函数不能使用箭头函数(事件监听的回调函数中需通过 this 读取元素本身,但箭头函数内的 this 为外层的 window)
  • 对象的方法不适合使用箭头函数(对象方法需要通过 this 获取对象自己,但箭头函数内的 this 为外层的 window)
  • 原型链的方法不适合使用箭头函数(原因与对象的方法相同)
  • Vue 的生命周期和方法不能使用箭头函数(原因与对象的方法相同)

回调函数

被作为另一个函数的参数传入的函数

闭包

访问了外部变量的函数

this关键字是如何工作的?(this 的指向)

  • 全局上下文中,this 指向全局对象(在浏览器中是window)
  • 事件监听器中,this 指向触发事件的元素
  • 类的静态方法中,this 指向类
  • 普通函数中,this 指向全局对象(在浏览器中是window)
  • 构造函数中,this 指向新创建的对象实例
  • 箭头函数中,this 指向外层作用域的 this
  • 对象的方法用普通函数书写,被调用时 this 指向对象,用箭头函数书写时,this 指向外层的 this (在浏览器中是window)
  • bind、call、apply 方法创建的新函数中 ,this 指向方法的第一个参数

详见
https://mp.csdn.net/mp_blog/creation/success/123093256

原型 vs 原型链

原型链是 JavaScript 中实现对象继承的一种机制

扫描二维码关注公众号,回复: 17429378 查看本文章
  • 声明构造函数/类时,不仅会创建该构造函数/类,还会创建相应的原型对象,通过构造函数/类的 protoType 属性可以访问,所有可以被继承的属性和方法都存储在该原型对象中
  • 使用构造函数/类创建对象时,对象实例的 __proto__ 属性存储了原型对象的引用
  • 通过 __proto__ 属性,便将对象继承的所有原型串联了起来,构成了原型链
  • 当访问对象的属性/方法时,若对象中没有找到,就会由近及远去原型链中找,直到原型链的顶端 Object.prototype

异步

一种避免耗时较长的任务阻塞代码执行的 JS 代码运行机制 ( 详解下文 JS 代码在浏览器中的执行顺序

最初使用回调函数实现,但因代码层层嵌套,形成了回调地域 难以维护,ES6 新增了 Promise 实现异步,通过 Promise 可链式调用的特征,解决了回调函数的回调地域 问题。

Promise

Promise 是构造函数,专为异步设计,可通过 new 创建未来会执行的对象实例(一个状态为 pending 的 Promise 实例 )

let p1 = new Promise((resolve, reject) => {
    
    });

pending 状态的 Promise 实例执行 resolve() 后,Promise 实例的状态会变为 resolved / fulfilled,并立即触发后续的 then 函数

let p1 = new Promise((resolve, reject) => {
    
    
  resolve();
});

let p2 = p1.then(function () {
    
    
  console.log("打印 p1 --", p1); // p1 状态为 fulfilled
});

// 若then函数内无报错,p2 状态为 fulfilled,若报错,则 p2 状态为 rejected
console.log("打印 p2 --", p2);

pending 状态的 Promise 实例执行 reject() 后,Promise 实例的状态会变为 rejected ,并立即触发后续的 catch 函数

let p1 = new Promise((resolve, reject) => {
    
    
  reject();
});

let p2 = p1.catch(function () {
    
    
  console.log("打印 p1 --", p1); // p1 状态为 rejected 
});

// 若catch 函数内无报错,p2 状态为 fulfilled,若报错,则 p2 状态为 rejected
console.log("打印 p2 --", p2);

Promise 的串行执行

async function promise_serial() {
    
    
  let result = [];

  result.push((await promise_list[2]).data);
  result.push((await promise_list[1]).data);
  result.push((await promise_list[0]).data);

  console.log(result);
}

promise_serial();

Promise 的并行执行

Promise.all() 用于将多个 Promise 实例,包装成一个新的 Promise 实例,实现等待多个 Promise 实例全部变为 fulfilled 状态才执行目标操作。

import axios from 'axios'

let infoList = []

let id_list = ['1', '2', '3']

let promise_list = []

for (let id of id_list) {
    
    
  promise_list.push(axios.get(`http://jsonplaceholder.typicode.com/users/${
      
      id}`))
}

Promise.all(promise_list).then((res) => {
    
    
  infoList = res.map((item) => item.data)
  console.log(infoList) // 得到预期结果
})

async await

async await 是基于Promise的语法糖,用于实现使用同步的语法实现异步

  • async 函数返回的都是 Promise 对象
  • await 只能在 async 函数中使用
  • await 后跟 Promise 对象:会阻塞代码的运行,需等待 Promise 变为 resolved 状态返回结果后,才继续执行后续代码
  • await 后跟非 Promise 对象:会原样返回
  • await 相当于 Promise 的 then
  • await 中的异常需用 try…catch 捕获
  try {
    
    
    const res = await p4;
     console.log('await后的Promise执行成功后的返回值res:',res);
  } catch (err) {
    
    
    console.error('await后的Promise执行失败后的报错err:',err); 
  }

JS 代码在浏览器中的执行顺序

异步任务分为 微任务宏任务

在这里插入图片描述

  • 同步任务放入调用栈 Call Stack
  • 微任务(Promise,async、await 等) 放入微任务队列 micro task queue
  • 宏任务(setTimeout ,setInterval、ajax、Dom事件等) 放入 Web APIs

添加 async 的函数本身不是异步,内部代码会同步执行,遇到 await 时,await 后紧跟的函数会立即执行,是同步任务,await 语句后的代码才是异步微任务

async function async1() {
    
    
  console.log("async1 start"); // 第 1 个打印
  await async2(); // 先执行 async2() , 进入 async2 函数内

  // await 后的代码都是微任务,将其放入微任务队列 --- 微任务 1
  console.log("async1 end"); // 第 3 个打印
}

// 定义函数,先跳过
async function async2() {
    
    
  console.log("async2"); // 第 2 个打印
}

// 代码从此开始执行,进入 async1 函数内
async1(); 

执行结果

async1 start
async2
async1 end

Promise 函数内的代码是同步执行,待 Promise 执行 resolve() 后会立即触发 then 函数,then 函数内的代码才是异步微任务

new Promise(function (resolve) {
    
    
  console.log("promise 函数内"); // 第1个打印
  resolve(); // Promise 状态变为 resolved , 立即触发了 then 函数
}).then(function () {
    
    
  // then 函数是个微任务,将其放入微任务队列
  console.log("then 函数内"); // 第3个打印
});

console.log("promise 函数外"); // 第2个打印

执行结果

promise 函数内
promise 函数外
then 函数内

setTimeout 和 setInterval 从被放入 Web APIs 开始计时,计时结束后,会放入回调队列(Callback Queue),等待 event loop 触发执行

对象

创建对象的方法

  • {}
  • 构造函数,含 new Object()
  • Object.create()
  • 工厂函数

{}、Object()、new Object()、Object.create() 的区别

  • {} 是通过字面量的方式创建空对象,原型为 Object.prototype,语法最简洁,也最常用。
  • Object() 与 new Object() 功能相同,无论传入什么参数,其创建的对象的原型都是 Object.prototype,但 new Object() 创建对象的意图更明确
  • Object.create() 创建的对象的原型由其传入的第一个参数决定。

【实战】判断变量是否为空对象

if(JSON.stringify(obj)==="{}"){
    
    
  // 是空对象
}

数组

【实战】判断是否为数组

Array.isArray(val)

数组常用的 API

数组的API 功能 入参 返回值 是否改变原数组
unshift 在数组头部追加元素 新元素 数组的新长度 改变
push 在数组尾部追加元素 新元素 数组的新长度 改变
shift 移除数组的第一项 被移除的元素 改变
pop 移除数组最后一项 被移除的元素 改变
sort 数组排序 排序规则函数 排序后的数组 改变
reverse 数组反转 反转后的数组 改变
fill 数组覆写 新元素,起始下标,终点下标 覆写后的数组 改变
splice 拼接数组 下标,删除数量,新元素 移除元素的数组 改变
slice 数组截取 起始下标,终点下标 截取的数组 不改变
filter 数组过滤 过滤规则函数 过滤后的数组 不改变
concat 数组合并 被合并的数组/元素 合并后的数组 不改变
map 数组格式化 被合并的数组/元素 格式化后的数组 不改变
reduce 数组缩减 缩减规则函数 计算结果 不改变
toString 数组转字符串 字符串 不改变
join 拼接元素 拼接符 字符串 不改变

数组的 API,有哪些是纯函数 ?

  • concat
  • map
  • filter
  • slice

纯函数:1.不改变源数组 2.返回一个数组

数组遍历

遍历方法 返回值 使用场景 备注 副作用
for 循环 —— 遍历数组 通用 可以改变原数组
forEach 循环 —— 遍历数组 ES5 新增,不支持中断和异步 可以改变原数组
for of 循环 —— 遍历数组 ES6 新增 可以改变原数组
map 格式化后的数组 格式化 数组的API 不会改变原数组
filter 过滤后的数组 过滤 数组的API 不会改变原数组
reduce 最终计算结果 累计 数组的API 不会改变原数组
every 匹配结果 全部匹配 数组的API 不会改变原数组
some 匹配结果 部分匹配 数组的API 不会改变原数组

数组去重

最简单的是使用 Set

let oldList = [1, 2, 3, 3];
let newList = Array.from(new Set(oldList)); // 得到 [1, 2, 3]

其他思路: 创建一个新数组,循环遍历目标数组,若新数组中不存在则添加,否则不添加,用 reduce 可便捷实现

let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
    
    
    if(!pre.includes(cur)){
    
    
      return pre.concat(cur)
    }else{
    
    
      return pre
    }
},[])
console.log(newArr);// [1, 2, 3, 4]

事件

事件冒泡 vs 事件捕获

  • 事件冒泡指事件从触发事件的元素开始,逐级向外传播至 html 节点
  • 事件捕获指事件从 html 节点,逐级向内传播到触发事件的元素

异常

throw 语句手动抛出异常

// 用户定义的 throw 语句 --- 通用错误
throw new Error('The number is low');

try catch 语句手动捕获并处理异常

try {
    
    
  // 执行目标代码
} catch (err) {
    
    
  // 控制台打印报错信息
  console.log(err);
} finally {
    
    
  // 无论是否报错都会执行的代码
}

常见的异常:

  • 通用错误 Error
  • 语法错误 SyntaxError
  • 类型错误 TypeError
  • 引用错误 ReferenceError
  • 范围错误 RangeError

垃圾回收 GC

导致内存泄漏的场景

  • 不断创建全局变量
  • 未及时清理的闭包
  • DOM元素的引用
  • 事件监听器

避免内存泄漏的方法

  • 避免创建全局变量。
  • 使用严格模式或者let/const声明变量。
  • 及时解除DOM元素的引用。
  • 在移除DOM元素之前,移除相关的事件监听器。
  • 使用弱引用或者引用计数来处理循环引用的问题。(如使用 WeakMap 和 WeakSet)
  • 使用try/catch/finally来确保资源在异常发生时也能被正确释放。

JS 的垃圾回收机制 GC

  • 引用计数(之前)
  • 标记清除(现代)
  • 标记整理(优化)
  • 分代式垃圾回收(V8引擎)

其他

延迟加载

https://blog.csdn.net/weixin_41192489/article/details/141133404

监控白屏、首屏

// 监听DOMContentLoaded事件来计算首屏时间和白屏时间
document.addEventListener('DOMContentLoaded', (event) => {
    
    
  const perf = performance.timing;
  // 首屏时间(First Paint)
  const firstPaint = event.timeStamp - perf.navigationStart;
  console.log(`首屏时间(First Paint): ${
      
      firstPaint} ms`);
 
  // 通过DOMContentLoaded事件的延迟来估算白屏时间
  const whiteScreenTime = event.timeStamp - perf.fetchStart;
  console.log(`估算的白屏时间: ${
      
      whiteScreenTime} ms`);
});

猜你喜欢

转载自blog.csdn.net/weixin_41192489/article/details/140994319