一.简介

什么是ECMAScript?

        ECMAScript是由网景的布兰登·艾奇开发的一种脚本语言的标准化规范;最初命名为Mocha,后来改名为LiveScript,最后重命名为JavaScript。1995年12月,升阳与网景联合发表了JavaScript。1996年11月,网景公司将JavaScript提交给欧洲计算机制造商协会进行标准化。ECMA-262的第一个版本于1997年6月被Ecma组织采纳。ECMA Script是ECMA-262标准化的脚本语言的名称。尽管JavaScript和JScript与ECMAScript兼容,但包含超出ECMA Script的功能。

        ECMAScript是一种可以在宿主环境中执行计算并能操作可计算对象的基于对象的程序设计语言。ECMAScript最先被设计成一种Web脚本语言,用来支持Web页面的动态表现以及为基于Web的客户机—服务器架构提供服务器端的计算能力。但作为一种脚本语言, ECMAScript具备同其他脚本语言一样的性质,即“用来操纵、定制一个已存在系统所提供的功能,以及对其进行自动化”。 

        发展历程:1998年6月,ECMAScript 2.0版发布。1999年12月,ECMAScript 3.0版发布。2007年10月,ECMAScript 4.0版草案发布。2009年12月,ECMAScript 5.0版正式发布。2011年6月,ECMAscript 5.1版发布。2013年3月,ECMAScript 6草案冻结,不再添加新功能。新的功能设想将被放到ECMAScript 7。2013年12月,ECMAScript 6草案发布。然后是12个月的讨论期,听取各方反馈。2015年6月17日,ECMAScript 6发布正式版本。........

ECMAScript 和 JavaScript 的关系?

        1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。

        该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

        因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。

二.ECMAScript各版本新增的特性

  • ECMAScript 1

        本质上与javascript1.1相同。

  • ECMAScript 2

        没有什么特别明显的改变

  • ECMAScript 3

        新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。

  • ECMAScript 4

        于2008年7月发布前被废弃。

  • ECMAScript 5

        新增了原生JSON对象、继承的方法、高级属性的定义以及引入严格模式

       1.ECMAScript 5 Object的新属性方法

        /* 
            Object.create(,prototype[descriptors])
            这个方法用于创建一个对象,并把其prototype属性赋值为第一个参数,同时可以设置多个descriptors,
            关于decriptor下一个方法就会介绍这里先不说。只需要这样就可以创建一个原型链干净对象了
        */
        var o = Object.create({
            "say": function () {
                alert(this.name);
            },
            "name": "Byron"
        });
        console.log('o:', o);

        /* 
            Object.getOwnPropertyDescriptor(O,property)
            这个方法用于获取defineProperty方法设置的property 特性
        */
        var props = Object.getOwnPropertyDescriptor(o, 'age');
        console.log(props); //Object {value: 24, writable: true, enumerable: true, configurable: true}
        
        /* 
            Object.defineProperty(O,Prop,descriptor) / Object.defineProperties(O,descriptors)
        */
        Object.defineProperty(o,'age', {
            value: 24,
            writable: true,
            enumerable: true,
            configurable: true
        });

        Object.defineProperty(o, 'sex', {
            value: 'male',
            writable: false,
            enumerable: false,
            configurable: false
        });

        console.log(o.age); //24
        o.age = 25;

        for (var obj in o) {
            console.log(obj + ' : ' + o[obj]);
            /*
            age : 25  //没有把sex : male 遍历出来
            say : function () {
                alert(this.name);
            } 
            name : Byron 
            */
        }
        delete o.age;
        console.log(o.age);//undefined 

        console.log(o.sex); //male
        //o.sex = 'female'; //Cannot assign to read only property 'sex' of #<Object> 
        delete o.age; 
        console.log(o.sex); //male ,并没有被删除

        /* 
            Object.getOwnPropertyNames:获取所有的属性名,不包括prototy中的属性,返回一个数组
        */
        console.log(Object.getOwnPropertyNames(o)); //["age", "sex"]

        /* 
            Object.keys():和getOwnPropertyNames方法类似,但是获取所有的可枚举的属性,返回一个数组
        */
        console.log(Object.keys(o)); //["age"]

        /* 
            Object.preventExtensions(O) / Object.isExtensible:方法用于锁住对象属性,使其不能够拓展,
            也就是不能增加新的属性,但是属性的值仍然可以更改,也可以把属性删除,Object.isExtensible用于
            判断对象是否可以被拓展
        */
        console.log(Object.isExtensible(o)); //true
        o.lastName = 'Sun';
        console.log(o.lastName); //Sun ,此时对象可以拓展

        Object.preventExtensions(o);
        console.log(Object.isExtensible(o)); //false

        o.lastName = "ByronSun";
        console.log(o.lastName); //ByronSun,属性值仍然可以修改

        //delete o.lastName;
        console.log(o.lastName); //undefined仍可删除属性
        o.firstname = 'Byron'; //Can't add property firstname, object is not extensible 不能够添加属性
        
        /* 
            Object.seal(O) / Object.isSealed:方法用于把对象密封,也就是让对象既不可以拓展也
            不可以删除属性(把每个属性的configurable设为false),单数属性值仍然可以修改,Object.isSealed
            由于判断对象是否被密封
        */
        Object.seal(o);
        o.age = 25; //仍然可以修改
        delete o.age; //Cannot delete property 'age' of #<Object>
        
       /* 
            Object.freeze(O) / Object.isFrozen:终极神器,完全冻结对象,在seal的基础上,属性值也不可以修改
            (每个属性的wirtable也被设为false)
       */
        Object.freeze(o);
        o.age = 25; //Cannot assign to read only property 'age' of #<Object>

   2.ECMAScript 5 Array的新属性方法

  Array.isArray(element):

        用来判断一个对象是不是数组。

        var a = new Array(123);
        var b = new Date();
        console.log(Array.isArray(a)); //true
        console.log(Array.isArray(b)); //false
  Array.indexOf(element) / Array.lastIndexOf(element):

        这两个方法用于查找数组内指定元素位置,查找到第一个后返回其索引,没有查找到返回-1,indexOf从头至尾搜索,lastIndexOf反向搜索。

        var a=new Array(1,2,3,3,2,1);
        console.log(a.indexOf(2)); //1
        console.log(a.lastIndexOf(2)); //4
  forEach(element,index,array) :

        遍历数组,参数为一个回调函数,回调函数有三个参数:当前元素,元素索引,整个数组

        var a=new Array(1,2,3,4,5,6);
        a.forEach(function(e,i,array){
            array[i]=e+1;
        });
        console.log(a); //[2, 3, 4, 5, 6, 7]
 Array.every(function(element,index,array)) / Array.some(function(element,index,array)) :

       这两个函数类似于离散数学中的逻辑判定,回调函数返回一个布尔值,every是“所有”函数的每个回调函数都返回true的时候才会返回true,当遇到false的时候终止执行,返回false;some函数是“存在”有一个回调函数返回true的时候终止执行并返回true,否则返回false。在空数组上调用every返回true,some返回false。

        var a=new Array(1,2,3,4,5,6);
        /*0 : 1 
        1 : 2
        2 : 3
        3 : 4 
        4 : 5 
        false */
        console.log(a.every(function(e,i,arr){
            console.log(i+' : '+e);
            return e<5;
        }));
        var a=new Array(1,2,3,4,5,6);
        /*0 : 1 
        1 : 2
        2 : 3
        3 : 4 
        4 : 5 
        true */
        console.log(a.some(function(e,i,arr){
            console.log(i+' : '+e);
            return e>4;
        }));
map(function(element)) :

        与forEach类似,遍历数组,回调函数返回值组成一个新数组返回,新数组索引结构和原数组一致,原数组不变。

        var a=new Array(1,2,3,4,5,6);
        console.log(a.map(function(e){
            return e*e;
            })); // [1, 4, 9, 16, 25, 36] 
        console.log(a); //[1, 2, 3, 4, 5, 6]
Array.filter(function(element)) :

        返回数组的一个子集,回调函数用于逻辑判断是否返回,返回true则把当前元素加入到返回数组中,false则不加,新数组只包含返回true的值,索引缺失的不包括,原数组保持不变。过滤数组

        var a=new Array(1,2,3,4,5,6);
        console.log(a.filter(function(e){
            return e%2==0;
        })); // [2, 4, 6] 
        console.log(a); //[1, 2, 3, 4, 5, 6]
Array.reduce(function(v1,v2),value) / .reduceRight(function(v1,v2),value) : 

        遍历数组,调用回调函数,将数组元素组合成一个值,reduce从索引最小值开始,reduceRight反向,方法有两个参数

1.回调函数:把两个值合为一个,返回结果

2.value,一个初始值,可选

            var a=new Array(1,2,3,4,5,6);
            console.log(a.reduce(function(v1,v2){
            return v1+v2;
            })); // 21

            console.log(a.reduceRight(function(v1,v2){
                return v1-v2;
            },100)); // 79

 3.ECMAScript 5 的其他新特性

String.prototype.trim():

        这是字符串的一个实例方法,用于去除字符串首尾的空白符(不只是空格,还有tab、垂直制表符啊神马的)和换行符,终于不用自己用正则表达式写了,这个方法返回trim后结果,不改变原字符串值。

        var s = '    123 \n';
        console.log(s.length);//6
        console.log(s.trim());//123
        console.log(s.trim().length);//3
        console.log(s); //    123 
        console.log(s.length); //6

Function.prototype.bind(thisArg,[,arg1[,arg2,…]]) :

        Function.prototype.bind返回一个把内部this设为thisArg的方法,读起来很绕,其实就是,返回一个新方法,这个方法内部的this是参数thisArg

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="test">Click Here</div>
    <script>
        var handler = {
            message: 'This is a test',
            click: function () {
                alert(this.message);
            }
        };
        document.getElementById('test').onclick = handler.click;
    </script>
</body>

</html>

        如果这样绑定div的click事件处理程序,大家都会看出来,点击的时候弹出来的对话框内容是undefined,因为执行的时候this是window,我们需要一定的技巧才可以处理此事,达到预期效果,但是使用新添的bind我们可以轻松改变this。

document.getElementById('test').onclick = handler.click.bind(handler);

这样就可以达到我们预期效果了。

JSON.parse(text [,reviver]) 

        JSON.parse用于反序列化json格式字符串为对象,第二个参数是可选的,是一个有key和value两个参数的函数,用于过滤或者处理返回值。

var jsonString = '{"name":"Byron", "age":24}';
var jsonObj = JSON.parse(jsonString);
console.log(typeof jsonObj); //Object
console.log(jsonObj.name); //Byron
JSON.stringify(value [, replacer [, space]])

        JSON.stringify方法用于序列化json对象为字符串,最简单的就是传入一个json对象。

        var jsonObj = {
            "name": "Byron",
            "BirthDate": new Date('06/15/1989')
        };
        var jsonStr=JSON.stringify(jsonObj);
        console.log(jsonStr); // {"name":"Byron","BirthDate":"1989-06-14T16:00:00.000Z"}
Date.prototype.toJSON() 

        从Date类型转成json的方法,序列化的时候用。

Date.now()

        获取当前的时间戳

  • ECMAScript 6

1.类(class)

        ES6 引入了 class(类),让 JavaScript 的面向对象编程变得更加简单和易于理解。

class Student {
  constructor() {
    console.log("I'm a student.");
  }
 
  study() {
    console.log('study!');
  }
 
  static read() {
    console.log("Reading Now.");
  }
}
 
console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."

2.模块化

        ES5 支持原生的模块化,在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和 import 组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过 import 来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。

 3.箭头函数

       箭头函this指向它的父级,本身没有this。能帮你很好的解决this的指向问题。

4.模板字符串

var first = 10,
last = 01,
var name = `章节123 ${first} ${last}.`

5.解构赋值

        解构赋值语法是 JavaScript 的一种表达式,可以方便的从数组或者对象中快速提取值赋给定义的变量。

// 对象
const student = {
    name: 'Sam',
    age: 22,
    sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];

// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);

// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);

6.拓展运算符

        拓展运算符可以在函数调用/数组构造时, 将数组表达式或者 string 在语法层面展开;还可以在构造对象时, 将对象表达式按 key-value 的方式展开。

//在函数调用时使用延展操作符
function sum(x, y, z) {
  return x + y + z
}
const numbers = [1, 2, 3]
console.log(sum(...numbers))

//数组
const stuendts = ['Jine', 'Tom']
const persons = ['Tony', ...stuendts, 'Aaron', 'Anna']
conslog.log(persions)

7.Promise

        Promise 是异步编程的一种解决方案,比传统的解决方案 callback(回调地狱) 更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

8.let 与 const

        let:块级({})作用域声明变量,const是声明常量的,var是声明全局变量的。

9.Generator 函数

        Generator 函数是 ES6 提供的一种异步编程解决方案

  • ES7新增特性

1.Array.prototype.includes()

        includes() 函数用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false。

[1, 2, 3].includes(-1)                   // false
[1, 2, 3].includes(1)                    // true
[1, 2, 3].includes(3, 4)                 // false
[1, 2, 3].includes(3, 3)                 // false
[1, 2, NaN].includes(NaN)                // true
['foo', 'bar', 'quux'].includes('foo')   // true
['foo', 'bar', 'quux'].includes('norf')  // false 

2.指数操作符

        在 ES7 中引入了指数运算符 **, **具有与 Math.pow(..)等效的计算结果。使用指数运算符 **,就像 +、- 等操作符一样。

//之前的版本
Math.pow(5, 2)

// ES7
5 ** 2
// 5 ** 2 === 5 * 5 
  • ES8新增特性

1.async/await

        继Promise后解决异步的顶。ES6Generator 函数的语法糖。

const resolveAfter3Seconds = function() {
  console.log('starting 3 second promsise')
  return new Promise(resolve => {
    setTimeout(function() {
      resolve(3)
      console.log('done in 3 seconds')  
    }, 3000)  
  })  
}

const resolveAfter1Second = function() {
  console.log('starting 1 second promise')
  return new Promise(resolve => {
      setTimeout(function() {
        resolve(1) 
        console.log('done, in 1 second') 
      }, 1000)
  })  
}

const sequentialStart = async function() {
  console.log('***SEQUENTIAL START***')
  const one = await resolveAfter1Second()
  const three = await resolveAfter3Seconds()

  console.log(one)
  console.log(three)
}

sequentialStart(); 

2.Object.values()

        Object.values()是一个与 Object.keys()类似的新函数,但返回的是 Object 自身属性的所有值,不包括继承的值。

const obj = { a: 1, b: 2, c: 3 }
//不使用 Object.values()
const vals = Object.keys(obj).map((key) => obj[key])
console.log(vals)
//使用 Object.values()
const values = Object.values(obj1)
console.log(values) 
  • ES9新增特性

1.async iterators

        ES9 引入异步迭代器(asynchronous iterators), await可以和 for...of循环一起使用,以串行的方式运行异步操作。

//如果在 async/await中使用循环中去调用异步函数,则不会正常执行
async function demo(arr) {
  for (let i of arr) {
    await handleDo(i);
  }
}

//ES9
async function demo(arr) {
  for await (let i of arr) {
    handleDo(i);
  }
}

2.Rest/Spread属性

        Rest:对象解构赋值的其余属性。

        Spread:对象解构赋值的传播属性。

//Rest
let { fname, lname, ...rest } = { fname: "Hemanth", lname: "HM", location: "Earth", type: "Human" };
fname; //"Hemanth"
lname; //"HM"
rest; // {location: "Earth", type: "Human"}

//Spread
let info = {fname, lname, ...rest};
info; // { fname: "Hemanth", lname: "HM", location: "Earth", type: "Human" }
  • ES10新增特性

Array的 flat()方法和 flatMap()方法

        flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

        flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap通常在合并成一种方法的效率稍微高一些。

let arr = ['a', 'b', ['c', 'd']];
let flattened = arr.flat();

console.log(flattened);    // => ["a", "b", "c", "d"]

arr = ['a', , , 'b', ['c', 'd']];
flattened = arr.flat();

console.log(flattened);    // => ["a", "b", "c", "d"]

arr = [10, [20, [30]]];

console.log(arr.flat());     // => [10, 20, [30]]
console.log(arr.flat(1));    // => [10, 20, [30]]
console.log(arr.flat(2));    // => [10, 20, 30]
console.log(arr.flat(Infinity));    // => [10, 20, 30]
  • ES11新增特性

1.Promise.allSettled

        Promise.all最大问题就是如果其中某个任务出现异常(reject),所有任务都会挂掉,Promise 直接进入 reject 状态。

        Promise.allSettled在并发任务中,无论一个任务正常或者异常,都会返回对应的的状态(fulfilled 或者 rejected)与结果(业务 value 或者 拒因 reason),在 then 里面通过 filter 来过滤出想要的业务逻辑结果,这就能最大限度的保障业务当前状态的可访问性。

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));

// expected output:
// "fulfilled"
// "rejected"

2.String.prototype.matchAll

        matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。 在 matchAll出现之前,通过在循环中调用 regexp.exec来获取所有匹配项信息(regexp需使用 /g 标志)。如果使用 matchAll,就可以不必使用 while 循环加 exec 方式(且正则表达式需使用/g标志)。使用 matchAll会得到一个迭代器的返回值,配合 for...of, array spread, or Array.from() 可以更方便实现功能。

const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';

const array = [...str.matchAll(regexp)];

console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]

console.log(array[1]);
// expected output: Array ["test2", "e", "st2", "2"]
  • ES12新增特性

1.逻辑运算符和赋值表达式

a ||= b
//等价于
a = a || (a = b)

a &&= b
//等价于
a = a && (a = b)

a ??= b
//等价于
a = a ?? (a = b)

2.replaceAll

const str = 'hello world';
str.replaceAll('l', ''); // "heo word"

  三.参考文档

        真正常用的也就es6多一点,大家可以参考文档:ES6 入门教程【知识点】:ECMAScript简介及特性-LMLPHPhttps://es6.ruanyifeng.com/#docs/destructuring大家评论区一起学习交流

01-08 09:04