???? 如果想阅读最新的文章,或者有技术问题需要交流和沟通,可搜索并关注小红书和微信公众号“希望睿智”。
概述
作为一名软件工程师,不管你是不是前端开发的岗位,工作中或多或少都会用到一点JavaScript。JavaScript是大家所了解的语言名称,但是这个语言名称是Oracle公司注册的商标。JavaScript的正式名称是ECMAScript。1996年11月,JavaScript的创造者网景公司将JS提交给国际化标准组织ECMA(欧洲计算机制造联合会),希望这种语言能够成为国际标准。随后,ECMA发布了规定浏览器脚本语言的标准,即ECMAScript,这也有利于这门语言的开放和中立。
ES6,全称ECMAScript 6.0,正式名称为ECMAScript 2015,是JavaScript的下一个版本标准,于2015年6月17日发布。
下面我们了解下ECMAScript的发展历程。
1997年,ECMAScript 1.0诞生。
1998年6月,ECMAScript 2.0诞生,包含一些小的更改,用于同步独立的ISO国际标准。
1999年12月,ECMAScript 3.0诞生,在业界得到了广泛的支持,它奠定了JS的基本语法。
2000年的ECMAScript 4.0是ES6的前身,但由于这个版本太过激烈,所以暂时被“和谐”了。
2009年12月,ECMAScript 5.0版正式发布。ECMA专家组预计ECMAScript的第五个版本会在2013年中期到2018年作为主流的开发标准。2011年6月,ES 5.1版发布,并且成为ISO国际标准。
2013年,ES6草案被冻结,不再添加新的功能,新的功能将被放到ES7中。
2015年6月,ES6正式通过,成为国际标准。
JavaScript ES6提出了很多新的特性,它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
let关键字
let用来声明局部变量,是块级作用域。函数内部使用let定义后,对函数外部无影响,而且有暂时性死区的约束。
{
let i = 9;
}
// 提示找不到i的错误
console.log(i);
let a = 99;
f();
// 输出:99
console.log(a);
function f() {
// 提示找不到i的错误
console.log(a);
let a = 10;
// 输出:10
console.log(a);
}
const关键字
const用来声明常量,用它定义的变量不可以修改,而且必须初始化。常量的值不能通过重新赋值来改变,并且不能重新声明。
const foo = {};
foo.prop = 123;
// 其属性可以修改,正常输出:123
console.log(foo.prop)
// 报错,const变量自身不可修改
foo = {};
export/import
1、export default向外暴露的成员可以使用任意的变量来接收。在一个模块中,只能使用export default向外暴露一次。
2、使用export向外暴露的成员只能使用 {} 接收,这种情况叫按需导出。一个模块中可以同时使用多个 export。如果想在引用时改变名称,可以通过as。
// test.js
export default {
name: 'zs',
age: 10
};
export var title = "小星星";
//main.js
import m1, { title } from './test'
// test2.js
export var title = "小星星";
export var content = '哈哈哈';
// main2.js
import { title, content } from './test2'
import { title as title123, content } from './test2'
import * as test from './test2'
箭头函数
箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式:第一种只包含一个表达式,连{ ... }和return都省略掉了;第二种可以包含多条语句,这时候就不能省略{ ... }和return。
() => 3.14
var double = num => num * 2
(x, y) => x * x + y * y
和一般的函数不同,箭头函数不会绑定this,或者说箭头函数不会改变this本来的绑定。箭头函数内部的this是词法作用域,由上下文确定。
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth;
return fn();
}
};
模板字符串
模版字符串,用`(反引号)标识,用${}将变量括起来。模板字符串中可以引入变量、反引号自身、js表达式,还能调用函数。如果使用模版字符串表示多行字符串,所有的空格和缩进都会被保存在输出中。
`He is <b>${person.name}</b>and we wish to know his${person.age}.that is all`
console.log( `No matter\` what you do,
I trust you.`);
var x=88;
var y=100;
console.log(`x=${++x},y=${x+y}`);
function test() { return “I like ES6"; }
console.log(`hello, ${test()}`);
默认参数函数
默认参数函数,支持为函数的参数提供默认值,默认参数必须在所有参数的最后面。
function greet(name = 'Student', greeting = 'Welcome') {
return `${greeting} ${name}!`;
}
greet();
greet('James');
greet('Richard', 'Howdy');
解构
解构,用于从数组和对象中提取值并赋值给独特的变量。
const point = [10, 25, -34];
const [x, y, z] = point;
console.log(x, y, z);
const gemstone = {
type: 'quartz',
color: 'rose',
karat: 21.29
};
const {type, color, karat} = gemstone;
const {type2, color2, karat2} = gemstone;
console.log(type, color, karat);
...展开运算符
...,用于将数组序列化,成为逗号隔开的序列。
// 1、获取数组最大的值。
Math.max(...[1, 2, 3])
// 2、调用方法
function sum(a, b){
console.log(a+b)
}
sum(...[2, 3])
// 3、连接数组
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);
// 4、连接数组
var arr1 = ['a', 'b'];
var arr2 = ['c'];
[...arr1, ...arr2]
// 5、字符串转为真正的数组
[...'hello'] // [ "h", "e", "l", "l", "o" ]
// 6、扩展运算法
let map = new Map([ [1, 'one'], [2, 'two'] ]);
let arr = [...map.keys()];
...剩余参数/可变参数
剩余参数,也用三个连续的点( ... )表示,可以将多个不定数量的元素绑定到一个数组中。
可变参数,用于参数不固定的函数,ES6之前使用参数对象(arguments)处理。
// 剩余参数
const order = [20.17, 18.67, 1.50, "cheese", "eggs", "milk", "bread"];
const [total, subtotal, tax, ...items] = order;
console.log(total, subtotal, tax, items);
// 可变参数
function sum(...nums) {
let total = 0;
for(const num of nums) {
total += num;
}
return total;
}
更简单的对象字面量
ES6使得声明对象字面量更加简单,提供了属性简写和方法简写功能,属性名计算的新特性。
function getInfo(name, age, weight) {
return {
name, age, weight,
['over' + weight]: true,
desc() {
console.log(name, age, weight);
}
};
}
let person = getInfo('Kia', 27, 400);
console.log(person); // {name: "Kia", age: 27, weight: 400, over400: true, desc: ƒ}
for of
for of支持终止循环,也不会遍历非数字属性。
const fruits = ['apple','coconut','mango'];
fruits.fav = 'my favorite fruit';
for(let index in fruits){
console.log(fruits[index]);
}
for(let fruit of fruits){
if(fruit === 'mango' ){
break;
}
console.log(fruit);
}
Map
javascript的Object本身就是键值对的数据结构,但实际上属性和值构成的是”字符串-值“对,属性只能是字符串。Map提供了”值-值“对的数据结构,键名不仅可以是字符串,也可以是对象。它是一个更完善的Hash结构。
const map1 = new Map();
const objkey = {p1: 'v1'};
map1.set(objkey, 'hello');
console.log(map1.get(objkey));
console.log('%s', map1.size);
const map2 = new Map();
map2.set('a', 4);
console.log('map2: %s', map2.has('a'));
map2.delete('a');
map2.clear();
for (let key of map2.keys()) {
console.log(key);
}
for (let value of map2.values()) {
console.log(value);
}
for (let item of map2.entries()) {
console.log(item[0], item[1]);
}
for (let [key, value] of map2.entries()) {
console.log(key, value);
}
类
ES6中的类,与C++、Java中的类有点类似。
1、声明一个类,首先编写class关键字,紧跟着是类的名字,其他部分的语法类似于对象字面量方法的简写形式,但是不需要在子类的各元素之间使用逗号分隔。
2、constructor方法是构造方法,在实例化一个类的时候被调用。constructor方法是必须的,也是唯一的,一个类不能含有多个constructor方法,我们可以在该方法里面自定义一些对象的属性。
3、在类中可以定义类的实例方法,实例化后对象可以调用此方法。
4、静态方法的定义需要使用static关键字来标识,而实例方法不需要。静态方法通过类名来调用,而实例方法通过实例对象来调用。
5、静态属性写在类定义外面( ES7有新的写法,直接用static写在类定义里面即可)。
6、必须使用new创建字来创建类的实例对象。
7、必须先声明定义类,再创建实例,否则会报错。
class Animal {
constructor(name){
this.name = name;
}
getName(){
return 'This is a '+this.name;
}
static friends(a1,a2){
return `${a1.name} and ${a2.name} are friends`;
}
}
let dog = new Animal('dog');
console.log(dog.name);
console.log(dog.getName());
let cat = new Animal('cat');
Animal.friends(dog, cat);
继承
1、使用extends关键字来实现子类(派生类)继承父类(基类)。派生类中的方法总会覆盖基类中的同名方法。
2、关键字super,相当于是父类中的this。可以使用super来引用父类,访问父类的方法。
3、子类必须在constructor方法中调用super方法。
4、子类的构造函数中,必须先调用super方法,才可以使用this,否则报错。
5、在子类的普通函数(非构造函数)中访问父类的函数,可以使用this,也可以使用super。
6、在子类中访问父类的属性,只能使用this,不能使用super。
7、支持内建对象的继承。
class Animal {
constructor(name){
this.name = name;
}
say(){
return `This is a animal`;
}
}
class Dog extends Animal {
constructor(name, color){
super(name);
this.color = color;
}
getDesc(){
return `${super.say()},
name:${this.name},
color:${this.color}`;
}
}
let dog = new Dog("dog", "black");
dog.getDesc();
Web Worker
Web Worker的意义在于可以将一些耗时的数据处理操作从主线程中剥离,使主线程更加专注于页面渲染和用户交互。
// main.js
let worker = new Worker('work.js');
worker.postMessage('Hello World');
worker.postMessage({method: 'echo', args: ['Work']});
worker.onmessage = function (event) {
console.log('Recv msg ' + event.data);
}
worker.terminate();
// work.js
importScripts('script1.js');
self.onmessage = function (event) {
console.log('You said: ' + event.data);
}
self.close();
Promise
根据Promise/A规范,promise是一个对象,只需要then这一个方法。then方法带有如下三个参数:成功回调、失败回调、前进回调(规范没有要求包括前进回调的实现,但是很多都实现了)。
一个全新的promise对象从每个then的调用中返回。
Promise对象代表一个异步操作,其不受外界影响,有三种状态:Pending(进行中、未完成的)、Resolved(已完成,又称Fulfilled)、Rejected(已失败)。
function getNumber(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*10);
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
return p;
}
getNumber()
.then(
function(data){
console.log('resolved');
console.log(somedata);
},
function(reason, data){
console.log('rejected');
})
.catch(function(reason){
console.log('rejected');
});
async/await
1、async/await是ES7提出的语法,可通过babel在项目中使用。
2、async/await是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)。
3、async/await更加语义化,async 是“异步”的简写,async function用于申明一个function是异步的;await,可以认为是async wait的简写,用于等待一个异步方法执行完成。
4、可以通过多层async function的同步写法代替传统的callback嵌套。
5、async自动将常规函数转换成Promise,返回值也是一个Promise对象。
6、只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
7、只有async函数内部才可以使用await,在普通函数内使用会报错。
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms);
});
}
async function asyncPrint(ms) {
try {
console.log('start');
await timeout(ms);
console.log('end');
} catch(err) {
console.log(err);
}
}
asyncPrint(1000);
async function asyncPrint2(ms) {
console.log('start');
await timeout(ms)
console.log('end');
}
asyncPrint2(1000).catch(err => {
console.log(err);
});