ES6重点归纳

一、let 和 const

let

  • 内存特性:let声明的变量后所分配的存储空间里的值(如:数字、字符串、数组等)可以被改变,存储的指针可变更、指针指向的内存空间也可被改变也可改变(如:数组、对象等)
  • 作用域: 块级作用域
  • 暂时性死区:在声明之前使用会产生从而报错
  • 变量提升:不存在变量提升,不可重复声明
  • for循环:JavaScript 引擎内部会记住上一轮循环 ,初始化本轮let声明的变量

const

  • 内存特性:const声明变量后所分配的存储空间里的值(如:数字、字符串、数组等)等同于常量不可改变,存储的指针不可改变,指针指向的内存空间里的值可改变(如:数组、对象等)
  • 作用域、暂存行死区、变量提升特性与let相同
  • // 正确示例
    const obj = {}
    obj.father = '123'
    
    // 错误示例
    const obj = {}
    obj = { father: '123'}
    
    // 冻结对象
    const obj = {
      name: 'Alice',
      age: 20
    };
    
    Object.freeze(obj);
    
    obj.name = 'Bob'; // 抛出TypeError错误

顶层对象

  • 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window
  • 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self
  • Node 里面,顶层对象是global,但其他环境都不支持。

二、解构赋值和扩展

解构赋值

  • 解构赋值支持对象:对象、数组、字符串、布尔值、函数
  • 解构赋值支持设置默认值:当赋值对象严格等于undefined,默认值才生效
  • 对象的结构赋值内部机制,先找到同名属性,然后再赋值给对应的变量,真正被复制的是后者,而不是前者,该赋值过程“前者”称为“模式”,“后者”称为“变量”
  • 结构赋值的过程不能使用圆括号的三种场景:
    • 变量声明语句
    • 函数语句中的参数
    • 赋值语句中的模式
  • // 对象
    const node = {
      loc: {
        start: {
          line: 1,
          column: 5
        }
      }
    };
    
    let { loc, loc: { start }, loc: { start: { line }} } = node;
    line // 1
    loc  // Object {start: Object}
    start // Object {line: 1, column: 5}
    
    // 数组
    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
    
    // 字符串
    let {length : len} = 'hello';
    len // 5
    
    // 布尔类型
    let {toString: s} = true;
    s === Boolean.prototype.toString // true
    
    // 函数
    function add([x, y]){
      return x + y;
    }
    
    add([1, 2]); // 3

函数的扩展

  • 函数的参数可以设置默认值 ,例:
function fn(a,b = 1) {}

// 默认值作用域指向外层对象
let foo = 'outer';

function bar(func = () => foo) {
  let foo = 'inner';
  console.log(func());
}

bar(); // outer
  • 与解构赋值的默认值结合使用
// 这种写法函数第二个参数不能省略
function fetch(url, { body = '', method = 'GET', headers = {} }) {
  console.log(method);
}

fetch('http://example.com', {})  // "GET"


fetch('http://example.com')  // 报错


// 这种写法函数第二个参数可以省略
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
  console.log(method);
}

fetch('http://example.com')
// "GET"
  • rest参数
const obj = [1,2,3,4]
console.log(...obj) // 1,2,3,4
  • 箭头函数
    • 对于普通函数来说,内部的this指向函数运行时所在的对象,但是这一点对箭头函数不成立。它没有自己的this对象,内部的this就是定义时上层作用域中的this。也就是说,箭头函数内部的this指向是固定的,相比之下,普通函数的this指向是可变的。
    • 箭头函数实际上可以让this指向固定化,绑定this使得它不再可变,这种特性很有利于封装回调函数。
// 只有一条语句且返回值不为对象时,不需要return且可以省略代码块
const sum = (num_a, num_b) => num_a + num_b

// 只有一条语句时且返回值为对象时, 需要用圆括号
const getObj = id => ({ id: id, name: Tom })  // 不报错
const getObj = id => { id: id, name:Tom }  // 报错

// 箭头函数this指向问题
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

数组的扩展

  • 扩展运算符 :
console.log(...[1, 2, 3])  // 1 2 3
  • 扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符
  • 对象的扩展运算符等同于使用Object.assign()方法
    let aClone = { ...a };
    // 等同于
    let aClone = Object.assign({}, a);

三、Proxy、Promise、Reflect

proxy

  • Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
  • ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
var proxy = new Proxy({}, {
  get: function(target, propKey) {
    return 35;
  }
});

proxy.time // 35
proxy.name // 35
proxy.title // 35
  • 手撕Proxy:
const target = {
  name: 'Tom',
  age: 20
};

const handler = {
  get: function(target, prop) {
    console.log(`Getting ${prop} property`);
    return target[prop];
  },
  set: function(target, prop, value) {
    console.log(`Setting ${prop} property to ${value}`);
    target[prop] = value;
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // 输出:Getting name property Tom
proxy.age = 25; // 输出:Setting age property to 25
console.log(proxy.age); // 输出:Getting age property 25

Promise

  • Promise 是一种异步编程模式,它允许你在 JavaScript 中处理异步操作。Promise 对象表示一个异步操作的最终完成或失败,并且可以使用 then() 方法来处理异步操作的结果。Promise 可以帮助你避免回调地狱,使异步代码更加清晰和易于维护。
  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
  • 手撕Promise:
  • function MyPromise(fn) {
      const self = this;
      self.value = null;
      self.error = null;
      self.onFulfilled = null;
      self.onRejected = null;
    
      function resolve(value) {
        setTimeout(() => {
          self.value = value;
          self.onFulfilled(self.value);
        }, 0);
      }
    
      function reject(error) {
        setTimeout(() => {
          self.error = error;
          self.onRejected(self.error);
        }, 0);
      }
    
      fn(resolve, reject);
    }
    
    MyPromise.prototype.then = function(onFulfilled, onRejected) {
      const self = this;
      return new MyPromise((resolve, reject) => {
        self.onFulfilled = function(value) {
          try {
            const result = onFulfilled(value);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        };
    
        self.onRejected = function(error) {
          try {
            const result = onRejected(error);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        };
      });
    };
    
    const promise = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello, world!');
      }, 1000);
    });
    
    promise.then(
      value => console.log(value),
      error => console.error(error)
    );

Reflect

  • 在 JavaScript 中,对象的操作方法比较分散,例如获取属性值可以使用 obj[key]、obj.property、Object.getOwnPropertyDescriptor() 等方法,这些方法的名称和用法都不一样,不太方便记忆和使用。Reflect 提供了一组统一的方法,例如 Reflect.get()、Reflect.set()、Reflect.getOwnPropertyDescriptor() 等,使得对象操作更加一致和易于使用。
  • Reflect 提供了一组统一的 API,使得对象操作更加一致和易于使用,同时也支持 Proxy 拦截和提供了一些新的操作方法,从而提高了 JavaScript 的灵活性和可扩展性。

示例:

const obj = {
  name: 'Tom',
  age: 20
};

console.log(Reflect.get(obj, 'name')); // 输出:Tom

Reflect.set(obj, 'age', 25);
console.log(obj.age); // 输出:25

console.log(Reflect.has(obj, 'name')); // 输出:true

Reflect.deleteProperty(obj, 'age');
console.log(obj.age); // 输出:undefined

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person = Reflect.construct(Person, ['Tom', 20]);
console.log(person.name, person.age); // 输出:Tom 20

function sayHello(name) {
  console.log(`Hello, ${name}!`);
}

Reflect.apply(sayHello, null, ['Tom']); // 输出:Hello, Tom!

四、Class 与 构造器

Class 类

  • 类定义:使用class关键字定义类,类名通常采用大写字母开头的驼峰命名法。
  • 构造函数:类中可以定义一个构造函数,使用constructor关键字定义,用于初始化对象的属性。
  • 属性和方法:类中可以定义属性和方法,使用类似于对象字面量的语法,但是不需要使用逗号分隔。
  • 继承:类可以通过extends关键字实现继承,子类可以继承父类的属性和方法,并且可以重写父类的方法。
  • super关键字:子类中可以使用super关键字调用父类的构造函数和方法。
  • 静态方法:类中可以定义静态方法,使用static关键字定义,静态方法可以直接通过类名调用,而不需要创建对象。
  • getter和setter:类中可以定义getter和setter方法,用于获取和设置对象的属性值。
  • 类表达式:类也可以使用表达式的方式定义,类表达式可以赋值给变量,也可以作为函数参数传递。

Generator函数

  • 异步编程:Generator函数可以使用yield关键字暂停和恢复函数的执行,可以用于处理异步操作,避免回调地狱和多层嵌套的回调函数。
  • 迭代器:Generator函数返回的是一个迭代器对象,可以用于遍历数据结构,例如数组、对象、Map等。
  • 状态机:Generator函数可以用于实现状态机,每次调用next方法时,函数会从上一次暂停的位置继续执行,可以根据不同的状态返回不同的值。
  • 控制流程:Generator函数可以用于控制流程,例如可以使用yield关键字暂停函数的执行,等待某个条件满足后再继续执行。
  • 协程:Generator函数可以用于实现协程,可以在函数执行过程中暂停和恢复执行,可以用于处理一些复杂的任务。
  • 用Generator和Promise模拟GPT文字输出的效果
  • function* gptOutput(text) {
      for (let i = 0; i < text.length; i++) {
        yield text[i];
        yield new Promise(resolve => setTimeout(resolve, 50));
      }
    }
    
    const output = gptOutput('Hello, world!');
    
    const timer = setInterval(() => {
      const { value, done } = output.next();
      if (done) {
        clearInterval(timer);
      } else {
        console.log(value);
      }
    }, 100);

五、待完善......

猜你喜欢

转载自blog.csdn.net/m0_56516186/article/details/129708493