前端宝典二十二:ES6必须掌握的功能和源码实现

本文主要是介绍前端宝典二十二:ES6必须掌握的功能和源码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在前端宝典二十一中,我们已经一起探讨了Promise\ async\ await,并手写了Promise相关的方法all、race、allSettled、any等。
这一讲我们一起来看ES6其他重点的内容

一、let、const

let和const的出现,首先是基于var存在的问题,来解决问题的
var存在的问题:

  • 变量提升
  • 无法形成词法作用域
  • 可以随意篡改变量值、重复声明

1、let与const

  • let是声明块级作用域的局部变量,只在代码块内有效
  • const声明的是只读常量,常量必须在声明时初始化
    这样声明的好处是安全、简洁、规范

2、解决的问题

1. 循环中使用let声明变量避免问题

for (let i = 0; i < 5; i++) {setTimeout(() => {console.log(i); }) 
}

上面的代码如果用var声明循环的变量,就会打印出5个5

2. const常量防止被篡改

const PI =3.1415926;
const URL='https://editor.csdn.net/md?articleId=141468095';

3. const声明对象属性避免被修改

const uer = {name: 'frank',age: 18,gender: 'Male'
}
user.like='sports';  // { name: 'frank', age: 18, gender: 'Male', like: 'sports' }
Object.freeze(user);
user.like='music'
console.log(user); // 报错

3、底层原理

let 的底层实现过程

  1. 编译阶段
    在代码编译阶段,编译器会扫描整个函数体(或全局作用域),查找所有使用 let 定义的变量,为这些变量生成一个初始值为 undefined 的词法环境(LexicalEnvironment)并将其保存在作用域链中。

  2. 进入执行上下文
    当进入执行块级作用域(包括 for、if、while 和 switch 等语句块)后,会创建一个新的词法环境。如果执行块级作用域中包含 let 变量声明语句,这些变量将被添加到这个词法环境的环境记录中。

  3. 绑定变量值
    运行到 let 定义的变量时,JavaScript 引擎会在当前词法环境中搜索该变量。首先在当前环境记录中找到这个变量,如果不存在变量,则向包含当前环境的外部环境记录搜索变量,直到全局作用域为止。如果变量值没有被绑定,JavaScript 引擎会将其绑定为 undefined,否则继续执行其他操作。

  4. 实现块级作用域
    使用 let 定义变量时,在运行时不会在当前作用域之外创建单独的执行上下文,而是会创建子遮蔽(shadowing)新环境。在子遮蔽的词法环境中,变量的值只在最接近的块级作用域内有效。

const 的底层实现过程

const 具有与 let 相同的底层实现原理,区别在于 const 定义的变量被视为常量(在赋值之后无法更改),因此变量声明时 必须初始化。此外,应该注意的是,使用 const 声明的对象是可以修改属性的。在定义 const 对象时,对象本身是常量,而不是对象的属性。只有对象本身不能被修改,而对象包含的属性可以任意修改。

let和const有暂时性死区
有点类似webcomponent的shadowDom

二、Proxy

在 ES6 中,Proxy是一种用于创建对象代理的机制,可以拦截并自定义对目标对象的各种操作,如属性访问、赋值、函数调用等。它提供了一种强大的方式来实现面向切面编程(AOP)和元编程。

Proxy对象由两部分组成:目标对象(target)和处理程序对象(handler)。处理程序对象定义了各种捕获器(trap)方法,这些方法会在对目标对象进行特定操作时被调用。

1、代码实例

以下是一些使用 Proxy 的示例:

  1. 拦截属性访问:
const target = {name: 'John',age: 30
};const handler = {get: function(target, prop) {console.log(`Accessing property "${prop}"`);return target[prop];}
};const proxy = new Proxy(target, handler);console.log(proxy.name); // Accessing property "name", then prints "John"
  1. 拦截属性赋值:
const target = {count: 0
};const handler = {set: function(target, prop, value) {console.log(`Setting property "${prop}" to "${value}"`);target[prop] = value;return true;}
};const proxy = new Proxy(target, handler);proxy.count = 10; // Setting property "count" to "10"
console.log(proxy.count); // 10
  1. 拦截函数调用:
const target = {add: function(a, b) {return a + b;}
};const handler = {apply: function(target, thisArg, args) {console.log(`Calling function with arguments ${args}`);return target.apply(thisArg, args);}
};const proxy = new Proxy(target, handler);console.log(proxy.add(3, 4)); // Calling function with arguments [3,4], then prints 7

通过使用 Proxy,可以在不修改原始对象的情况下,对对象的行为进行灵活的控制和扩展。

三、 Reflect

在 ES6 中,Reflect 是一个内置的对象,它提供了一组与对象操作相关的静态方法,这些方法与传统的对象操作方法类似,但更加规范化和统一。Reflect 的目的是为了提供一种更加优雅和一致的方式来执行对象操作,并且可以与 Proxy 对象一起使用,实现对对象操作的拦截和自定义。

1、常见方法及示例

  1. Reflect.get(target, propertyKey[, receiver]):获取对象的属性值。

    const obj = {name: 'John',age: 30
    };
    console.log(Reflect.get(obj, 'name')); // John
    
  2. Reflect.set(target, propertyKey, value[, receiver]):设置对象的属性值。

    const obj = {name: 'John'
    };
    Reflect.set(obj, 'age', 30);
    console.log(obj); // { name: 'John', age: 30 }
    
  3. Reflect.has(target, propertyKey):检查对象是否具有某个属性。

    const obj = {name: 'John',age: 30
    };
    console.log(Reflect.has(obj, 'name')); // true
    
  4. Reflect.apply(target, thisArgument, argumentsList):以指定的 this 值和参数列表调用一个函数。

    function add(a, b) {return a + b;
    }
    console.log(Reflect.apply(add, null, [3, 4])); // 7
    
  5. Reflect.construct(target, argumentsList[, newTarget]):使用指定的构造函数和参数列表创建一个新对象。

    class Person {constructor(name, age) {this.name = name;this.age = age;}
    }
    const person = Reflect.construct(Person, ['John', 30]);
    console.log(person); // Person { name: 'John', age: 30 }
    

Reflect 的方法与传统的对象操作方法类似,但在一些情况下更加灵活和安全,特别是在与 Proxy 对象一起使用时,可以实现对对象操作的拦截和自定义。

四、class语法

JavaScript 的类最终也是一种函数,使用 class 关键字创建的类会被编译成一个函数,因此其底层原理与函数有一些相似之处。

1、js中原型继承实现继承有哪些方法?

构造器借用、寄生式继承、组合继承

2、class语法继承,最接近哪种?

寄生+组合
js引擎将class语法转化为了原型链继承的方式

3、构造函数

使用 class 关键字来定义类时,在内部会创建一个特殊的函数,称为构造函数(constructor)。构造函数用于在创建对象时初始化对象的属性,类似于传统的基于原型的代码中的构造函数。

class Person {constructor(name, age) {this.name = name;this.age = age;}
}const p = new Person("Tom", 20);
console.log(p.name, p.age); // Tom 20

class 中定义的属性和方法分别定义在这个构造函数的 prototype 属性中。并且与原型方式不同的是,类的方法(静态方法)是不可枚举的,因此无法使用 for…in 循环进行遍历。

问题:是否可枚举
判断方法:
是否可枚举,就用for…in循环遍历到属性,不能遍历就不可枚举
如何设置不可枚举:

Object.defineProperty(obj,'name',{value:'frank',enumerable: false // 不可枚举
})
for (const key in obj) {console.log(key); // 没有输出
}

4、继承

class Student extends Person {constructor(name,age,grade){super(name,age);this.grade = grade;}
}

这段代码中,子类 Student 继承了父类 Person 的构造函数方法并添加了自己的属性和方法。

5、静态方法和属性

类中的静态方法和属性可以使用 static 关键字来定义,它们不是类实例的属性,而是类本身的属性和方法。

class Person {static species = "human";static saySpecies() {console.log(`We are ${this.species}.`);}
}

这段代码中定义了一个静态方法和一个静态属性,可以通过类本身直接调用静态方法和属性。

6、也可以使用class表达式创建函数对象

const Person = class {constructor(name,age){this.name = name;this.age = age;}
}

五、@修饰器(Decorators)

ES6 修饰器是一种特殊的语法,它可以用来修改类及其成员(方法、属性、访问器等)的行为。修饰器本质上是一个函数,它接收被修饰的目标、名称和描述符作为参数,并返回一个新的描述符或直接修改目标。

修饰器可以用于实现许多功能,如日志记录、性能测量、权限控制等,它可以帮助我们在不修改原有代码的情况下增强类的功能。

1、代码实例

  1. 方法修饰器

    function logMethod(target, name, descriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args) {console.log(`Calling method ${name} with arguments ${args}`);const result = originalMethod.apply(this, args);console.log(`Method ${name} returned ${result}`);return result;};return descriptor;
    }class MyClass {@logMethodmyMethod(arg1, arg2) {return arg1 + arg2;}
    }const instance = new MyClass();
    instance.myMethod(2, 3);
    

    在这个例子中,logMethod修饰器用于在方法调用前后输出日志信息。

  2. 类修饰器

    function logClass(constructor) {console.log(`Creating class ${constructor.name}`);return constructor;
    }@logClass
    class MyClass {constructor() {console.log('Constructing MyClass instance');}
    }const instance = new MyClass();
    

    这里的logClass修饰器在创建类的时候输出日志信息。

2、注意:

修饰器只可用于类和类属性方法,不可用于函数,因为存在函数提升。
修饰器是在编译时执行而不是运行时,所以修饰符是可以在编译时执行的代码,优秀哦

六、箭头函数

在箭头函数中,this关键字的作用域与它周围代码作用域相同,因而有时也被称为“词法作用域函数”。

1、基础概念与使用

箭头函数的原理是基于JavaScript中的闭包、this和参数作用域。在箭头函数中,this关键字始终指向函数所在上下文的this指针,而不是所在作用域的this指针。

2、箭头函数与普通函数的区别

  • this指向,箭头函数不能定义构造器
  • 不能new
  • 内部无arguments对象
  • this绑定方法失效,如apply、call、bind

所以,当我们需要引用所在父级的this指向时,可以用箭头函数。

这篇关于前端宝典二十二:ES6必须掌握的功能和源码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120727

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu