ECMAScript 6规范总结(长文慎入)

2023-10-30

闲话

学习ES6的动机起于对其promise标准的好奇,它与jQuery源码中Deferred不同,而且在异步编程中加入了Generator,在后续ES7中更有Async。这勾起我强烈的兴趣了解ES6更多的内容,于是完整的学习了阮一峰老师的《ECMAScript 6入门》

本文不对规范细节做详细说明。希望通过这篇博客,记录自己所理解的es6的语言风格和编程思想。

注:以《ECMAScript 6入门》为蓝本,大量用例出自其中。

ES6介绍

ECMAScript 6(简称ES6)是JavaScript语言的下一代标准,于2015年6月正式发布,也称ECMAScript 2015。

摘自《ECMAScript 6入门》

ECMAScript 3.0(1999年12月)成为通行标准,奠定了JavaScript通行标准,直到今天,我们一开始学,都是在学3.0版本语法。
ECMAScript 4.0草案(2007年10月),对ES3做了彻底升级,各方代表对是否通过产生严重分歧。2008年7月,EMCA开会决定终止开发,将其中涉及现有功能改善的一小部分,发布为ECMAScript 3.1(会后不久改名为ECMAScript 5),将其他激进的设想放在以后的版本,由于会议的气氛,该项目代号起名为Harmony(和谐)。

2009年12月,ECMAScript 5.0正式发布。Harmony项目一分为二,一些较为可行的设想定为JavaScript.next继续开发,后来演变成ES6,一些不是很成熟的设想,被视为JavaScript.next.next,在更远的将来再考虑推出。

2011年6月,ECMAscript 5.1版发布,并且成为ISO国际标准(ISO/IEC 16262:2011)
2015年6月,ECMAScript 6正式通过,成为国际标准。


ES6总览

tips:

  • ES6规范的原则是尽可能完整的向下兼容,除了块级作用域支持外,原有代码几乎不受影响。通过新增API及语法扩展支持。随着规范的普及,完全参照严格模式'use strict'将成为编程最佳实践

  • 不同类别的工具方法挂载在对应的构造函数上,而不是作为全局方法(如isNaN() -> Number.isNaN()),对原有全局方法进行了迁移(原有的还在)。

下面,分 5 点对 ES6 进行全面解读。ES6总览后,为每点的分条详述。

1、语法升级

对基本语法进行了增强,并调整为块级作用域支持。

用更直观的“声明式”思想(解构赋值、...扩展运算符、无 this 上下文困扰的箭头函数、for…of 遍历),对取值、赋值、对象表示、构造函数及继承等的过程进行了大幅简化。

2、模块化

静态化的模块系统支持(默认严格模式编程)。完美的循环依赖处理(commonjs只算半支持),动态的输出值引用。

3、类型升级

Number 新的二/八进制写法、浮点误差、安全数;String RegExp:全面支持32位utf16字符,定义了超简易的模板字符串拼接(并可便捷的自定义模板处理规则);引入基本数据类型Symbol,代表独一无二值,有效防止属性命名冲突;Array数组空位处理方法的修正,提供 for…of 遍历及对名值遍历的API支持;新增数据结构SetMap及弱引用的WeakSetWeakMap,可去重存储value、key-value。

数据结构的增加,使得ES6 for…of遍历不仅仅需要对数组、字符串等带有length属性的类数组生效,还需要能够个性化定制。抽象出Symbol.iterator接口,凡是带有该接口的对象均可被遍历(仅有length属性的类数组不可以),调用该接口。比如数组会调用Array.prototypeSymbol.iterator

Symbol属性的添加也使对象枚举相关的API增加了几个(是否枚举Symbol、原型链、不可枚举属性)

tips:遍历与枚举的不同在于,遍历是对值(value)的,枚举是对键(key)的。遍历的顺序是Symbol.iterator接口定义的(数组是0~n数字顺序);枚举是底层内部定义的(顺序:先数字排序、属性按时间排、Symbol按时间排),未开放权限

4、语言层面

分层的权限

为了便于理解,我把底层行为分为 规则层(基于对象,被遍历、被枚举、被正则匹配、被new、被转类型等)、属性配置层(基于属性,propertyDescriptor)。

ES6的一大特点是,开放权限。姑且把我所理解的权限分为 5 类:原型链、调用栈、作用域链、对象规则层、属性配置层。

ES6函数严格模式执行时不再对调用栈引用,此时支持尾递归优化。作用域链引用不可开放,这是词法作用域安全性、隔离性的根本。开放了规则层自定义,使得开发者能够自定义一些对细部规则的反应。开放了原型链的访问,使得已有对象也能直接改变原型链引用,使更强大的继承容易做到(通常尽可能不用)。属性配置层到了ES5就比较完善了。

ES6把规则层的部分行为抽象为一系列接口,涉及被正则匹配、被判断instanceof、被for…of遍历、数组是否可展开、构造器的返回对象和stringTag等。出于防止命名冲突的考虑,都使用Symbol值(独一无二),保存在内置的Symbol构造函数的属性上,共11个(很多并不是语言层面的重要规则操作,定位:偏个性化的需求 + 部分重要规则)

Object实例是js里的基础对象,包括函数都是由object衍生而来。它是一种基本的key-value式的数据结构。

  • 1、对象下通过内置Symbol规则属性个性化定义特殊行为时如何反应。
  • 2、每个属性的value,只是属性描述的一部分。Object.getOwnPropertyDescriptor(obj, pro)可获取,设定是否可枚举、可定义、只读、是否为访问器(get、set)。

Proxy和Reflect

ES6新增Proxy数据类型,可以通过new Proxy(obj, handler)生成对象操作的代理,本质是一个拦截层,涉及增删查改属性值、设置原型链、属性配置、遍历枚举、环境绑定、new等等操作(部分内置Symbol不是对对象的主要操作,只是小的个性化补充,就不包含在内了)。

新增Reflect,提供了所有与Proxy对应的语言默认操作方法,一一对应,目前有 14 个。

Reflect有着几乎所有对对象的重要操作,ES6以前跟语言相关的配置操作都在Object上,都迁移了过去,并且对设置型的API都以返回false表示设置失败,而不是抛出错误。以后语言内部相关的方法都将扩充到Reflect,Object上不一定会添加。

5、异步编程

传统的异步使用回调函数,函数以参数形式传入以待调用。复杂情况时,回调函数里可能也有异步逻辑,导致层层嵌套。而且还需要手动catch错误。

ES6推出了promise标准。既能把每层的逻辑解耦分开,又有自动的机制catch错误。通过then串联起来要执行的逻辑。

ES6支持Generator函数。它是语言层面的支持,用同步的方式来顺序书写异步代码,以yield暂停。相较promise有着更直观的控制流管理,“半协程”的实现,使得在yield进程的切换中仍然保留着调用栈,使得内部定义的 try…catch 总能捕捉到内部的错误,是完全意义上的同步式写法。虽然在promise的源码中利用词法作用域的特点也能解决。

但Generator只相当于一个状态机,声明式的定义了流程,还需要封装一个co模块函数才能实现支持异步逻辑的自动流程处理。

ES7提供了Async函数,是Generator的语法糖,调用时等同于被co函数加载执行的Generator函数。到此,异步编程算是得到了最佳实践。


语法升级

核心:用一目了然的方式,简化表达。定义ES6推荐的最佳编程实践。

1、作用域

ES6支持了块级作用域,{} 部分包裹的代码块具有独立的作用域,如if、for。新增let(变量) const(常量)定义变量,必须先定义后使用,不会变量提升,更不容易出错,填var的坑。

{
    let a = 5;
    const b = 4;  // 不能重新赋值或改变引用,但能改变引用对象内的属性
    a = 3;  // 3
    b = 3;  // error
}
console.log(a)  // error

// 自执行函数 作用有 2 点:1.防止全局污染; 2.构造闭包保存变量状态
// 在只需 第1点 时,可以 { 代码 } 替代

函数声明可以在块级内声明 { }不再报错,只在块级作用域中变量提升。

if (true)
    function a() {
   }  // error

// 正确版本,不能省略{}
if (true) {
    function a() {
   }
}
console.log(a);  // error


2、取值、赋值、对象表达

对象简写

let b ='check';
let obj = {
    a: 1,
    b,  // 等同 b: b ,即 b: 'check'

    c(x, y) { return x+y },  // 等同 c: function(){}

    get d() { return 2; }, // 设置 d 的 get 取值器函数
    [Symbol('foo')]() {
  return true},  // 设置 [Symbol('foo')] 的函数值
    * e(x) { yield x; }  // 设置 e 的Generator函数值

function test(x, y) {
   
    return {x, y};  // {x: x, y: y}
}

一步到位 的赋值方法 —— 解构赋值 + 默认值,直观、高效。

let [a, [b, c]] = [3, 'str'];
// a=3, b='s', c='t'   数组型赋值:要求右侧值有Symbol.iterator接口,如数组、字符串

let {a, b=4, c=4, d: _d=5} = {a: 2, b: 3};  // 等同 let {a: a, b: b=4, c: c=4, d: _d=5} = {a: 2, b: 3};
// a=2, b=3, c=4(默认值), _d=5(默认值)

[x, y] = [y, x]  // 交换赋值


/* ---- 优化示例 ---- */

// ES3,遇上一个 fun(args) 的API,args有7个可选属性接口,看得出么,一脸懵逼(゚Д゚≡゚Д゚)
function sb(args) {
   
    return args.a + args.b * args.c;
}

// ES6
function sb({a, b, c}) {
   
    return a + b * c;  // 无参数时出错
}
sb({a:1, b:2, c:3});  // 7

// 不传参数时默认为 {a:0, b:0, c:0}
function sb({a, b, c} = {a:0, b:0, c:0}) {
   
    return a + b * c;
}
sb();  // 7
sb({a:2});  // error, 不使用默认值,但b、c为undefined

// 属性默认值
// 无值参数取默认值{},无a、b、c参数,默认取0
function sb({a=0, b=0, c=0} = {}) {
    // 等同 {a: a=0, b: b=0, c: c=0} = {}
    return a + b * c;
}
sb();  // 7

引入“…”扩展运算符数组环境(函数参数算数组环境),用于赋值(左侧=)为rest参数(只可用于尾参数),用于取值则为扩展值(=右侧,需Symbol.iterator接口支持)

/* 赋值,rest 参数 */
let [a ,b, ...c] = [1, 2, 3, 4, 5];
// a=1, b=2, c=[3, 4, 5]
let [a ,b, ...[c, d]] = [1, 2, 3];
// a=1, b=2, c=3, d=undefined

function t(a, ...arr) {
   }
t(1,2,3) -> a=1, arr[1, 2]

/* 取值 */
let a = [...[1, 2], 3, ...'str'];
// [1, 2, 3, 's', 't', 'r']

/* 两者结合 —— 解决平常厌恶的只能apply传入相同参数的问题 */
function test(...args) {
     // 赋值
    return function _test() {
   
        return fun(...args);  // 取值,等同 fun.apply(this, args)
    }
}

ES7提案 引入“…”扩展运算符对象环境。用于赋值(左侧=)为rest参数(只可用于尾参数),用于取值则为扩展值(=右侧,只扩展自身的可枚举属性,等同Object.key(obj))

/* 赋值,rest 参数 */
let {a ,b, ...re} = {a:1, b:2, c:3, d:4, e:5};
// a=1, b=2, re={c:3, d:4, e:5]
let {...{x, y}} = {x:1, y:2};
// x=1, y=2

/* 取值 */
let a = {...[1, 2], gg:3, ...{x:4, y:5}};
// {'0':1, '1':2, gg:3, x:4, y:5}

ES7提案 ‘::’简化bindapplycall

foo::bar;
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

::console.log  // 等同于 console::console.log


3、箭头函数

只相当于一个简单的 { } 块级代码段(选择性使用)。没有普通函数的能力:独立的 this 上下文(这有时候是坑的来源。箭头函数 bind 也无效)、arguments 参数、对调用栈的访问。不能用作Generator状态机。

let a = (x) => x+2;
// '=>' 左侧的参数若是一个,可简写为 let a = x => x+2;
// '=>' 右侧 x+2 是 {return x+2;} 的简写,{}中包含函数中所有代码

// 若返回对象,可({})返回。(x) => {return {id: x};} 可简写为 x => ({id: x})

var obj = {
    a: 1,
    b: function() {
   
        setTimeout( () => {
            this.a++;  // this 为 b 函数内 this,可以更简单的绑定环境
        }, 0);
    }
};

// 便捷易懂的管道
let f = (x=0) => (y=0) => ({
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ECMAScript 6规范总结(长文慎入) 的相关文章

随机推荐

  • CSS字体的单位

    长度单位 1 像素 px 实际上是屏幕上的一个个小点 100px 100个小点 这个点 正常情况我们是看不到 如果我们把一个内容放大很多倍 就可以看到了 在pc端 电脑端 一般情况下1px 1个发光点 也是我们最常用的长度单位 它是固定单位
  • “区块链”技术在传统行业中的应用

    点击上方 中兴开发者社区 关注我们 每天读一篇一线开发者原创好文 比特币可能是一场庞氏骗局 但区块链技术却真实存在 2013年以来 比特币受到了全世界投资者的狂热追捧 虽然几经涨跌 大部分国家监管方对其态度也不甚明朗 但作为比特币底层技术的
  • react hooks实现原理(useState为例)

    一 源代码 逻辑十分绕 建议多敲几遍 let isMount true 判断是挂载还是更新 let workInProgressHook App组件对应的fiber对象 const fiber memorizedState null 当前h
  • 【NLP】T5:文本到文本转换器

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 智能指针类HasPtr

    智能指针是存储指向动态分配对象指针的类 用于控制对象的生存期 能够确保自动正确的销毁动态分配的对象 防止内存泄露 HasPtr 在其他方面的行为与普通指针一样 具体而言 复制对象时 副本和原对象将指向同一基础对象 如果通过一个副本改变基础对
  • 微信小程序车牌号码输入(虚拟键盘)

    近日在网上看到一位博主写的微信小程序 输入车牌号 有新能源 原文链接 https blog csdn net qq706352062 article details 105554453 ops request misc 257B 2522r
  • ppt复现CVPR顶会流程图

    本次目标如下图 难点在于立方体和矩阵格网的绘制 文末附机器学习绘图模板 先来绘制立方体 插入 形状 立方体 调节成如下图 再点击水平翻转 绘制矩形 多绘制几个组合成矩形格网 右键设置形状和格式 输入以下参数 调整好使其贴合立方体 如图所示
  • 全志V3S环境编译开发流程

    这里使用的是荔枝派Zero 官网上面没有带spiflash的 首先准备一张SD卡 U BOOT 首先需要配置交叉编译环境 这里就不多说了 需要的话前往Sipeed官网 Sipeed 首先获取uboot源码 git clone https g
  • vscode快捷键:定位某一行,跳转到这一行

    快捷键 Ctrl G 然后在弹出的框中输入行数就可以了 参考 https blog csdn net cvper article details 81090028
  • Spring的三种注入方式:构造方法注入,set方法注入,注解注入

    本文演示三种值注入方式和三层模式下的注解注入获取对象 首先是搭建基本的Spring运行环境导入四个基本的核心jar包和两个日志包 在src根目录下新建bean xml的配置文件 同时引入dtd约束 一 构造函数注入 在bean xml中配置

  • 分割线样式

    hr style height 2px border none border top 2px ridge green
  • vue项目 后端传给base64格式图形验证码 ,前端进行解析,回显。

    我们在实际项目中时在登录的时候 时常会遇到图形验证码 来进行验证用户操作 什么是图形验证码 图形验证码是验证码的一种 有防止黑客对某一特定注册用户用程序暴力破解私人信息 恶意破解密码 刷论坛灌水的作用 票 图形验证码是一种区分用户是计算机还
  • SpringBoot底层原理

    SpringBoot底层原理 一 SpringBoot是什么 二 SpringBoot核心原理 三 springboot启动原理 一 SpringBoot是什么 想要了解springboot底层原理必须要先知道springboot是什么 作
  • 计算机视觉领域关注的会议和期刊

    原本为给师弟师妹总结的自己经验 节省计算机视觉领域大家看什么论文和去哪里看论文的困惑 一 会议论文 视觉的领域主要关注的三大顶会论文 CVPR ICCV ECCV 搜索途径 1 CVPR和ICCV都是IEEE库 可以在IEEE explor
  • QT中ui文件生成关联的C++类

    在VS2008中给对话框资源添加关联的C 类时 可通过右键菜单 添加类 直接添加关联的C 类 但QT中不支持这样的操作 在QT中在创建界面ui时 可手动也可自动创建ui文件关联的C 类 分别如下所示 一 自动创建ui文件和对应的C 类 项目
  • C语言开发网站

    在正式开发之前 先了解一下网站的原理 请求 处理 响应 在浏览器的network中可以看到浏览器和服务器的交互过程 请求一个网站的本质就是咱们的浏览器和服务器交互的一个过程 比如说咱们请求www baidu com 就是咱们的浏览器向服务器
  • lede 插件_路由器帮你签到!「LEDE/Openwrt系统“签到狗”插件使用教程」

    每日签到 废话不多说 用了才知道 图标 支持的站点 baidu 百度贴吧 百度文库v2ex V2EXhostloc hostloc comacfun A站bilibili B站163music 网易云音乐PCmiui 小米论坛52pojie
  • getline函数

    在我的印象中 getline函数经常出现在自己的视野里 模糊地记得它经常用来读取字符串 但是又对它的参数不是很了解 今天又用到了getline函数 现在来细细地总结一下 首先要明白设计getline函数的目的 其实很简单 就是从流中读取字符
  • 超强大JS表格:DataViewsJS 1.8.16.1407 Crack

    DataViewsJS完整的 JavaScript 数据呈现和数据网格平台 通过从各种不同的演示视图中进行选择 包括树 卡片 砖石 网格 时间线 甘特图 日历和网格 超越传统的表格显示 快速地 纯 JavaScript 针对速度进行了优化
  • ECMAScript 6规范总结(长文慎入)

    闲话 学习ES6的动机起于对其promise标准的好奇 它与jQuery源码中Deferred不同 而且在异步编程中加入了Generator 在后续ES7中更有Async 这勾起我强烈的兴趣了解ES6更多的内容 于是完整的学习了阮一峰老师的