【前端】ES6一本通_划重点_补充面试题

近两天更新完基本内容,后续长期更新,建议关注收藏点赞。
ES6(ECMAScript 2015)是现代 JavaScript 的基础,在前端面试中非常常见。

  • 本文已汇总的本站笔记
    ES6最重要10特性
    对象新增
    数组新增
    异步、生成器
    Promise
    模块化
    类、继承
    async / await
    set / weakset

目录

  • 变量声明(let、const、var)
  • 解构赋值Destructuring Assignment
  • 模板字符串
  • 箭头函数(Arrow Function)
  • 默认参数 & 剩余参数 & 扩展运算符(...)
  • 对象字面量增强
  • Symbol
  • Set 和 Map
  • Promise(异步编程)
  • 类(class)和继承
  • 模块化(ES Modules)
  • 迭代器与生成器(进阶)
  • 可选链 & 空值合并运算符(ES2020+)
  • 其他
    • ​WeakSet
    • async/await


  • 扩展
    ES6+(7~12)
    Math数字新增

变量声明(let、const、var)

  • var 全局;let和const是块级作用域,仅在某一块有效。
  • const是常量不可改动其引用对象,既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值。const声明时必须赋值。
  • ​​​​​​​let 和 const 不存在变量提升,在赋值前不可访问->有“暂时性死区TDZ“
    在这里插入图片描述

解构赋值Destructuring Assignment

  • 如果解构不成功,变量跟数值个数不匹配的时候,变量的值为undefined。解构赋值解 undefined:数组解构允许、对象结构不允许、带默认值的解构允许
  • 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开
const [a, b = 2] = [1]; // a = 1, b = 2
const { x, y } = { x: 10, y: 20 };let x = 1, y = 2;
[x, y] = [y, x]; // 交换变量let[a,b,c]=[1,2,3];
let [, a, , b] = arr;//JSON中的解构赋值 
let json={name:'a',age:18}; 
let {n,a}=json;//都是undefined 
let {name,age}=json;//必须名字对应上
//起别名let {name: myName, age: myAge} = json; // myName myAge 属于别名console.log(myName); // 'a' console.log(myAge); // 18
//对象解构
function show({x, y}) {//从传入的对象中提取 x 和 y 属性并将它们作为函数的局部变量使用console.log(x, y);
}

模板字符串

  • 模板字符串和传统字符串拼接的区别?
项目传统字符串拼接模板字符串
引号单引号 ' 或双引号 "反引号
拼接方式使用 + 拼接使用 ${} 插值
多行字符串需要手动加 \n+支持直接换行
支持表达式不直观直接写表达式、函数、变量等${func(a)}

箭头函数(Arrow Function)

箭头函数属于表达式函数,因此不存在函数提升
箭头函数中没有 arguments,只能使用 … 动态获取实参
简化了函数表达式的写法,并且不绑定 this,即 this 始终指向定义时的上下文。
箭头函数不能当构造函数。

  • 箭头函数和普通函数有什么区别
    箭头函数没有prototype,所以箭头函数本身没有this,使用new调用箭头函数会报错 TypeError: fun is not a constructor,不能当构造函数
    箭头函数 没有自己的 this、arguments、super,箭头函数的this指向继承自外层,且不能通过 call/bind/apply 改变
    不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  • call() 和 apply() 对箭头函数的影响
    对于普通函数,call() 和 apply() 可以显式地改变 this 绑定。但对于箭头函数来说,不能改变
  • 何时使用箭头函数
    适用于回调函数,例如 setTimeout,避免 this 绑定错误
    适用于类方法,防止 this 绑定丢失
  • 优点
    解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题
    处理回调函数时特别有用,避免了this绑定问题。
  • 箭头函数有两种格式。
  1. 只包含一个表达式,连{ … }和return都省略掉了。自动作为返回值被返回
  2. 可以包含多条语句,这时候就不能省略{ … }和return。如果参数不是一个,就需要用括号()括起来。
const add = (a, b) => a + b;
let show=(...args)=>{//操作return;
}// 两个参数:
(x, y) => x * x + y * y// 无参数:
() => 3.14// 可变参数:
(x, y, ...rest) => {var i, sum = x + y;for (i=0; i<rest.length; i++) {sum += [i];}return sum;
}如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:
x => { foo: x }
正确写法:
x => ({ foo: x })
  • 输出练习
//例子1
const obj = {value: 42,getValue: () => {console.log(this.value);}
};
obj.getValue(); // undefined,this 不是 obj,而是外层作用域的 this
obj.getValue.call({ value: 100 }); // 仍然是 undefined,call() 无法改变 this//例子2
function outer() {const arrowFunc = () => {console.log(this); // 取决于 outer() 调用时的 this};arrowFunc();
}const obj1 = { method: outer };
obj1.method(); // this 是 obj1const obj2 = { method: outer.bind({ x: 1 }) };
obj2.method(); // this 是 { x: 1 },但箭头函数内的 this 仍然是 obj2.method() 调用时的 this//例子3
var obj = {birth: 1990,getAge: function (year) {var b = this.birth; // 1990var fn = (y) => y - this.birth; // this.birth仍是1990return fn.call({birth:2000}, year);//2015-1990}
};
obj.getAge(2015); // 25//例子4
const obj = { name: '张三'} function fn () { console.log(this);//this 指向 是obj对象return () => { console.log(this);
//this 指向 的是箭头函数定义的位置,
//那么这个箭头函数定义在fn里面,而这个fn指向是的obj对象,所以这个this也指向是obj对象} } const resFn = fn.call(obj); resFn();

默认参数 & 剩余参数 & 扩展运算符(…)

ES6中,函数也可以给默认参数了

  • ...args 是什么?与 arguments 区别?
    ...args 是 剩余参数(rest parameters) 的语法,是 ES6 引入的标准,用于收集函数的多余参数为一个数组。它与传统的 arguments 类似,但更强大、更灵活。
对比点...argsarguments
类型真正的数组 Array类数组对象(非真正数组)
是否可用于箭头函数✅ 可用❌ 不可用
是否支持解构、数组方法✅ 支持(如 map, reduce❌ 不支持直接用数组方法
是否明确命名✅ 参数名明确(如 args)❌ 不明确,只有 arguments
可读性✅ 好❌ 较差
  • 扩展运算符Spread & Rest
    如果传入的参数连正常定义的参数都没填满,最后的扩展参数会接收一个空数组(注意不是undefined),但固定的参数如果没传会是undefined
    • 作用
      合并、复制数组、传参数组
      参数收集、解构
      合并、复制对象
      字符串展开为数组
// 扩展数组
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4];// 扩展对象
const obj1 = { name: 'Alice' };
const obj2 = { ...obj1, age: 25 };// Rest 参数(收集函数的剩余参数)
function sum(...numbers) {return numbers.reduce((acc, num) => acc + num, 0);
}window.onload=function (){let aLi = document.querySelectorAll('ul li');let arrLi = [...aLi];//可以把ul li转变为数组arrLi.pop();arrLi.push('asfasdf');console.log(arrLi);
}function foo(a, b, ...rest) {console.log('a = ' + a);console.log('b = ' + b);console.log(rest);
}foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]foo(1);
// 结果:
// a = 1
// b = undefined
// Array []let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students; 
console.log(s1);  // 'wangwu' 
console.log(s2);  // ['zhangsan', 'lisi']//扩展运算符可以将数组或者对象转为用逗号分隔的参数序列let ary = [1, 2, 3];...ary  // 1, 2, 3console.log(...ary);    // 1 2 3,相当于下面的代码console.log(1,2,3);//扩展运算符可以应用于合并数组
// 方法一 let ary1 = [1, 2, 3];let ary2 = [3, 4, 5];let ary3 = [...ary1, ...ary2];// 方法二 ary1.push(...ary2);//将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];

对象字面量增强

const name = 'Bob';
const person = {name,           // 属性简写greet() { ... } // 方法简写//只是省略了 function 关键字和冒号 :,更清爽。
};

Symbol

  • 定义
    原本数据类型有 number、string、boolean、null、undefined,增加symbol
    Symbol 是一种新的原始数据类型,通常用于对象属性的唯一标识,避免命名冲突。symbol不可以new,symbol()返回的是一个唯一值。
  • 作用
    用作对象属性名,唯一性,防止属性冲突
  • Symbol 如何作为对象属性?
const key = Symbol('id');
const obj = { [key]: 123 };

Set 和 Map

Set 是一个无重复元素的集合,Map 是一个键值对集合(类似于Object)。

  • Set 与 Array 区别?去重?
特性ArraySet
是否允许重复值✅ 允许❌ 不允许
是否有顺序✅ 有顺序(按插入顺序)✅ 有顺序(按插入顺序)
支持索引访问arr[0] 可访问❌ 不支持 set[0] Set 没有索引
数据结构有序列表唯一集合
是否可遍历✅ 支持✅ 支持
去重能力❌ 需要手动处理✅ 自动去重
//set array互相转换
const arr = [1, 2, 2, 3];
const unique = [...new Set(arr)]; // ✅ 数组去重
console.log(unique); // [1, 2, 3]const set = new Set([1, 2, 3]);
const backToArray = Array.from(set); // ✅ Set 转数组
  • Map 与 Object 有何不同?
特性ObjectMap
键的类型支持只能是字符串或 Symbol任意类型(对象、函数、数字等)
是否有原型链有,默认继承自 Object没有原型,纯净键值对
键的顺序无保障,按照插入顺序可能乱✅ 按插入顺序保存
获取长度Object.keys().lengthmap.size
是否可迭代❌ 不能直接 for...of✅ 可直接 for...of
适合频繁增删❌ 效率低✅ 性能更优
是否用于 JSON 数据✅ 常用于结构化数据❌ 不能直接序列化
  • Map v.s. Set
    它们好多方法都一样。除了set get
特性MapSet
结构键值对(key => value)值的集合(value)
是否唯一key 唯一所有值唯一
是否可迭代✅ 是(按插入顺序)✅ 是(按插入顺序)
常见用途存储需要键值对的数据存储去重的值
键的类型任意类型(对象、函数都行)值本身是唯一的,无键
方法set(), get(), has(), delete(), clear()add(), has(), delete(), clear()
const set = new Set([1, 2, 3, 3]);  // { 1, 2, 3 } 重复的只显示一个
//set中的元素可以是任何可迭代对象 如数组、字符串、Map、Set
let str = "hello";
let set = new Set(str);
console.log(set);  // Set { 'h', 'e', 'l', 'o' }let map = new Map([["a", 1],["b", 2],["c", 3]
]);
let set = new Set(map);
console.log(set);  // Set { ['a', 1], ['b', 2], ['c', 3] }set.add(5);
set.delete(1);
set.has(2) //是否有这个值在set中 返回布尔值
set.size
set.clear()//清除所有成员 无返回值//Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
s.forEach(value => console.log(value))//set也有这些方法
for(let item of setArr){//默认就是valueconsole.log(item);
}
for(let item of setArr.keys()){console.log(item);}
for(let item of setArr.values()){}
for(let [k,v] of setArr.entries()){}let new_Set=[...set]//让set转为数组//类数组都有的特性 filter 是true的才留下
set = new Set([...set].filter(val=>val%2==0));//--------------
const map = new Map([['a', 1], ['b', 2]]);
const userMap = new Map();
userMap.set('name', 'Alice');
userMap.set('age', 30);
console.log(userMap.get('name')); // 'Alice'
console.log(userMap.has('age'));  // true
userMap.delete('age');
console.log(userMap.size);        // 1
userMap.clear();                  // 清空所有键值// 1. for...of
for (const [key, value] of map) {console.log(key, value);
}// 2. forEach
map.forEach((value, key) => {console.log(key, value);
});// 3. 获取键、值、键值对
console.log([...map.keys()]);   // ['a', 'b']
console.log([...map.values()]); // [1, 2]
console.log([...map.entries()]); // [['a', 1], ['b', 2]]

Promise(异步编程)

Promise 是 ES6 引入的一种解决异步问题的方式,能够更优雅地处理异步任务和错误。
Promise是一个构造函数,代表一个异步操作。调用.then(成功的回调函数,失败的回调函数)方法时,成功的回调函数是必选的、失败的回调函数是可选的。
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。

  • 优点:
    解决了回调函数带来的回调地狱问题。
    链式调用使得异步操作更加简洁、可读。
    提供了统一的错误处理机制(.catch())。
//回调地狱 嵌套多层
setTimeout(()=>{console.log('ok')setTimeout(()=>{console.log('ok')setTimeout(()=>{console.log('ok')//....continue...},3000)},2000)
},1000)
  • 缺点:比回调函数稍微复杂一些,需要一定的学习成本。
  • Promise 的三种状态?
    pending(待定)、fulfilled(已完成)和 rejected(已拒绝)。
  • .then()、.catch()、.finally() 的执行顺序?
    成功时.then()->finally()
    失败时.catch()->finally()
    finally()始终最后执行
  • async/await 如何配合 Promise 使用?
    await所在的函数加async,await 后面必须是一个 Promise(或类 Promise 对象)
    try…catch 捕获 Promise 的 reject
    async 函数本身也返回 Promise
function getError() {return new Promise((_, reject) => {setTimeout(() => reject('出错了'), 1000);});
}async function fetchWithError() {try {const res = await getError();console.log(res);} catch (err) {console.error('捕获错误:', err); // 输出:捕获错误: 出错了}
}let a = 1;
let promise = new Promise(function(resolve, reject){
//将现有的东西,转成一个promise对象,有resolve成功状态;reject失败状态if(a==10){resolve('成功');}else{reject('失败鸟');}
});
//promise.then(success, fail);
promise.then(res=>{console.log(res);
},err=>{     //err可以省略不写 也可以改成catchconsole.log(err);
})
promise.then(res=>{console.log(res);
}).catch(err=>{  //reject,发生错误,别名console.log(err);
})
  • 批量处理promise
    Promise.all([p1, p2, p3]): 并行执行异步任务。把promise打包,扔到一个数组里面,打包完还是一个promise对象。必须确保,所有的promise对象,都是resolve状态,都是成功状态
    Promise.race([p1, p2, p3]): 只要有一个成功,就返回
let p1 = Promise.resolve('aaaa');
let p2 = Promise.resolve('bbbb');
let p3 = Promise.resolve('cccc');Promise.all([p1,p2,p3]).then(res=>{//console.log(res);let [res1, res2, res3] = res;console.log(res1, res2, res3);
})const promiseArr=[//数组中promise实例哪个先执行完 就返回哪个结果fs.readFile('./1.txt', 'utf8'),fs.readFile('./2.txt', 'utf8'),fs.readFile('./3.txt', 'utf8'),
]
Promise.race(promiseArr)
.then(res=>{console.log(res)
})
.catch(err=>{console.log(err.message)
})

类(class)和继承

  • class 与构造函数的区别?
    相同点
    都可以创建实例对象;
    实例通过 new 创建;
    实例上的属性是构造函数内部 this 定义的;
    共享方法都挂载在 prototype 上;
    都可以继承(但 class 更方便);
对比点构造函数(Function)Class(类)
语法可读性较低更清晰、接近传统面向对象
原型方法定义手动挂载在 prototype 上自动添加到 prototype
严格模式默认非严格模式自动使用严格模式,不能被提升
构造函数调用可以不加 new(但会出错)必须使用 new 调用
类体方法枚举性自定义方法默认可枚举类中方法默认不可枚举
静态方法需要手动挂载使用 static 定义更方便
类本身是语法糖,本质还是函数本质是 function
继承借用 callObject.create使用 extends 更优雅
  • 构造函数首字母应当大写,而普通函数首字母应当小写,一些语法检查工具如jslint将可以帮你检测到漏写的new
  • ES6 引入了面向对象编程的语法糖,使用 class 关键字来定义类,简化了传统的面向对象方法。class的定义包含了构造函数constructor,避免手动继承(操作原型链)Student.prototype.hello = function () {…}这样分散的代码。
    JavaScript中,可以用关键字new来调用构造函数,并返回一个对象。它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;用new 创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身
xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true
Object.getPrototypeOf(xiaoming) === Student.prototype; // true
xiaoming instanceof Student; // true
  • ES6 中的 class 是没有提升功能的,而在 ES5 中,使用函数模拟的类是有“提升”效果的。即class 并没有函数提升的特性,这意味着类声明必须在使用之前声明。如果在类 person 定义之前访问它,会抛出 ReferenceError 错误。这是因为 ES6 的 class 声明是 块级作用域,并且没有提升。
    在 ES5 中,我们通常使用构造函数来模拟类,而 JavaScript 中的函数声明是有提升的(hoisting),也就是说函数声明会被提升到作用域的顶部,所以我们可以在函数声明之前调用它。
  • 静态方法 在方法前面加static,不需要创建实例,直接调用类.类方法,class_name.static_func()
  • 继承:新方法 子类extends父类
  //父类class Person{constructor(name){this.name = name;}showName(){return `名字为: ${this.name}`;}}//子类class Student extends Person{constructor(name,skill){//别忘了加上参数super(name);
//扩展子类而不是覆盖对应函数时,必须super()把父类对应函数拉过来 this.skill = skill;}showSkill(){super.showSkill(); //父级的同名方法执行return  `哥们儿的技能为: ${this.skill}`;}}//调用let stu1 = new Student('Strive','逃学');console.log(stu1.showSkill());let a = 'strive';
let b = 'method';class Person {//ES6 class
//注意首字母大写!!constructor(name, age) {this.name = name;this.age = age;}greet() {console.log(`Hello, I'm ${this.name}`);}
// 计算属性名(computed property names)在 ES6 中动态创建对象的属性或方法名。
//[a + b] 这种语法可以通过表达式来计算属性名或方法名。[a + b]() {console.log("This is a dynamic method!");}//get 和 set 是用于定义**属性访问器(accessor properties)**的关键字。
//通过这两个方法,可以自定义属性的获取(getter)和设置(setter)行为。
//Getter:用来获取属性的值。
//Setter:用来设置属性的值。
/*
使用 getter 和 setter 的优点:
你可以在设置属性时,加入额外的逻辑,例如数据验证、格式化等。
获取属性时,你可以动态计算或处理值,而不需要暴露复杂的内部逻辑。
*/// Getter: 获取_name的值,通过 person.name 的方式访问,实际上调用的是 get name() 方法。get name() {return this._name;}// Setter: 设置_name的值,通过 person.name = 'newName' 的方式调用,实际上会触发 set name(value) 方法set name(value) {if (value.length < 3) {console.log("Name must be at least 3 characters long.");} else {this._name = value;}}
}const p = new Person('Alice', 25);
p.greet();<script>function Person(name, age){//ES5 模拟类的函数this.name = name;this.age = age;}/* Person.prototype.showName = function(){return `名字为: ${this.name}`;};Person.prototype.showAge = function(){return `年龄为: ${this.age}`;}; */
//新旧版对比Object.assign(Person.prototype,{ //assign 合并showName(){return `名字为: ${this.name}`;},showAge(){return `年龄为: ${this.age}`;}});let p1 = new Person('Strive', 18);console.log(p1.showName());console.log(p1.showAge());//父类function Person(name){this.name = name;}Person.prototype.showName = function(){return `名字是: ${this.name}`;};//子类function Student(name,skill){Person.call(this,name); //继承属性  //使用矫正this函数this.skill = skill;}Student.prototype = new Person(); //继承方法//调用let stu1 = new Student('Strive','逃学');console.log(stu1.showName());</script>

模块化(ES Modules)

  • CommonJS 适用于服务器端的 Javascript 模块化。nodejs默认支持CommonJS模块化规范,如果想在nodejs中体验ES6模块化规范,需要按照以下步骤配置:确保安装v14.15.1及+版本的nodejs,在package.json根节点中添加"type":"module"节点
  • ES Module v.s. CommonJS
    相同点:都是模块化规范,导入导出模块
    不同点:
    CommonJS出现在ES6前,不支持动态导入,运行时加载,同步加载模块。导出的是值的拷贝,导入模块执行完后模块内部的变化不会影响外部。导出用module.exports or exports,导入用require。适用于服务端nodeJS。共享模块内的变量。
    ES Module ES6引入,支持动态引入,编译时加载,异步加载模块。导出的是值的引用,外部和内部时刻保持一致变化。适用于浏览器。export and import。顶层this是undefined,在严格模式下运行,每个模块有各自的顶层作用域,内部变量不会全局共享。有利于前端代码拆分、懒加载优化性能、静态分析Tree Shaking。
  • ES6模块化规范
    ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不需再额外学习 AMD、CMD 或 CommonJS 等模块化规范。
    ES6 模块化规范中定义:
    ⚫ 每个 js 文件都是一个独立的模块
    ⚫ 导入其它模块成员使用 import 关键字
    有提升效果,import会自动提升到顶部,首先执行,也就是说:不管放前面放后面都最先引入
    无论导入多少次 都只导入一次
    ⚫ 向外共享模块成员使用 export 关键字
  1. 默认导出/导入
    注意:每个模块只允许一次export default否则报错
export default {n1,show
}//a.js
export function get_data(url){ //默认导出fetch(url).then(response => response.json()).then(data => {console.log(data);return data;});
}
//b.js
import { get_data } from './utils.js'; //默认导入
//同时要在b.html中模块化的引入b.js//html
<script  type="module" src="./js/b.js"></script>
  1. 按需导出/导入
    每个模块可以多次。
    按需导入时可用as重命名,可以和默认导入一起使用
// 按需导出
export const add = (a, b) => a + b;
export const a=12; //也可以导出变量
const b=1;
const c=2;
export {b as beta ,c as cici}
export function say(){}// 按需导入
import { add,a,cici } from './math.js';
  1. 如果是单纯执行某个模块代码,不需要export,可以直接导入并执行即可。
// c.js
for(let i=0;i<3;i++)console.log(i)//index.js
import './c.js' //直接导入并执行
  1. html文件中如何引入?<script type="module"></script>+上述引入方式
<script type="module">import * as modTwo from './modules/2.js';console.log(modTwo.aaa);
</script>
  • import/export 和 require/module.exports 区别?
特性import/export(ES6)require/module.exports(CommonJS)
规范ES6(ECMAScript 2015)模块化规范CommonJS 是 Node.js 模块化规范
执行时机静态分析(编译时)动态加载(运行时)
是否支持异步加载支持异步加载(import()不支持(同步加载)
默认导入/导出使用 export default通过 module.exportsexports 导出
导入方式import {a, b} from './file'const {a, b} = require('./file')
动态导入支持动态导入(import()仅能使用 require 动态加载(但相对较麻烦)
运行时加载方式在文件编译阶段完成依赖关系在运行时逐步解析依赖
是否支持循环依赖支持(但会警告)支持(解决方案:require 返回缓存模块)
是否会被提升不会,必须在顶部使用会提升,可以在文件任何位置使用
浏览器支持原生支持(需要模块化支持的浏览器)需要通过构建工具(如 Webpack)支持
Node.js 版本支持Node.js 12+(需要开启 "type": "module"默认支持
加载方式优化,支持 Tree Shaking每次执行都会重新加载模块
  • export default 和 export 有什么不同?
特性export defaultexport (命名导出)
数量每个模块只能有一个 default 导出每个模块可以有多个命名导出
导入方式导入时可以自定义名字必须使用与导出时相同的名字
适用场景用于导出模块的主要功能/类/对象等用于导出多个功能、常量、函数等
导入时的灵活性导入时名称不受限制导入时名称必须一致
动态导入适用于动态导入,便于重命名导入适用于动态导入,并保持一致性

迭代器与生成器(进阶)

generator函数是‘生成器’,解决异步,深度嵌套的问题,现在有更好的解决方案: async/await

 function * gen(){yield 'welcome';//第一步yield 'to';//第二步return '牧码人';//第三步}let g1 = gen();console.log(g1.next()); //{value: "welcome", done: false没完成}console.log(g1.next()); //{value: "to", done: false}console.log(g1.next()); //{value: "牧码人", done: true}console.log(g1.next());//{ value: undefined, done: true }for(let val of g1){console.log(val);//不会遍历到return!
}//生成器可以解构赋值
let [a, ...b] = gen();//let[a.b.c]
//扩展运算符console.log(...gen());//转换为数组
console.log(Array.from(gen()));//应用:请求
<script src="https://unpkg.com/axios/dist/axios.min.js"></script><script>//https://api.github.com/users/itstrivefunction * gen(){let val = yield 'aaa';yield axios.get(`https://api.github.com/users/${val}`);}let g1 = gen();let username = g1.next().value;//console.log(g1.next(username).value);g1.next(username).value.then(res=>{console.log(res.data);});</script>/*
整个流程没有问题,
实际上你只是通过生成器来控制异步操作的执行顺序(类似协程的模式)。
但是,生成器的部分在这里并没有发挥出它的全部优势。
通常生成器和异步操作结合的方式是通过 yield 来暂停执行,等待异步操作完成,
然后再继续执行后续的代码。你可以通过使用 async/await 来简化代码结构,
或者将生成器的控制交给一个专门的调度器。
*///改进
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>//https://api.github.com/users/itstrivefunction* gen() {let val = yield 'aaa';  // This is where we get the usernamelet response = yield axios.get(`https://api.github.com/users/${val}`);return response.data;//返回一个 Promise 对象}function handleGenerator(gen) {const iterator = gen();function iterate(result) {if (result.done) return;  // If done, stop//配合Promise.then()result.value.then(val => {iterate(iterator.next(val));  
// Pass the result to the next yield
//递归调用});}iterate(iterator.next());  
// Start the iteration}handleGenerator(gen);
</script>

可选链 & 空值合并运算符(ES2020+)

更方便地处理嵌套对象和可能为 null 或 undefined 的值。

  • 可选链(Optional Chaining)
    可选链(?.)是用来安全地访问嵌套对象的属性,它会自动判断中间的任何一部分是否为 null 或 undefined,如果是,则会短路并返回 undefined,而不会抛出错误。
    user?.info?.nameuser->user.info->user.info.name
  • 空值合并运算符(Nullish Coalescing Operator)
    与 逻辑 OR 运算符 (||) 类似,但 ?? 仅在左侧值为 null 或 undefined 时 才会返回右侧值,而 || 会在左侧值是任何假值(如 0, false, “”, NaN 等)时返回右侧值。
  • 练习
    const name = user?.info?.name ?? 'default';

其他

​WeakSet

  • 定义
    WeakSet 是 ES6 引入的一种新的集合类型。
    弱引用:WeakSet 对象的元素是 弱引用,这意味着如果一个对象没有其他引用指向它,垃圾回收机制会回收它,而不会因为它在 WeakSet 中的存在而阻止回收。
    WeakSet 支持以下基本操作:
    add(value):将一个对象添加到 WeakSet 中。如果该对象已经存在,什么都不做。初始往里面添加东西,是不行的。最好用add添加
    has(value):检查某个对象是否在 WeakSet 中。如果存在返回 true,否则返回 false。
    delete(value):从 WeakSet 中删除一个对象。如果该对象存在,
// 创建一些对象
let obj1 = {name: 'Alice'};
let obj2 = {name: 'Bob'};
let obj3 = {name: 'Charlie'};// 创建 WeakSet
let weakSet = new WeakSet();// 添加对象到 WeakSet
weakSet.add(obj1);
weakSet.add(obj2);// 检查对象是否在 WeakSet 中
console.log(weakSet.has(obj1));  // true
console.log(weakSet.has(obj3));  // false// 删除对象
weakSet.delete(obj1);
console.log(weakSet.has(obj1));  // false
  • 为什么会有weakset?
    1)跟踪对象的存在性:WeakSet 可用于跟踪对象是否曾经存在,而不阻止这些对象的垃圾回收。例如,某些需要动态管理的对象,且在不再需要时能被自动清除。
    2)避免内存泄漏:由于 WeakSet 使用弱引用,集合中的对象可以被垃圾回收,避免了因长期引用对象而导致的内存泄漏。​​​​​​​在一些缓存系统中,可以使用 WeakSet 来存储对象,确保不再被引用时自动被清除。
  • WeakSet 与 Set 的区别:
    在这里插入图片描述

async/await

在此之前 promise只能通过链式.then()方式处理,可读性不好,比较复杂。

  • async/await 是 ES8(ECMAScript 2017)引入的语法糖,旨在让异步代码更简洁、更易读。它是基于 Promise的,提供了更直观的方式来处理异步操作,避免了传统回调函数带来的“回调地狱”(callback hell)。async/await 本质上 是写法上看起来像同步,但底层仍是异步执行。async-await是配套使用的。在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行。
  • 虽然await写法是一步一步的,但 await 后面的操作其实是非阻塞的,它只是让当前 async 函数“暂停”,而不会阻塞整个程序的运行。你可以把 await 理解成一个“暂停点”,程序会在那儿等着 Promise 完成,然后继续往下走。
    注意:await 是暂停,但不是“阻塞线程”
  • JavaScript 是 单线程的
    await 并不是开了一个新线程,而是把当前 async 函数挂起,等 Promise 完成后再“恢复”继续执行;同时,主线程可以继续处理其他任务(比如事件监听、渲染页面、执行其他函数等);类似于单核 CPU 的分时复用,这边暂停下来,先去忙别的事,这边ok了再继续回来执行。
  • async 关键字
    async 用来标记一个函数为异步函数。当一个函数被标记为 async 时,它会返回一个 Promise对象。即使函数内部没有显式返回 Promise,async 函数会自动将返回值封装在一个 Promise 对象中。
    当一个 async 函数中的 await 语句后面的 Promise 变成 reject(即发生错误)时,整个 async 函数会中断执行,并且后续的代码不会再被执行,除非你在 async 函数内使用了 try…catch 来捕获错误。
  • await 关键字
    await 只能在 async 函数内使用,它会暂停函数的执行,等待一个 Promise 完成,并返回 Promise 的结果。如果 Promise 被拒绝,它会抛出异常,必须使用 try/catch 来捕获错误。
    一旦 Promise 被解析,await 会返回解析值。如果 Promise 被拒绝,await 会抛出拒绝的错误。
    await后面可以是promise对象,也可以数字、字符串、布尔值等。
async function fetchData() {try {let response = await fetch("https://api.github.com/users/octocat");let data = await response.json();return data;} catch (error) {console.log("错误:", error);}
}
fetchData();  // 如果网络请求失败,输出错误async function getUserRepos(username) {let userResponse = await fetch(`https://api.github.com/users/${username}`);let user = await userResponse.json();let reposResponse = await fetch(user.repos_url);let repos = await reposResponse.json();return repos;
}
getUserRepos("octocat").then((repos) => {console.log(repos);  // 输出该用户的所有仓库
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

初识 iOS 开发中的证书固定

引言 在移动应用安全领域&#xff0c;HTTPS/TLS 是数据传输的第一道防线&#xff0c;但仅依赖系统默认的证书验证仍有被中间人&#xff08;MITM&#xff09;攻击的风险。Certificate Pinning&#xff08;证书固定&#xff09;通过将客户端信任“钉”在指定的服务器证书或公钥上…

单片机的各个种类及其详细介绍

一、按架构分类的深度解析 1. ARM Cortex-M系列 核心优势&#xff1a; 统一架构&#xff1a;ARM生态完善&#xff0c;工具链&#xff08;Keil、IAR、GCC&#xff09;通用。 性能分层&#xff1a;M0&#xff08;低功耗&#xff09;、M3&#xff08;平衡&#xff09;、M4/M7&am…

5.7/Q1,GBD数据库最新文章解读

文章题目&#xff1a;Global, regional, and national burden and trends of rheumatoid arthritis among the elderly population: an analysis based on the 2021 Global Burden of Disease study DOI&#xff1a;10.3389/fimmu.2025.1547763 中文标题&#xff1a;全球、区域…

从微服务到AI服务:Nacos 3.0如何重构下一代动态治理体系?

在现代微服务架构的浪潮中&#xff0c;Nacos早已成为开发者手中的“瑞士军刀”。作为阿里巴巴开源的核心中间件&#xff0c;它通过动态服务发现、统一配置管理和服务治理能力&#xff0c;为云原生应用提供了坚实的基石。从初创公司到全球500强企业&#xff0c;Nacos凭借其开箱即…

Unity与Unreal Engine(UE)的深度解析及高级用法

以下是Unity与Unreal Engine(UE)的深度解析及高级用法对比,结合技术特性、行业应用与未来发展进行综合阐述: 一、核心差异与适用场景对比 1. 技术架构与编程模式 Unity 语言与脚本:主要使用C#,语法简洁且易于学习,适合快速原型开发和中小型项目。支持可视化脚本工具(如…

李沐动手深度学习(pycharm中运行笔记)——05.线性代数

05.线性代数&#xff08;与课程对应&#xff09; 1、导入torch import torch2、 标量由只有一个元素的张量表示 x torch.tensor([3.0]) y torch.tensor([2.0]) print("x y:", x y, "\nx * y:", x * y, "\nx / y:", x / y, "\nx ** y…

Python3与Dubbo3.1通讯解决方案(dubbo-python)

【文章非VIP可读&#xff0c;如果发现阅读限制为系统自动修改阅读权限&#xff0c;请留言我改回】 概述 最近AI项目需要java与python通讯&#xff0c;两边都是比较新的版本。因此需要双方进行通讯&#xff0c;在这里记录一下所采用的方案和关键点。 JAVA调用Python python通…

使用 DBeaver 将数据从 PostgreSQL 导出到 SQLite

使用 DBeaver 将数据从 PostgreSQL 导出到 SQLite&#xff0c;可按以下步骤进行&#xff1a; 1、连接到 PostgreSQL 数据库&#xff1a;打开 DBeaver&#xff0c;点击 “新建连接”&#xff0c;选择 “PostgreSQL”&#xff0c;输入数据库的地址、端口、用户名和密码等信息&am…

介词:连接名词与句子其他成分的桥梁

文章目录 1. with伴随1.表示“跟人或物”的伴随2.“行为”和“状态”的伴随2. of所属关系1. 人或物的所属关系2. 比较抽象的所属关系3. in1. 在......中,在......范围里2. 在某一段时间4. on1. 表示地点:在......上2. 表示时间:在某一天3. 关于某个主题5. at1. at + 具体时间…

FastApi快速实践

文章目录 一、主要功能&#xff1a;二、安装 FastAPI 和 Uvicorn&#xff08;运行服务器&#xff09;三、示例代码&#xff1a;四、运行服务器&#xff1a;1. 方式一&#xff1a;2. 方式二&#xff1a; 五、访问接口六、如果需要跨域&#xff08;CORS&#xff09;七、总结 下面…

深度学习中保存最优模型的实践与探索:以食物图像分类为例

深度学习中保存最优模型的实践与探索&#xff1a;以食物图像分类为例 在深度学习的模型训练过程中&#xff0c;训练一个性能良好的模型往往需要耗费大量的时间和计算资源。而保存最优模型不仅可以避免重复训练&#xff0c;还能方便后续使用和部署。本文将结合食物图像分类的代…

护理岗位技能比赛主持稿串词

男&#xff1a;尊敬的各位老师 女&#xff1a;亲爱的各位同学 合&#xff1a;大家下午好。 男&#xff1a;在这鸟语花香&#xff0c;诗意盎然的季节里 女&#xff1a;在这阳光灿烂&#xff0c;激情似火的日子里 合&#xff1a;我们欢聚一堂&#xff0c;共同庆祝五一二国际护士节…

【翻译、转载】MCP 核心架构

核心架构 了解 MCP 如何连接客户端、服务器和 LLM 模型上下文协议 (MCP) 构建在一个灵活、可扩展的架构之上&#xff0c;能够实现 LLM 应用程序与集成之间的无缝通信。本文档涵盖了核心的架构组件和概念。 概述 MCP 遵循客户端-服务器 (client-server) 架构&#xff0c;其中…

Python 数据智能实战 (11):LLM如何解决模型可解释性

写在前面 —— 不只知其然,更要知其所以然:借助 LLM,揭开复杂模型决策的神秘面纱 在前面的篇章中,我们学习了如何利用 LLM 赋能用户分群、购物篮分析、流失预测以及个性化内容生成。我们看到了 LLM 在理解数据、生成特征、提升模型效果和自动化内容方面的巨大潜力。 然而…

Linux:进程优先级及环境

一&#xff1a;孤儿进程 在Linux系统中&#xff0c;当一个进程创建了子进程后&#xff0c;如果父进程执行完毕或者提前退出而子进程还在运行&#xff0c;那么子进程就会成为孤儿进程。子进程就会被systemd&#xff08;系统&#xff09;进程收养&#xff0c;其pid为1 myproces…

Java大厂面试:Java技术栈中的核心知识点

Java技术栈中的核心知识点 第一轮提问&#xff1a;基础概念与原理 技术总监&#xff1a;郑薪苦&#xff0c;你对JVM内存模型了解多少&#xff1f;能简单说说吗&#xff1f;郑薪苦&#xff1a;嗯……我记得JVM有堆、栈、方法区这些区域&#xff0c;堆是存放对象的地方&#xf…

CF1000E We Need More Bosses

CF1000E We Need More Bosses 题目描述 题目大意&#xff1a; 给定一个 n n n 个点 m m m 条边的无向图&#xff0c;保证图连通。找到两个点 s , t s,t s,t&#xff0c;使得 s s s到 t t t必须经过的边最多&#xff08;一条边无论走哪条路线都经过ta&#xff0c;这条边就是…

imx6uLL应用-v4l2

Linux V4L2 视频采集 JPEG 解码 LCD 显示实践 本文记录一个完整的嵌入式视频处理项目&#xff1a;使用 V4L2 接口从摄像头采集 MJPEG 图像&#xff0c;使用 libjpeg 解码为 RGB 格式&#xff0c;并通过 framebuffer 显示在 LCD 屏幕上。适用于使用 ARM Cortex-A 系列开发板进…

强化学习机器人模拟器——QAgent:一个支持多种强化学习算法的 Python 实现

QAgent 是一个灵活的 Python 类,专为实现经典的强化学习(Reinforcement Learning, RL)算法而设计,支持 Q-learning、SARSA 和 SARSA(λ) 三种算法。本篇博客将基于提供的 q_agent.py 代码,详细介绍 QAgent 类的功能、结构和使用方法,帮助您理解其在强化学习任务中的应用,…

Feign的原理

为什么 SpringCloud 中的Feign&#xff0c;可以帮助我们像使用本地接口一样调用远程 HTTP服务&#xff1f; Feign底层是如何实现的&#xff1f;这篇文章&#xff0c;我们一起来聊一聊。 1. Feign 的基本原理 Feign 的核心思想是通过接口和注解定义 HTTP 请求&#xff0c;将接…