理解语言与平台之间的关系,系统化地学习ECMAJavaScript
内容概要
- ECMAScript 与 JavaScript
- ECMAScript 的发展过程
- ECMAScript 2015 的新特性
- And more...
ECMAScript 与 JavaScript
- ECMAScript是由Ecma国际标准组织以ECMA-262和ECMA-402规范的形式进行标准化的脚本程序设计语言
- ECMAScript是形成JavaScript语言基础的脚本语言
- 在浏览器中,JavaScript = ECMAScript + Web API
- 在nodejs中,JavaScript = ECMAScript + Nodejs API
ECMAScript 的发展过程
| 名称 | 标准版本 | 发⾏时间 |
|---|---|---|
| ECMAScript2019(ES2019) | 10 | 2019年6⽉ |
| ECMAScript2018(ES2018) | 9 | 2018年6⽉ |
| ECMAScript2017(ES2017) | 8 | 2017年6⽉ |
| ECMAScript2016(ES2016) | 7 | 2016年6⽉ |
| ECMAScript2015(ES2015) | 6 | 2015年6⽉ |
| ECMAScript5.1(ES5.1) | 5.1 | 2011年6⽉ |
| ECMAScript5(ES5) | 5 | 2009年12⽉ |
| ECMAScript4(ES4) | 4 | 被放弃 |
| ECMAScript3(ES3) | 3 | 1999年12⽉ |
| ECMAScript2(ES2) | 2 | 1998年6⽉ |
| ECMAScript1(ES1) | 1 | 1997年6⽉ |
ECMAScript 2015 的新特性
ECMAScript2015(ES2015)因为排在ES5.1之后,所以称为ES6。由于距离上一版本的发布相隔了4年之久,ES6相较上个版本发生很大的变化,我们来认识ES6的新特性。 • 对原有语法进⾏增强 • 解决原有语法上的⼀些问题或者缺陷 • 全新的对象、全新的⽅法、全新的功能 • 全新的数据类型和数据结构
let与块级作用域
ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
{
let a = 0;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
这里用let和var声明了两个变量a和b,然后在代码块之外调用a和b,变量a报错,变量b返回正确的值。 这表明,let声明的变量只在它所在的代码块有效。
var arr = [1,2,3,4,5,6];
for (var i = 0; i < arr.length; i++) {
setTimeout(function(){
console.log(arr[i])
}, 0)
}
// undefined undefined undefined undefined undefined undefined
相信大家也明白,var i 声明的变量作用域是全局的,for循环执行完了再执行定时器里面的代码,此时获取的i值已经被修改成6,我们就只输出6次arr[6]的, 而没有依次输出数组arr 的内容了。使用let 命令就可以很好解决这个问题。
var arr = [1,2,3,4,5,6];
for (let i = 0; i < arr.length; i++) {
setTimeout(function(){
console.log(arr[i])
}, 0)
}
// 1 2 3 4 5 6
ES6 的块级作用域
let实际上为 JavaScript 新增了块级作用域。
for (let i = 0; i < 3; i++) {
let i = '^_^';
console.log(i);
}
// ^_^
// ^_^
// ^_^
上面代码正确运行,输出了 3 次^_^。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。当然我们不建议样使用,因为容易引起变量名混乱。
不存在变量提升
var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象实在怪异,像是某种缺陷。 let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
// var 的情况
console.log(a); // 输出undefined
var a = 0;
// let 的情况
console.log(b); // Uncaught ReferenceError: b is not defined
let b = 2;
const命令
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const a = 0;
a = 1; // Uncaught TypeError: Assignment to constant variable
// 一开始为0,终生为0,怎么改也不行了
尝试改变const声明的变量会引起报错。 这就意味着,const 声明变量必须立即初始化。
const b // Uncaught SyntaxError: Missing initializer in const declaration
// 现在不给值,以后就没机会了,且行且珍惜啊!
所以正确的做法应该是,const 声明变量立即初始化值
const c = 1;
c // 1
// 一次为一,始终为一,做一个专一的好变量
const 变量的不可修改性,指的是不对变量进行二次赋值,但是可以给变量添加属性。
const obj = {};
obj.name = 'Tom';
obj.age = 20;
obj // {name: "Tom", age: 20}
// 变量还是那个变量,只要指向的内存地址不变,要加点成员还是可以的^.^
除了不可修改,const 与let 用法大体相同,比如const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const MIN = 18;
}
MIN // Uncaught ReferenceError: MIN is not defined
// 花括号{ }围起来的是别人家的地盘了,别人家的东西不能随便拿o_o
数组的解构
ES6中可以从数组中提取值,按照对应位置,对变量赋值。
const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at ] = date_range;
start_at // '2020-10-23'
end_at // '2021-03-23'
// 开始时间-结束时间,一前一后,非常直观
如果解构不成功,变量的值就等于undefined。
const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at ] = date_range;
start_at // '2020-10-23'
end_at // '2021-03-23'
work_at // undefined
// 解构超过了数组的长度,所以没有获取第三个元素的值,work_at为undefined
解构赋值允许指定默认值
const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at = '2021-05-01'] = date_range;
start_at // '2020-10-23'
end_at // '2021-03-23'
work_at // '2021-05-01'
// 没有第三个元素,work_at赋值为默认值'2021-05-01'
对象的解构赋值
除了数组,解构还可以用于对象。
const obj = {name: "Tom", age: 20};
const { name, age } = obj;
name // "Tom"
age // 20
当然,对象里面的数组也是可以一起解构的.
const obj = {name: "Tom", age: 20, date_range: ['2020-10-23','2021-03-23']};
const { name, age, date_range: [start_at, end_at ] } = obj;
name // "Tom"
age // 20
start_at // '2020-10-23'
end_at // '2021-03-23'
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at = '2021-05-01'] = date_range;
// 普通字符串
`This course start at 2020`
// 多行字符串
`This course start at
2020`
// 字符串中嵌入变量
`This course start at ${start_at}, and end at ${end_at}`
模板字符串还有一个更高级的用法,就是在字符串前面添加一个函数,可以获取字符串片段和嵌入的变量,返回值是模板字符串的结果。
const date_range = ['2020-10-23','2021-03-23'];
const [ start_at, end_at, work_at = '2021-05-01'] = date_range;
function myTagFunc(strings, a, b){
console.log(strings, a, b)
return a+b
}
// 字符串中嵌入变量
const str = myTagFunc`This course start at ${start_at}, and end at ${end_at}`
// ["This course start at ", ", and end at ", "", raw: Array(3)] "2020-10-23" "2021-03-23"
str // '2020-10-232021-03-23'
字符串的扩展方法
- includes 判断当前字符串是否包含指定的字符串
- startsWith 判断当前字符串是否以指定的字符串开始
- endsWith 判断当前字符串是否以指定的字符串结束
const str = `hello world`;
str.includes('hello') // true
str.includes('apple') // false
str.startsWith('hello') // true
str.startsWith('apple') // false
str.endsWith('world') // true
str.endsWith('hello') // false
函数的扩展
函数参数的默认值
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'Shenzhen') // Hello Shenzhen
log('Hello', '') // Hello
参数默认值可以与解构赋值的默认值,结合起来使用。
function foo({x, y = 0}) {
console.log(x, y);
}
foo({}) // undefined 0
foo({x: 1}) // 1 0
foo({x: 1, y: 2}) // 1 2
rest 参数
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(1, 2, 3) // 6
箭头函数
ES6 允许使用“箭头”(=>)定义函数。
const f = data => data;
// 等同于
const f = function (data) {
return data;
};
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
const f = () => 100;
// 等同于
const f = function () {
return 100;
};
const sum = (a, b) => a + b;
// 等同于
const sum = function(a, b) {
return a + b;
}
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
const sum = (num1, num2) => {
console.log(num1, num2);
return num1 + num2;
}
箭头函数与rest 参数一起使用:
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]
箭头函数有几个使用注意点。
- 函数体内的
this对象,就是定义时所在的对象,而不是使用时所在的对象。 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。不可以使用yield命令,因此箭头函数不能用作Generator函数。
对象的扩展
属性的简洁表示法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
const name = 'Tom';
const age = 20;
const obj = { name, age }
// 等同于
const obj = { name: name, age: age }
属性名表达式
ES6 允许字面量定义对象时,表达式作为对象的属性名,即把表达式放在方括号内。
const obj = {
['date'+'_'+'range']: ['2020-10-23','2021-03-23']
}
方法的 name 属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
const obj = {
name: 'Tom',
sayName(){
console.log(this.name);
}
}