day1-ES6学习基础语法

title: day1-ES6学习基础语法
date: 2025-01-15 12:00:00
tags:
  
- 前端
categories:
  
- 前端

ES6

#

一 认识:什么是ES6?

ES6,全称 ECMAScript 2015,是JavaScript语言的一个重要版本,对语言进行了许多重大改进和新增功能。ES6的引入极大地提升了JavaScript的可用性、可读性和开发效率

*ECMAScript

  • 是由 ECMA 国际标准化组织制定的脚本语言标准,*JavaScript

  • 是其最广泛的实现。ES6(ECMAScript 2015)是ECMAScript标准的第六个版本,于2015年正式发布。ES6引入了许多新特性,使得JavaScript在语法和功能上更加强大和灵活。

#

二 第一章:ECMASript 相关介绍

# #

  • 什么是ECMASript?

ECMAScript 是一种由 ECMA 国际标准化组织制定的脚本语言标准,JavaScript 是其最广泛的实现。ECMAScript 定期发布新版本,引入新的语法和功能,提升语言的性能和可用性

  • 为什么学习ES6?

  • 1. 现代JavaScript开发的基础

    ES6引入了大量的新特性和语法,使得JavaScript更加现代化和强大。许多现代前端框架和库(如React、Vue、Angular等)都依赖于ES6及其后续版本的特性。因此,掌握ES6是学习和使用这些框架的基础。

    2. 提高代码可读性和可维护性

    ES6提供了更简洁、清晰的语法,使代码更具可读性和可维护性。

# #

*ES6语法

# # #

let语法

使用 let 声明变量的基本语法如下:

  • 变量不能重复声明

​
  var c = 1;
  var c = 2; // 合法
  console.log(c); // 输出: 2
  
  let d = 1;
  let d = 2; // 报错: SyntaxError: Identifier 'd' has already been declared
  
  • 块级作用域

​
let variableName = initialValue;
​

声明变量:let 用于声明一个块级作用域的变量。

初始化:可以选择性地为变量赋予一个初始值。

​
let name = '张三';
console.log(name); // 输出: 张三
​
name = '李四';
console.log(name); // 输出: 李四
​
  • 不存在变量提升

    • var:变量声明会被提升到函数或全局作用域的顶部,但不会提升赋值。未赋值时,变量值为 undefined

    • let:变量声明同样会被提升,但不会初始化访问未初始化的 let 变量会导致 ReferenceError

​
console.log(a); // 输出: undefined
var a = 5;
​
console.log(b); // 报错: ReferenceError: Cannot access 'b' before initialization
let b = 10;
​
  • 不影响作用域链

    由于 let 的块级作用域特性,使用 let 声明变量不会改变或扩展当前的作用域链。它仅在其所在的代码块内部引入新的作用域,而不会影响外部的作用域链结构。

​
function testVar() {
    console.log(a); // 输出: undefined(变量提升)
    var a = 10;
    if (true) {
        var a = 20; // 同一个变量,影响整个函数作用域
        console.log(a); // 输出: 20
    }
    console.log(a); // 输出: 20
}
​
function testLet() {
    // console.log(b); // 报错: ReferenceError: Cannot access 'b' before initialization
    let b = 10;
    if (true) {
        let b = 20; // 不同的变量,仅在块级作用域内有效
        console.log(b); // 输出: 20
    }
    console.log(b); // 输出: 10
}
​
testVar();
testLet();
​

# # #

const 语法

​
const PI = 3.14159;
console.log(PI); // 输出: 3.14159
​
// 尝试重新赋值会导致错误
// PI = 3.14; // 报错: TypeError: Assignment to constant variable.
​

声明常量:const 用于声明一个不可重新赋值的常量。

初始化:const 声明时必须同时进行初始化,不能先声明后赋值。

  • Const 一定要赋初值

  • 一般常量使用大写(潜规则)

  • 常量的值不能修改

  • 块儿级作用域 也只能在块内执行

  • 对于数组与对象的元素修改,不算做对常量的修改,不会报错

    const 只保证变量绑定的不可变性,而不保证对象或数组内部的内容不可变。即,使用 const 声明的对象或数组,其属性或元素仍然可以被修改

​
const person = {
    name: '张三',
    age: 25
};
​
// 修改对象属性是允许的
person.age = 26;
console.log(person.age); // 输出: 26
​
// 不能重新赋值整个对象
// person = {}; // 报错: TypeError: Assignment to constant variable.
​
const numbers = [1, 2, 3];
​
// 修改数组元素是允许的
numbers.push(4);
console.log(numbers); // 输出: [1, 2, 3, 4]
​
// 不能重新赋值整个数组
// numbers = [4, 5, 6]; // 报错: TypeError: Assignment to constant variable.
​

# # #

解构赋值

允许从数组或对象中提取值,并将其赋值给变量。解构赋值不仅使代码更加简洁和易读,还提升了开发效率

解构赋值是一种方便从数组或对象中提取数据,并将其赋值给独立变量的语法。它通过匹配结构的方式,将复杂的数据结构中的值“拆解”出来

# # # #

数组解构

  • 基本用法

​
const numbers = [1, 2, 3, 4, 5];
const [first, second, third] = numbers;
​
console.log(first);  // 输出: 1
console.log(second); // 输出: 2
console.log(third);  // 输出: 3
​

数组解构允许你按照数组的索引位置,将数组中的元素赋值给对应的变量。

  • 跳过元素

​
  const colors = ['red', 'green', 'blue', 'yellow'];
  const [primary, , tertiary] = colors;
  
  console.log(primary);   // 输出: red
  console.log(tertiary); // 输出: blue
  

如果你不需要某些元素,可以使用逗号(,)跳过。

  • 默认值

    如果解构时对应的元素不存在,可以为变量设置默认值

​
const fruits = ['apple'];
const [firstFruit, secondFruit = 'banana'] = fruits;
​
console.log(firstFruit);  // 输出: apple
console.log(secondFruit); // 输出: banana
​
  • 剩余元素

    使用展开运算符(...)可以将剩余的元素收集到一个数组中。

​
  const numbers = [1, 2, 3, 4, 5];
  const [first, second, ...rest] = numbers;
  
  console.log(first);  // 输出: 1
  console.log(second); // 输出: 2
  console.log(rest);   // 输出: [3, 4, 5]
  

# # # #

对象解构

对象解构允许你根据对象的属性名称,将对象中的属性值赋值给对应的变量

  • 基本用法

​
  const person = {
      name: '李四',
      age: 30,
      city: '北京'
  };
  
  const { name, age, city } = person;
  
  console.log(name); // 输出: 李四
  console.log(age);  // 输出: 30
  console.log(city); // 输出: 北京
  

这个是最简单的用法,通过前面的定义在输出对应的值

  • 重命名变量

​
  const user = {
      username: 'zhangsan',
      email: 'zhangsan@example.com'
  };
  
  const { username: userName, email: userEmail } = user;
  
  console.log(userName);  // 输出: zhangsan
  console.log(userEmail); // 输出: zhangsan@example.com
  

有时变量名称与对象属性名称不一致,可以使用冒号(:)进行重命名

  • 默认值

    如果解构时对应的属性不存在,可以为变量设置默认值。

​
  const settings = {
      theme: 'dark',
      language: 'zh-CN'
  };
  
  const { theme, fontSize = '16px' } = settings;
  
  console.log(theme);    // 输出: dark
  console.log(fontSize); // 输出: 16px
  

  • 嵌套结构

​
  const user = {
      name: '王五',
      address: {
          city: '上海',
          zip: '200000'
      },
      hobbies: ['reading', 'traveling']
  };
  
  const {
      name,
      address: { city, zip },
      hobbies: [firstHobby, secondHobby]
  } = user;
  
  console.log(name);        // 输出: 王五
  console.log(city);        // 输出: 上海
  console.log(zip);         // 输出: 200000
  console.log(firstHobby);  // 输出: reading
  console.log(secondHobby); // 输出: traveling
  

# # #

模版字符串

  • 是使用反引号(`)包裹的一种新的字符串表示方式。相比于传统的单引号(’)或双引号(”),模板字符串提供了更多的功能和更高的灵活性,使得字符串操作更加简洁和强大。

  • 声明

​
    const message = `这是一个模板字符串。`;
    console.log(message); // 输出: 这是一个模板字符串。
    
  • 内容中可以直接出现换行符

​
    let str = `
        
             
  • 沈腾
  •          
  • 玛丽
  •          
  • 魏翔
  •          
  • 艾伦
  •      
  `;       console.log(str);    
  • 变量拼接

    可以在模版字符串中变量拼接中直接使用${}来嵌入变量

​
    const name = '沈腾';
    const age = 44;
    
    const introduction = `你好,我是${name},今年${age}岁。`;
    
    console.log(introduction);
    // 输出: 你好,我是沈腾,今年44岁。
    

# # #

简化对象写法

当对象的属性名与赋值给他的变量名相同的时候,可以省略属性的显式声明,只有变量名即可

传统写法:

​
const name = '张三';
const age = 25;
​
const person = {
    name: name,
    age: age
};
​
console.log(person); // 输出: { name: '张三', age: 25 }
​

简化写法

​
const name = '张三';
const age = 25;
​
const person = {
    name,
    age
};
​
console.log(person); // 输出: { name: '张三', age: 25 }
​

# # # #

方法简写

在对象中定义方法时,可以省略fuction关键字

传统写法里面在运用$时必须要加上fuction关键字才可以

​
const person = {
    name: '李四',
    greet: function() {
        console.log(`你好,我是${this.name}`);
    }
};
​
person.greet(); // 输出: 你好,我是李四
​

但是简化用法的话,可以直接将fuction进行忽略

​
const person = {
    name: '李四',
    greet() {
        console.log(`你好,我是${this.name}`);
    }
};
​
person.greet(); // 输出: 你好,我是李四
​

等效于:

greet: function() { ... }。

# # #

箭头函数

# # # #

基本语法

​
(param1, param2, ..., paramN) => expression
​

参数:用小括号 () 包裹。如果只有一个参数,可以省略括号。

箭头:使用 => 表示函数体。

函数体

• 如果只有一条表达式,可以省略 {} 和 return,返回值是该表达式的结果。

• 如果有多条语句,需要用 {} 包裹,并显式使用 return 返回值。

  • 函数声明于调用

​
  let fn = (a, b) => {
      return a + b;
  };
  
  let result = fn(1, 2);
  console.log(result); // 输出: 3
  

# # # #

箭头函数与this

  • This 是静态的,this 始终指向函数声明中所在作用域下的this的值

​
function getName() {
    console.log(this.name);
}
let getName2 = () => {
    console.log(this.name);
};
window.name = '尚硅谷'; // 在浏览器环境中,为全局对象 window 添加 name 属性
​
const school = {
    name: 'ATGUIGU'
};
getName(); // 输出: 尚硅谷
getName2(); // 输出: 尚硅谷
​

这里是使用的直接调用

那么如果是call方法调用呢?

如果前面还是上面的代码,现在我改一种方式来写

​
getName.call(school); // 输出: ATGUIGU
getName2.call(school); // 输出: 尚硅谷
​

call ,普通函数通过call方法调用,显式的将this 指向school对象因此输出的就是ATCUIGU

然而,箭头函数的this 不会被call或者apply改变,始终指向全局对象window,因此输出window的值还是尚硅谷

  • 不能作为构造示例化对象

​
let Person = () => {};
new Person(); // 报错: Person is not a constructor
​

• 箭头函数没有自己的 this 和 prototype,因此不能通过 new 关键字调用。

• 构造函数需要 this 来指向新创建的实例对象,而箭头函数没有 this 的概念。

那么普通函数是可以用new 的

​
function Person(name, age) {
    // 将参数赋值给当前实例对象
    this.name = name;
    this.age = age;
​
    // 添加方法
    this.sayHello = function() {
        console.log(`你好,我是${this.name},今年${this.age}岁。`);
    };
}
​
// 使用 new 创建对象实例
const person1 = new Person('张三', 25);
const person2 = new Person('李四', 30);
​
// 调用方法
person1.sayHello(); // 输出: 你好,我是张三,今年25岁。
person2.sayHello(); // 输出: 你好,我是李四,今年30岁。
​
  • 不能用arguements变量

    箭头函数没有自己的 arguments 对象,它会继承自外层作用域的 arguments(如果外层作用域是普通函数)

    • 如果外层没有 arguments,在箭头函数中使用 arguments 会导致报错。

​
const arrowFunc = () => {
    console.log(arguments);
};
​
arrowFunc(1, 2, 3); // 报错: ReferenceError: arguments is not defined
​

那么普通 函数是怎样实现的呢

​
function normalFunc() {
    console.log(arguments);
}
​
normalFunc(1, 2, 3); // 输出: [Arguments] { '0': 1, '1': 2, '2': 3 }
​

1. arguments 的特点

1.1 定义

• arguments 是一个类数组对象,包含了函数调用时传递的所有参数。

• 类数组对象的意思是,它有 length 属性,但不能直接使用数组的方法,比如 map、reduce。

1.2 特性

~~• 只有普通函数会自动生成 arguments 对象,**箭头函数没有

  • arguments。*~~

• 它是一个动态对象,随着函数调用的参数变化而变化。

• arguments[0] 表示第一个参数,arguments[1] 表示第二个参数,依此类推。

• arguments.length 返回传入参数的数量。

  • 箭头函数的简写

有以下这几种形式:

  • 单表达式函数

    如果函数体只有一行表达式,可以省略{}和return关键字,直接返回表达式的结果

​
  // 普通写法
  const add = (a, b) => {
      return a + b;
  };
  
  // 简写
  const add = (a, b) => a + b;
  
  console.log(add(2, 3)); // 输出: 5
  

  • 单参数函数

    如果箭头函数只有一个参数,可以省略参数外的括号()

​
  // 普通写法
  const square = (x) => x 
* x;
  
  // 简写
  const square = x => x 
* x;
  
  console.log(square(4)); // 输出: 16
  

这样就连括号都不用加了

  • 返回对象字面量

    如果箭头函数返回一个对象字面量,必须用括号()将对象包裹起来,否则会被解析为代码块

​
  // 错误写法
  const createUser = (name, age) => { name: name, age: age };
  // 结果是 undefined,因为 `{}` 被解析为代码块
  
  // 正确写法
  const createUser = (name, age) => ({ name: name, age: age });
  
  console.log(createUser('张三', 25));
  // 输出: { name: '张三', age: 25 }
  

  • 默认参数

​
  // 普通写法
  const greet = (name = '游客') => `你好,${name}!`;
  
  console.log(greet()); // 输出: 你好,游客!
  console.log(greet('张三')); // 输出: 你好,张三!
  

# # #

参数默认值

  • 形参初始值,具有默认值的参数,一般位置要靠后(潜规则)

​
  function calculate(a = 1, b = 2) {
      return a + b;
  }
  
  console.log(calculate());          // 输出: 3
  console.log(calculate(5));         // 输出: 7 (b 使用默认值 2)
  console.log(calculate(3, 4));      // 输出: 7
  

  • 与解构赋值结合

day1-ES6学习基础语法_第1张图片

# # #

rest参数(形参位置)

​
function func(a, b, ...rest) {
    // a 和 b 是普通参数
    // rest 是一个数组,包含其余的参数
}
​

使用 ...变量名 表示 rest 参数,必须是函数参数列表中的最后一个参数

​
function func(a, b, ...rest) {
    console.log(a);    // 第一个参数
    console.log(b);    // 第二个参数
    console.log(rest); // 剩余参数组成的数组
}
​
func(1, 2, 3, 4, 5);
// 输出:
// 1
// 2
// [3, 4, 5]
​

# # #

扩展运算符(调用实参)

扩展运算符与 rest *参数

  • 的外观一样(都是...),但功能不同。扩展运算符用于展开,而 rest 参数用于收集

  • 合并数组

    使用扩展运算符可以方便地合并两个或多个数组。

​
  const arr1 = [1, 2, 3];
  const arr2 = [4, 5, 6];
  
  const combined = [...arr1, ...arr2];
  console.log(combined); // 输出: [1, 2, 3, 4, 5, 6]
  
  • 数组克隆

    使用扩展运算符可以快速克隆一个数组,创建一个全新的副本。

​
    const arr1 = [1, 2, 3];
  const arr2 = [...arr1]; // 克隆数组
  
  arr2[0] = 10;
  
  console.log(arr1); // 输出: [1, 2, 3] (原数组未被修改)
  console.log(arr2); // 输出: [10, 2, 3]
  

扩展运算符(...)是克隆数组的最简洁方式。

  • 将伪数组转换为真正的数组

​
  const nodeList = document.querySelectorAll('div');
  const nodeArray = [...nodeList];
  console.log(nodeArray); // 输出: 一个数组形式的节点列表
  

• document.querySelectorAll:

• querySelectorAll 是一种 DOM 操作方法,用于在文档中选择所有符合指定 CSS 选择器的元素。

它返回一个 NodeList 对象,这是一个类数组对象,包含所有匹配的 DOM 节点。

NodeList 不能直接使用数组的方法(如 map、filter 等),但可以通过索引访问元素,并具有 length 属性。

# # #

ES6 symbol

Symbol 是 ES6 引入的一种原始数据类型,表示唯一的标识符。它的主要特点是每个 Symbol 值都是唯一的,即使它们的描述(description)相同,两个 Symbol 值也不会相等。

  • *创建symbol

​
  const sym1 = Symbol();
  const sym2 = Symbol('描述文本');
  
  console.log(sym1); // 输出: Symbol()
  console.log(sym2); // 输出: Symbol(描述文本)
  

使用 Symbol() 函数创建一个新的 Symbol 值,可以选择为其添加一个描述(description)用于调试或打印。

Symbol 的主要用途是创建唯一的标识符,避免属性名冲突,所以symbol具有唯一性

​
const sym1 = Symbol('key');
const sym2 = Symbol('key');
​
console.log(sym1 === sym2); // 输出: false
​
  • 全局symbol注册

    通过symbol 可以创建全局可共享的symbol,但是对于相同的描述,symbol会返回相同的symbol值

​
const sym1 = Symbol.for('shared');
const sym2 = Symbol.for('shared');
​
console.log(sym1 === sym2); // 输出: true
​
// 获取全局 Symbol 的描述
console.log(Symbol.keyFor(sym1)); // 输出: shared
​
  • 不能与其他数据进行运算

  • 使用symbol 定义对象的唯一属性

​
  const sym = Symbol('uniqueKey');
  const obj = {
      [sym]: '值',
  };
  
  console.log(obj[sym]); // 输出: 值
  

使用 Symbol 作为属性名时,必须用方括号([])访问,因为 Symbol 不是字符串。

# # # #

内置symbol值

*内置 Symbol 值

  • 是 JavaScript 提供的一些特殊 Symbol,它们定义了一些语言层级的行为,使开发者可以自定义对象的特定行为。这些内置 Symbol 是 ES6 及以后版本中的一部分。

# # # # #

1. Symbol.iterator

定义对象的默认迭代器,用于 for...of 循环或扩展运算符(...)。

​
const iterable = {
    data: [1, 2, 3],
    [Symbol.iterator]() {
        let index = 0;
        return {
            next: () => ({
                value: this.data[index++],
                done: index > this.data.length,
            }),
        };
    },
};
​
for (const value of iterable) {
    console.log(value); // 输出: 1, 2, 3
}
​

# # # # #

2. Symbol.asyncIterator

定义对象的异步迭代器,用于 for await...of 循环.

​
const asyncIterable = {
    data: [1, 2, 3],
    [Symbol.asyncIterator]() {
        let index = 0;
        return {
            next: () =>
                new Promise((resolve) => {
                    setTimeout(() => {
                        resolve({
                            value: this.data[index++],
                            done: index > this.data.length,
                        });
                    }, 1000);
                }),
        };
    },
};
​
(async () => {
    for await (const value of asyncIterable) {
        console.log(value); // 每隔 1 秒输出: 1, 2, 3
    }
})();
​

# # # # #

3. Symbol.toStringTag

自定义 Object.prototype.toString 方法返回的值。

# # # # #

4. Symbol.toPrimitive

定义对象在类型转换(如数字、字符串)时的默认行为。

​
const obj = {
    [Symbol.toPrimitive](hint) {
        if (hint === 'number') return 42;
        if (hint === 'string') return 'Hello';
        return null;
    },
};
​
console.log(+obj); // 输出: 42
console.log(`${obj}`); // 输出: Hello
console.log(obj + ''); // 输出: null
​

# # # # #

5. Symbol.hasInstance

定义对象在 instanceof 操作符中的行为。

​
class MyClass {
    static [Symbol.hasInstance](instance) {
        return instance instanceof Array;
    }
}
​
console.log([] instanceof MyClass); // 输出: true
​

# # # # #

6. Symbol.isConcatSpreadable

定义对象是否可以在 Array.prototype.concat 中展开。

​
const arrayLike = {
    [Symbol.isConcatSpreadable]: true,
    length: 2,
    0: 'a',
    1: 'b',
};
​
const result = [].concat(arrayLike);
console.log(result); // 输出: ['a', 'b']
​

内置 Symbol 作用

Symbol.iterator 定义对象的默认迭代器行为。

Symbol.asyncIterator 定义对象的异步迭代器行为。

Symbol.toStringTag 自定义对象的 toString 输出内容。

Symbol.toPrimitive 自定义对象的类型转换行为。

Symbol.hasInstance 自定义 instanceof 的行为。

Symbol.isConcatSpreadable 定义对象是否可以在 concat 中展开。

Symbol.match 自定义字符串匹配行为(match 方法)。

Symbol.replace 自定义字符串替换行为(replace 方法)。

Symbol.search 自定义字符串搜索行为(search 方法)。

Symbol.split 自定义字符串分割行为(split 方法)。

Symbol.species 定义派生对象使用的构造函数。

Symbol.unscopables 定义哪些属性不会出现在 with 语句中。

Symbol.description 获取 Symbol 的描述文本。

#

二 第二章:迭代器

迭代器是 JavaScript 中一种用于按需访问集合元素的机制。它提供了一种统一的方式来遍历集合对象(如数组、字符串、Set、Map 等)。通过迭代器,开发者可以按顺序获取集合中的元素,而无需关心底层数据结构的实现迭代器是一个对象,它符合迭代协议(Iterator Protocol)。

• 它提供一个 next() 方法,每次调用会返回一个包含 value 和 done 两个属性的对象:

• value:当前迭代的值。

• done:布尔值,表示是否迭代完成。

• false 表示还有更多元素。

• true 表示迭代结束。

# #

工作原理

  • 创建一个指针对象,指向当前数据结构的起始位置

  • 第一次调用对象的next方法,指针自动指向数据结构的第一个成员

  • 接下来不断调用next方法,指针一直想后移动,直到指向最后一个成员

  • 每调用next方法返回一个包含value和done属性的对象

创建一个简单的迭代器

​
function createIterator(array) {
    let index = 0;
    return {
        next() {
            if (index < array.length) {
                return { value: array[index++], done: false };
            } else {
                return { value: undefined, done: true };
            }
        },
    };
}
​
const iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // 输出: { value: 1, done: false }
console.log(iterator.next()); // 输出: { value: 2, done: false }
console.log(iterator.next()); // 输出: { value: 3, done: false }
console.log(iterator.next()); // 输出: { value: undefined, done: true }
​

当你自定义遍历数组时,要想到迭代器

#

二 第二章:生成器

生成器(Generator)是 ES6 引入的一种特殊函数,用于控制函数的执行流程。生成器函数可以暂停执行,并在需要时恢复执行。生成器返回一个 迭代器对象,通过 yield 关键字生成值。

​
function
* generator() {
    console.log('执行开始');
    yield 1; // 暂停点 1
    console.log('暂停后恢复');
    yield 2; // 暂停点 2
    console.log('生成器结束');
    return 3; // 返回值
}
​
const gen = generator();
​
console.log(gen.next()); // 输出: { value: 1, done: false }
console.log(gen.next()); // 输出: { value: 2, done: false }
console.log(gen.next()); // 输出: { value: 3, done: true }
console.log(gen.next()); // 输出: { value: undefined, done: true }
​

1. 暂停与恢复

• 每次执行 gen.next(),生成器从上一次暂停的地方恢复执行,直到遇到下一个 yield 或函数结束。

• yield 会将一个值返回给调用者,并暂停生成器的执行。

2. next() 的返回值

• value:当前 yield 的值(或 return 的值)。

• done:布尔值,表示生成器是否完成。

3. 执行流程

• 第一次调用 next(),生成器开始执行,直到第一个 yield。

• 再次调用 next(),从暂停点继续执行,直到下一个 yield 或 return。

在代码中,也可以用for...of

​
遍历
for(let v of gen())
{
  console.log(v);
}
​

运用生成函数

# # # #

异步处理

利用生成器(Generator)控制异步函数的执行顺序。具体实现是通过 yield 和生成器的 next() 方法,将异步任务串行执行。

1. 每个异步函数(如 one()、two()、three())在执行完成后,调用 iterator.next(),恢复生成器的执行。

  1. 生成器函数 gen() 中的 yield 暂停点控制了异步任务的顺序。

​
    // 异步任务 1
    function one() {
        setTimeout(() => {
            console.log(111);  // 模拟异步任务完成
            iterator.next();   // 恢复生成器执行
        }, 1000); // 1 秒后执行
    }
    
    // 异步任务 2
    function two() {
        setTimeout(() => {
            console.log(222);  // 模拟异步任务完成
            iterator.next();   // 恢复生成器执行
        }, 2000); // 2 秒后执行
    }
    
    // 异步任务 3
    function three() {
        setTimeout(() => {
            console.log(333);  // 模拟异步任务完成
            iterator.next();   // 恢复生成器执行
        }, 3000); // 3 秒后执行
    }
    
    // 生成器函数,用于串行控制
    function
* gen() {
        yield one();   // 执行 one(),暂停直到调用 next()
        yield two();   // 执行 two(),暂停直到调用 next()
        yield three(); // 执行 three(),暂停直到调用 next()
    }
    
    // 启动生成器
    const iterator = gen();
    iterator.next(); // 开始执行第一个异步任务
    

#

二 第二章:promise

# #

promise介绍与基本语法

Promise 是 ES6 引入的一种用于处理异步操作的对象。它提供了更加优雅的方式来组织异步代码,避免回调地狱(Callback Hell)的问题

1.1 什么是 Promise?

• Promise 是一个代表未来某个事件(通常是一个异步操作)最终结果的对象。

• 一个 Promise 有三种可能的状态:

1. Pending(进行中):初始状态,既没有被解决,也没有被拒绝。

2. Fulfilled(已完成):操作成功完成,Promise 被解决。

3. Rejected(已拒绝):操作失败,Promise 被拒绝。

状态一旦从 Pending 变为 Fulfilled 或 Rejected,就不可再改变

  • 基本语法

  • 实例化 Promise 对象

    Promise 是一个构造函数,接受一个执行器函数(executor)作为参数。执行器函数有两个参数:

    • resolve:将 Promise 状态从 Pending 变为 Fulfilled。

    • reject:将 Promise 状态从 Pending 变为 Rejected。

​
const promise = new Promise((resolve, reject) => {
    // 异步操作
    const success = true; // 模拟结果
    if (success) {
        resolve('操作成功!'); // 调用 resolve 传递成功的值
    } else {
        reject('操作失败!'); // 调用 reject 传递失败的原因
    }
});
​

可以通过then 与 catch 来处理promise 的结果通过 .then 和 .catch 方法处理 Promise 的结果:

then(onFulfilled, onRejected)

• onFulfilled:状态变为 Fulfilled 时调用。

• onRejected(可选):状态变为 Rejected 时调用。

• catch(onRejected):用于捕获错误,等价于 .then(null, onRejected)。

​
promise
    .then((result) => {
        console.log(result); // 输出: 操作成功!
    })
    .catch((error) => {
        console.log(error); // 如果失败,输出: 操作失败!
    });
​

Promise 的状态流转

初始状态是pending ,状态变成fulfilled,value是成功的结果

在来一个例子

​
const p = new Promise(function (resolve, reject) {
    setTimeout(function () {
        // 模拟异步操作
        let err = '数据读取失败'; // 模拟错误消息
        reject(err); // 将 Promise 状态改为 Rejected,传递错误原因
    }, 1000);
});
p.then(
    function (value) {
        console.log(value); // 如果成功(resolve),打印结果
    },
    function (reason) {
        console.error(reason); // 如果失败(reject),打印错误原因
    }
);
​

解析:

• then(onFulfilled, onRejected):

• onFulfilled:当 Promise 状态为 Fulfilled 时调用,接收 resolve 的值。

• onRejected:当 Promise 状态为 Rejected 时调用,接收 reject 的原因。

• 在这段代码中:

• 成功的回调函数没有被调用,因为 Promise 被 reject(err) 标记为失败。

• 失败的回调函数会被调用,console.error(reason) 输出错误消息。

如果 Promise 的状态已经被 resolve 或 reject 更改,setTimeout 中的 reject(err) 虽然执行了,但它无法再影响 Promise 的状态,因为状态已经不可变。

​
const promise = new Promise((resolve, reject) => {
    reject('失败'); // Promise 状态变为 Rejected
});
​
promise
    .then((value) => {
        console.log(value); // 不会执行,因为状态是 Rejected
    })
    .catch((reason) => {
        console.error(reason); // 输出: 失败
    });
​

Promise 的状态不可变

• Promise 的状态只能从 Pending 转为:

• Fulfilled(通过 resolve)。

• Rejected(通过 reject)。

• 一旦状态变为 Fulfilled 或 Rejected,Promise 的状态就锁定,后续对 resolve 或 reject 的调用不会生效。

因此,如果状态已经改变,*定时器执行时调用的

  • resolve *或

  • reject 将被忽略

# #

promise 封装读取文件

  • 引入 fs 模块

​
  const fs = require('fs');
  
  • 调用方法读取文件

​
  fs.readFile('./resoureces/为学.md',(err,data)=>{
  //如果失败,则抛出错误
  if(err) throw err;
  //如果没有出错,则输出内容
  console.log(data.toString());
  });
  
  • 使用 Promise 封装

​
  const p = new Promise(fuction(resolve,reject)){
  fs.readFile("./resoureces/为学.md",(err,data)=>{
  //判断如果失败
  if(err) reject(err); 
  //如果成功
  resolve(data);
  })
  }
  

​
p.then(fuction(value)
{
  console.log(value.toString());
},fuction(reason){
console.log("读取失败!");
});
​

# # #

promise封装AJAX请求

使用 *Promise

  • 封装 AJAX 请求,可以让异步操作更加清晰和方便。下面是如何通过 XMLHttpRequest 结合 *Promise

  • 封装 AJAX 请求的详细示例。

​
function ajaxPromise(url, method = 'GET') {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(method, url, true);
​
        // 设置请求头(如果需要)
        xhr.setRequestHeader('Content-Type', 'application/json');
​
        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(xhr.response); // 请求成功时调用 resolve
            } else {
                reject(new Error(`请求失败,状态码:${xhr.status}`)); // 请求失败时调用 reject
            }
        };
​
        xhr.onerror = () => {
            reject(new Error('网络错误')); // 网络错误时调用 reject
        };
​
        xhr.send(); // 发送请求
    });
}
​
// 使用示例
ajaxPromise('https://jsonplaceholder.typicode.com/posts')
    .then((response) => {
        console.log('成功响应:', JSON.parse(response));
    })
    .catch((error) => {
        console.error('请求失败:', error.message);
    });
​

你可能感兴趣的:(es6,学习,前端)