欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ES6新特性

程序员文章站 2022-05-18 21:12:28
从ES6开始,JavaScript就朝着工程化和面向对象的大步迈进,我们并不知道这对于年轻的JavaScript来说是好还是坏,因为它最开始是做为一款轻量级的脚本语言而风靡全球的。这个问题就留给时间来证明吧! ......

ecmascript 6 是ecma于2015.06发布的版本,作为一个分界点,现在我们通常把这之后的版本统称为es6。es6带来了许多全新的语法,同时添加了类的概念,可以预见的是,javascript正朝着工程化语言迈进,我们并不知道这对于年轻的javascript来说是好还是坏,因为它最开始是做为一款轻量级的脚本语言而风靡全球的。

 

一  新的原始类型和变量申明

  1,symbol

  在es6之前,我们知道javascript支持6种数据类型:object,string,boolean,number,null,undefined。现在,es6新增了一种原始数据类型:symbol,表示独一无二的值,即每个symbol类型的值都不相同。这让我想起了另一个特殊的值:nan,想一想,他们是不是有一点类似呢!

1 var sy = symbol('test');
2 var sy1 = symbol('test');
3 console.log(tepeof sy);//'symbol'
4 sy == sy1;//false
5 var sy2 = new symbol('test');//error : symbol is not a constructor

  创建symbol数据类型的值时,需要给symbol函数传递一个字符串,并且有一点特殊的是:不能使用new关键字调用它。另外,每个symbol类型值都是独一无二的,即使传递的是相同的字符串。

  2,let和const

  es6新增了两个申明变量的关键字:let和const。他们申明的变量仅在let和const关键字所在的代码块内起作用,即在使用let和const的那一对大括号{}内起作用,也称块级作用域(es6之前只有函数作用域和全局作用域)。let和const申明变量不会在预编译过程中有提升行为(在全局申明也不会变成window的属性),且不能重复申明。所以要使用这类变量,只能在let和const关键字之后使用它们。

1 {
2     let a = 0;
3     console.log(a);//0
4 }
5 console.log(a);//error a is not defined

  const用来申明一个常量,申明时必须赋值,且一旦申明就不能改变。

  其实说const变量不能更改是不准确的,请看下面的例子:

1 const obj = {
2     name:'ren',
3     age:12
4 };
5 obj = {};//error
6 obj.sex = male;
7 consol.log(obj);//{name:'ren',age:12;sex:'male'}

  const申明的如果是一个原始值,那么上面的说法是准确的,如果const申明的是一个引用值,那么更准确的说法应该是一个不能被重新赋值的变量。

  3,解构赋值 

  解构赋值是对赋值运算符的扩展。它是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。

let [a,b,c] = [1,2,3];
console.log(a,b,c);//1,2,3
**************************
let [a,b,c] = [1,,3];
console.log(a,b,c);//1,undefined,3
**************************
let [a,,b] = [1,2,3];
console.log(a,b);//1,3
**************************
let [a,..b] = [1,2,3];//...是剩余运算符,表示赋值运算符右边除第一个值外剩余的都赋值给b
console.log(a,b);//1,[2,3]

  事实上所有可枚举(iterable)的对象都可以使用结构赋值,例如数组,字符串对象,以及es6新增的map和set类型。

1 let arr = 'hello';
2 let [a,b,c,d,e] = arr;
3 console.log(a,b,c,d,e);//'h','e','l','l','o'

  对象的解构赋值和数组类似,不过左边的变量名需要使用对象的属性名,并且用大括号{}而非中括号[]:

1 let obj = {name:'ren',age:12,sex:'male'};
2 let {name,age,sex} = obj;
3 console.log(name,age,sex);//'ren',12,'male';

   

二  新的对象和方法

  1,map和set

  map对象用于保存键值对,任何值javascript支持的值都可以作为一个键或者一个值。这听起来和对象差不多啊?其实它们还是有区别的:

    a) object的键只能是字符串或es6的symbol值,而map可以是任何值。

    b) map对象有一个size属性,存储了键值对的个数,而object对象没有类似属性。

1 let mymap = new map([['name','ren'],['age',12]]);
2 console.log(mymap);//{'name'=>'ren','age'=>12}
3 mymap.set('sex','male');
4 console.log(mymap);//{'name'=>'ren','age'=>12,'sex'=>'male'}
5 mymap.get('name');//'ren'
6 mymap.has('age');//true
7 mymap.delete('age');//true
8 mymap.has('age');//false
9 mymap.get('age');//undefined

  map构造函数接收一个二维数组来创建一个map对象。数组元素的第0位表示map对象的key,第1位表示map对象的value。

  map对象使用set方法来新增数据,set方法接收两个参数,第一个表示key,第二个表示value。使用get方法获取数据,参数是对象的key。

  map对象使用delete方法来删除数据,接收一个参数,表示需要被删除的key。

  map对象使用has方法检测是否已经具有某个属性,返回boolean值。

  set对象和map对象类似,但它是用来存储一组唯一值的,而不是键值对。类似数组,但它的每个元素都是唯一的。

1 let myset = new set([1,2,3]);
2 console.log(myset);//{1,2,3}
3 myset.add(4);
4 console.log(myset);//{1,2,3,4}
5 myset.delete(1);//true
6 myset.has(1);//false

  利用set对象唯一性的特点,可以轻松实现数组的去重:

1 let arr = [1,1,2,3,4,4];
2 let myset = new set(arr);
3 let newarr = array.from(myset);
4 console.log(newarr);//[1,2,3,4]

  2,对象新特性

  创建对象的字面量方式可以更加简洁。直接使用变量名作为属性,函数体作为方法,最终变量值变成属性值,函数名变成方法名。

 1 let name = 'ren';
 2 let age = 12;
 3 let myself = {
 4     name,
 5     age,
 6     say(){
 7         console.log(this.name);
 8     }
 9 };
10 console.log(myself);//{name:'ren',age:12,say:fn}
11 myself.say();//'ren'

  对象的拓展运算符(...)三点。用于拷贝目标对象所有可遍历的属性到当前对象。

1 let obj = {name:'ren',age:12};
2 let person = {...obj};
3 console.log(person);//{name:'ren',age:12}
4 obj == person;//false
5 let another = {sex:'male'};
6 let someone = {...person,...another};//合并对象
7 console.log(someone);//{name:'ren',age:12,sex:'male'}

  es6对象新增了两个方法,assign和is。

  assign用于浅拷贝源对象可枚举属性到目标对象。

1 let source = {a:{ b: 1},b: 2};
2 let target = {c: 3};
3 object.assign(target, source);
4 console.log(target);//{c: 3, a: {b:1}, b: 2}
5 source.a.b = 2;
6 console.log(target.a.b);//2

  如果有同名属性,那么目标对象的属性值会被源对象的属性值覆盖。所以数组的表现就有一点特别了:

1 object.assign([1,2,3],[11,22,33,44]);//[11,22,33,44]

  数组的index就是属性名,当使用assign方法时,从第0位开始,目标数组的值便开始被源数组的值覆盖了。

  is方法和(===)功能基本类似,用于判断两个值是否绝对相等。

1 object.is(1,1);//true
2 object.is(1,true);//false
3 object.is([],[]);//false
4 object.is(+0,-0);//false
5 object.is(nan,nan);//true

  他们仅有的两点区别是,is方法可以区分+0还是-0,还有就是它认为nan是相等的。

  3,字符串新方法

  includes()判断字符串是否包含参数字符串,返回boolean值。如果想要知道参数字符串出现的位置,还是需要indexof或lastindexof方法。

  startswith()/endswith(),判断字符串是否以参数字符串开头或结尾。返回boolean值。这两个方法可以有第二个参数,一个数字,表示开始查找的位置。

1 let str = 'blue,red,orange,white';
2 str.includes('blue');//true
3 str.startswith('blue');//true
4 str.endswith('blue');//false

  repeat()方法按指定次数返回一个新的字符串。如果次数是大于0的小数则向下取整,0到-1之间的小数则向上取整,其他负数将抛出错误。

1 console.log('hello'.repeat(2));//'hellohello'
2 console.log('hello'.repeat(1.9));//'hello'
3 console.log('hello'.repeat(-0.9));//''
4 console.log('hello'.repeat(-1.9));//error

  padstart()/padend(),用参数字符串按给定长度从前面或后面补全字符串,返回新字符串。

1 let arr = 'hell';
2 console.log(arr.padend(5,'o'));//'hello'
3 console.log(arr.padend(6,'o'));//'helloo'
4 console.log(arr.padend(6));//'hell  ',如果没有指定将用空格代替
5 console.log(arr.padstart(5,'o'));//'ohell'

  另外,如果字符串加上补全的字符串超出了给定的长度,那么,超出的部分将被截去。

  4,数组的新方法

  of()是es6新增的用于创建数组的方法。of把传入的参数当做数组元素,形成新的数组。

1 let arr = array.of(1,'2',[3],{});
2 console.log(arr);//[1,'2',[3],{}]

  from()方法可以将可迭代对象转换为新的数组。函数可接受3个参数:第一个表示将被转换的可迭代对象,第二个是回调函数,将对每个数组元素应用该回调函数,然后返回新的值到新数组,第三个是回到函数内this的指向。后两个参数是可选的。

1 let obj = {
2     double(n) {
3         return n * 2;
4     }
5 }
6 let arr = [1, 2, 3];
7 console.log(array.from(arr, function (n){
8     return this.double(n);
9 }, obj)); // [2, 4, 6]

  find()和findindex(),查找数组中符合条件的元素值或索引。如果有多个符合条件的,将只返回第一个。

1 let arr = [1,2,3,4,5];
2 console.log(arr.find(1));//1
3 console.log(arr.findindex(5));//4

  fill()/copywithin(),替换数组中部分元素,会修改原数组。

1 let arr = [1,2,3,4,5];
2 console.log(arr.fill(0,0,3));//[0,0,0,4,5]
3 //参数1表示目标值,参数2,3表示替换的始末位置,左闭右开区间。
4 console.log(arr.copywithin(0,2,4));//[0,4,0,4,5]
5 //参数1表示修改的起始位置,参数2,3表示用来替换的数据的始末位置,左闭右开区间。

  fill()用指定的值替换,copywithin()使用数组中原有的某一部分值替换。

  includes()用于检测数组是否包含某个值,可以指定开始位置。

1 let arr = [1,2,3,4,5];
2 console.log(arr.includes(2));//true
3 console.log(arr.includes(1,1));//false

  

三  函数

  1,参数默认值

  es6首次添加了参数默认值。我们再也不用在函数内部编写容错代码了。

1 function add(a=1,b=2){
2     return a + b;
3 }
4 add();//3
5 add(2);//4
6 add(3,4);//7

  和参数默认值一起,es6还带来了不定参。它的功能和使用arguments差不多。

1 function add(...num){
2     return num.reduce(function(result,value){
3         return result + value;
4     });
5 }
6 add(1,2,3,4);//10

  下面介绍的箭头函数没有arguments属性,如果箭头函数内要实现不定参,上述方式就是一个不错的选择了。

  2,箭头函数

  箭头函数实现了一种更加简洁的书写方式,并且也解决了关键字声明方式的一些麻烦事儿。箭头函数内部没有arguments,也没有prototype属性,所以不能用new关键字调用箭头函数。

  箭头函数的书写方式:参数 => 函数体。

1 let add = (a,b) => {
2     return a+b;
3 }
4 let print = () => {
5     console.log('hi');
6 }
7 let fn = a => a * a;
8 //当只有一个参数时,括号可以省略,函数体只有单行return语句时,大括号也可以省略,强烈建议不要省略它们,是真的难以阅读

  当函数需要直接返回对象时,建议用变量保存,然后返回变量名,或用小括号把对象包裹起来。否则将抛出错误。

1 var returnobj = () =>{
2     var obj = {name:'ren',age:12};
3     retufn obj;
4 };
5 //var returnobj = () => ({name:'ren',age:12});

  箭头函数和普通函数最大的区别在于其内部this永远指向其父级ao对象的this。

  普通函数在预编译环节会在ao对象上添加this属性,保存一个对象(请参照《javascript之深入对象(二)》)。每个普通函数在执行时都有一个特定的this对象,而箭头函数执行时不会在自己的this属性上添加一个新对象,而是直接引用父级ao对象上this绑定的对象。普通函数的ao对象只有在函数执行时才产生,换言之,普通函数的this是由函数执行时的环境决定。而箭头函数的特别之处在于,当函数被定义时,就需要引用其父级ao对象的this,即箭头函数的this由定义时的环境决定。

  根据箭头函数的特点,不难推测:如果定义对象的方法直接使用箭头函数,那么函数内的this将直接指向window。

1  var age = 123;
2  let obj = {
3      age:456,
4      say:() => {
5          console.log(this.age);
6      }
7  };
8 obj.say();//123
9 //对象是没有执行期上下文的(ao对象),定义对象的方法实际上是在全局作用域下,即window

   如果你一定要在箭头函数中让this指向当前对象,其实也还是有办法的(但是没必要这么麻烦啊,直接使用普通函数不是更好吗?):

 1  var age = 123;
 2  let obj = {
 3      age:456,
 4      say:function(){
 5          var fn = () => {
 6          console.log(this.age);
 7         }
 8          return fn();
 9      }
10  };
11 obj.say();//456

  我们来分析一下这是怎么做到的:首先,我们使用obj调用say方法时,say内创建了ao对象,并且该ao对象的this属性指向了obj(这都不明白的请回去往前复习一下我的《javascript之深入函数/对象》),然后,say内部又声明了一个箭头函数。我们说箭头函数在声明时就要强行引用父级ao的this属性,那么现在该箭头函数的父级ao是谁呢?当然就是say的ao啦,所以这里箭头函数的this直接就绑定了obj,最后箭头函数在执行时拿到的this,实际上就是say方法的ao.this,即obj本身。

  上面是在对象中使用箭头函数,如果那让你难于理解,那么请看下面这种方式:在普通函数中使用箭头函数。

1 var obj = {name:'ren'};
2 function test(){
3     var fn = () => {
4         console.log(this);
5     };
6     fn();
7 }
8 test();//window
9 test.call(obj);//{name:'ren'}

  test函数在全局执行时,其this指向window,这时也产生了箭头函数的定义,于是箭头函数内的this也被指向了window,所以最终打印出window对象。

  当我们手动改变test函数执行时this的指向时,箭头函数定义所绑定的this实际上也被我们修改了。所以最终打印出obj。

 

四  class(类)  

  class 作为对象的模板被引入es6,你可以通过 class 关键字定义类。class 的本质依然是一个函数。

  1,创建类

 1 class ex {//关键字申明方式
 2     constructor(name){
 3         this.name = name;
 4         this.say = () => {
 5             console.log(this.name);
 6         }
 7     }
 8     methods(){
 9         console.log('hello ' + this.name);
10     }
11     static a = 123;
12     static m = () => {
13         console.log(this.a);
14     };
15 }
16 //let ex = class{}  字面量方式
17 var example = new ex('ren');
18 example.say();//'ren'
19 ex.m();//123
20 example.methods();//'hello ren'
  constructor是创建类必须的方法,当使用new调用类创建实例时,将自动执行该方法,该方法和构造函数类似,默认返回this对象。实例的方法和属性都定义在constructor内部。相当于构造函数的this方式。
  类保留了prototype属性,类中的方法不需要使用function关键字,并且方法之间不需要逗号隔开。类中定义的方法实际上还是保存在类的prototype属性上。
  使用static关键字定义类的静态属性和方法。类中不能定义共有属性,要想定义实例的共有属性还是需要使用prototype属性:ex.prototype.属性名 = 属性值。

  创建实例依然使用new关键字。

  2,类的继承
  类的继承通过extends关键字实现。
 1 class person {
 2     constructor (name,age){
 3         this.name = name;
 4         this.age = age;
 5     }
 6     say(){
 7         console.log(this.name + ':' + this.age);
 8     }
 9 }
10 class student extends person{
11     constructor (name,age,sex){
12         super(name,age);
13         this.sex = sex;
14     }
15 }
16 var student = new student('ren',12,'male');
17 student.name;//'ren'
18 student.sex;//'male'
19 student.say();//'ren:12'

  子类继承自父类,不会隐式的创建自己的this对象,而是通过super()引用父类的this。这个过程和在子构造函数内使用父构造函数call(this)很像,但他们有本质的区别。另外,es6规定,super()必须在子类的this之前执行。所以一般我们把super()放在子类constructor方法的第一行,这样准没错!

 

五  异步机制

  es6新增了两种实现异步的新机制,promise和generator。文笔有限,怕讲的不清楚,误人子弟,请有兴趣的同学去下面的链接继续学习,廖老师的教程也是受很多人推崇的,当然mdn更官方。(实际上是需要较大篇幅才能讲明白,这里就偷个懒了)

  1,promise

  https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/promise

  

  2,generator

  https://developer.mozilla.org/zh-cn/docs/web/javascript/reference/global_objects/generator