ES 新特性与 TypeScript、JS 性能优化(Day05)

Part1 · JavaScript【深度剖析】

ES 新特性与 TypeScript、JS 性能优化

文章说明:本专栏内容为本人参加【拉钩大前端高新训练营】的学习笔记以及思考总结,学徒之心,仅为分享。如若有误,请在评论区支出,如果您觉得专栏内容还不错,请点赞、关注、评论。

共同进步!

上一篇:【JavaScript异步编程】、【手写promise】

一、ECMAScript概述

ECMAScript,即 ECMA-262 定义的语言,并不局限于 Web 浏览器。事实上,这门语言没有输入和输出之类的方法。ECMA-262 将这门语言作为一个基准来定义,以便在它之上再构建更稳健的脚本语言。

Web 浏览器只是 ECMAScript 实现可能存在的一种宿主环境(host environment)。宿主环境提供ECMAScript 的基准实现和与环境自身交互必需的扩展。扩展(比如 DOM)使用 ECMAScript 核心类型和语法,提供特定于环境的额外功能。其他宿主环境还有服务器端 JavaScript 平台 Node.js 和即将被淘汰的 Adobe Flash。

日常使用的Web环境下,JavaScript语言包括:ECMAScript、浏览器提供的BOM对象、DOM树。

Node.js环境下,JavaScript包括:ECMAScript、Node API(fs、net、etc.)

历史版本:

二、ES2015概述

ECMA-262 阐述了什么是 ECMAScript 符合性。要成为 ECMAScript 实现,必须满足下列条件:

  • 支持 ECMA-262 中描述的所有“类型、值、对象、属性、函数,以及程序语法与语义”;

  • 支持 Unicode 字符标准。

此外,符合性实现还可以满足下列要求。

  • 增加 ECMA-262 中未提及的“额外的类型、值、对象、属性和函数”。ECMA-262 所说的这些额外内容主要指规范中未给出的新对象或对象的新属性。

  • 支持 ECMA-262 中没有定义的“程序和正则表达式语法”(意思是允许修改和扩展内置的正则表达式特性)。

以上条件为实现开发者基于 ECMAScript 开发语言提供了极大的权限和灵活度,也是其广受欢迎的原因之一。

三、ES2015 let 与块级作用域

具体详情请阅读《JavaScript高级程序设计·第四版》中的第三章第三节(3.3)

1.var

  • var定义的变量会预解析,简单的说就是如果变量没有定义就直接使用的话,JavaScript回去解析这个变量,代码不会报错,只会输出undefined。
console.log(foo)  // undefined
var foo = 'foo';
  • var定义的变量可以反复去定义,当然后面的会覆盖前面的
var a = 1;
var a = 2;
  • var在循环中使用的时候,循环体外依然可以使用
for (var i = 0; i < 3; i++) {
	for (var i = 0; i < 3; i++) {
		console.log(i)
	}
	console.log('内层结束 i = ' + i)
}
//0
//1
//2
//内层结束 i = 3
  • 在循环绑定事件过程中,var定义的变量无法保存,循环会在瞬间执行完
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
	elements[i].onclick = (function (i) {
		return
	})
}
elements[1].onclick()

2.let

  • let定义的变量不会预解析,必须先声明再使用,否则会报错
console.log(b);  // 报错
let b = 'bar'
  • let不能定义已经定义过的变量(无论之前是用var定义的还是let或者const定义的)
let b = 3;
let b = 4;  // 报错
  • let是块级作用域,函数内部使用let定义后,对函数外部无影响,简单说就是在一个{}里面生效
for (let i = 0; i < 3; i++) {
	let i = 'foo';
	console.log(i)  // foo
}
  • 由于let是块级作用域,在循环绑定事件过程中let会在这个循环中生效,再次循环时let会重新定义生效
let elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
	elements[i].onclick = (function (i) {
		console.log(i)
	})
}
elements[0].onclick()

四、ES2015 const

  • const定义的变量不会预解析,必须先声明再使用,否则会报错
console.log(a);  // 报错 undefined
const a = 'foo'; 
  • const定义的变量不允许修改

    const a = 5;
    a = 6;  // err
    
    • 但是,在数组里面,const的值是允许被修改的,这是因为const存储的是地址,值的内容可以变化
    const arr = [1,2,3,4,5]
    arr[1] = 'array'
    console.log(arr);
    

五、ES2015 数组的解构

ECMAScript2015 新增了一种从数组或者对象获取指定元素的快捷方式,这是一种新的语法,这种新语法叫做解构。如下代码所示,定义一个数组:

const arr = [100, 200, 300]

const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)

在 ECMAScript2015 之前想要获取这个数组中的元素,需要通过索引访问对应的值,然后将访问的结果赋值给一个变量。

而在 ECMAScript2015 之后,可以通过数组的解构这种方式快速获取数组中的指定成员。如下代码所示:

const arr = [100, 200, 300]

const [foo, bar, baz] = arr
console.log(foo, bar, baz)

这里会根据变量的位置进行分配数组中对应位置的成员。如果只要获取某一个位置上的成员,比如上个数组中的最后一个位置的成员,只需要保留前两个占位就可以了。如下代码所示:

const arr = [100, 200, 300]
const [, , baz] = arr
console.log(baz)

除此之外,还可以在变量名前面增加 ... 来获取从当前位置到数组最后的所有成员。如下代码所示:

const arr = [100, 200, 300]

const [foo, ...rest] = arr
console.log(rest)  // [ 200, 300 ]

这里需要注意的是,这种解构的用法只能在成员变量的最后一个变量上才能使用。

如果解构的变量数量少于数组的成员数量的话,那会按照从前到后的顺序进行获取。如下代码所示:

const arr = [100, 200, 300]

const [foo] = arr
console.log(foo)  // 100

从打印的结果可以看到,数组中剩下的成员都不会被获取到。反之,如果解构的变量数量多于数组的成员数量的话,那多出来的变量的值为 undefined。如下代码所示:

const arr = [100, 200, 300]

const [foo, bar, baz, more] = arr
console.log(more)  // undefined

使用 ECMAScript2015 之后的解构将大大进行简化。如下代码所示:

const path = 'foo/bar/baz'

const [, rootDir] = path.split('/')
console.log(rootDir)

六、ES2015 对象的解构

在 ECMAScript2015 中,除了数组可以被解构之外,对象同样也可以被解构。只不过对象的解构,是需要通过属性名来获取,而不是位置。如下代码所示:

const obj = {
  name: '拉勾大前端',
  age: 3
}

const {
  name
} = obj
console.log(name)

上述代码的运行结果如下:

拉勾大前端

这里解构中的变量名还有一个很重要的作用,就是匹配解构对象中的成员,从而获取指定成员的值。比如上述代码结构总的 name 获取了 obj 对象中的 name 属性值。

因为对象的解构的这种特性,如果当前作用域中存在一个同名的变量,就会产生冲突。如下代码所示:

const obj = {
  name: '拉勾大前端',
  age: 3
}

const name = '拉勾大前端2'
const {
  name
} = obj
console.log(name)

上述代码的运行结果如下:

object-destructuring.js:13
  name
  ^

SyntaxError: Identifier 'name' has already been declared
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:599:28)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Function.Module.runMain (module.js:676:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3

因为 obj 对象的 name 属性必须在解构中定义 name 变量进行获取,那么这一冲突就无法避免。这个时候可以通过重命名的方式来解决这样的问题,如下代码所示:

const obj = {
  name: '拉勾大前端',
  age: 3
}

const name = '拉勾大前端'
const {
  name: objName
} = obj
console.log(objName)

如上述代码所示,解构中原本的 name 对应着 obj 对象的 name 属性,objName 是重命名的变量名,从而解决了同名的冲突。如下图所示:

今日分享截止到这里,明天继续更新后续部分:模板字符串、参数默认值、剩余参数等。

记录:2020/11/03

下一篇:模板字符串、参数默认值、剩余参数

匿名

发表评论

匿名网友