zoukankan      html  css  js  c++  java
  • es6的新内容

    前端学习总结(十八)ES6——新一代的javascript

    发表于2016/6/11 21:44:27  2733人阅读

    分类: javascript

    简介

    ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,已于2015年6月正式发布。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言

    支持情况

    各大浏览器的最新版本,对ES6的支持可以查看kangax.github.io/es5-compat-table/es6/。随着时间的推移,支持度已经越来越高了,ES6的大部分特性都实现了。

    ES6转ES5

    Babel转码器

    Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。这意味着,你可以用ES6的方式编写程序,又不用担心现有环境是否支持。

    Babel在线转换
    Babel提供一个REPL在线编译器,可以在线将ES6代码转为ES5代码。转换后的代码,可以直接作为ES5代码插入网页运行。

    Traceur转码器
    Google公司的Traceur转码器,也可以将ES6代码转为ES5代码。

    Traceur在线转换
    Traceur也提供一个在线编译器,可以在线将ES6代码转为ES5代码。转换后的代码,可以直接作为ES5代码插入网页运行。

    ES6新特性

    1 let

    作用:声明变量。用法类似于var,但它所声明的变量,只在let命令所在的代码块内有效。

    不存在变量提升,变量一定要在声明后使用,否则报错。

    不允许重复声明,let不允许在相同作用域内,重复声明同一个变量。

    2 块级作用域

    ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。(1)内层变量可能会覆盖外层变量。(2)用来计数的循环变量泄露为全局变量。

    let实际上为JavaScript新增了块级作用域。另外可以用{}声明一个块级作用域。

    外层作用域无法读取内层作用域的变量。也不会受到内层作用域变量的影响。块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。

    另外,ES6也规定,函数本身的作用域,在其所在的块级作用域之内。

    3 const

    const 声明一个只读的常量。一旦声明,常量的值就不能改变。作用域与let命令相同:只在声明所在的块级作用域内有效,不提升,不可重复声明。

    4 全局对象属性

    全局对象是最顶层的对象,浏览器环境指window对象,Node.js指global对象。ES5中,全局对象的属性与全局变量等价。

    ES6规定,var命令和function命令声明的全局变量,依旧是全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。也就是说,从ES6开始,全局变量将逐步与全局对象的属性脱钩。

    5 变量解构赋值

    ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

    以前,为变量赋值,只能直接指定值。

    var a = 1;
    var b = 2;
    var c = 3;
    • 1
    • 2
    • 3

    ES6允许写成这样:

    var [a, b, c] = [1, 2, 3];
    • 1

    本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

    如果解构不成功,变量的值就等于undefined。

    6 字符串的扩展

    (扩展内容较多,只选择我觉得重要有用的部分)

    (1)模板字符串

    传统的JavaScript语言,输出模板通常是这样写的。

    $('#result').append(
      'There are <b>' + basket.count + '</b> ' +
      'items in your basket, ' +
      '<em>' + basket.onSale +
      '</em> are on sale!'
    );
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面的写法相当繁琐不方便,ES6引入了模板字符串解决这个问题。

    $('#result').append(`
      There are <b>${basket.count}</b> items
       in your basket, <em>${basket.onSale}</em>
      are on sale!
    `);
    • 1
    • 2
    • 3
    • 4
    • 5

    模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

    (2)标签模板

    模板字符串的功能,不仅仅是上面这些。它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)。

    alert`123`
    // 等同于
    alert(123)
    • 1
    • 2
    • 3

    标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。

    var a = 5;
    var b = 10;
    
    tag`Hello ${ a + b } world ${ a * b }`;
    • 1
    • 2
    • 3
    • 4

    上面代码中,模板字符串前面有一个标识名tag,它是一个函数。整个表达式的返回值,就是tag函数处理模板字符串后的返回值。

    “标签模板”的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容。另一个应用,是多语言转换(国际化处理)。

    (3)字符串的正则方法

    字符串对象共有4个方法,可以使用正则表达式:match()、replace()、search()和split()。

    ES6将这4个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。

    7 Math对象的扩展

    ES6在Math对象上新增了17个与数学相关的方法。所有这些方法都是静态方法,只能在Math对象上调用,下面列举几个:

    Math.trunc():去除一个数的小数部分,返回整数部分。
    Math.sign():判断一个数到底是正数、负数、还是零。
    Math.cbrt():用于计算一个数的立方根。
    此外还新增加了4个对数相关方法,6个三角函数方法。

    8 数组的扩展

    (1)Array.from():用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

    (2)Array.of():用于将一组值,转换为数组。

    (3)数组实例的copyWithin():在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。使用这个方法,会修改当前数组。

    (4)数组实例的find():用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

    (5)数组实例的findIndex():用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

    (6)数组实例的fill():fill方法使用给定值,填充一个数组,用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。

    (7)数组实例的遍历方法entries(),keys()和values():

    ES6提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for…of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

    (8)数组实例的includes():返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。该方法属于ES7,但Babel转码器已经支持。

    没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相当运算符(===)进行判断,这会导致对NaN的误判。includes就没有这些问题。

    另外,Map和Set数据结构有一个has方法,需要注意与includes区分。Map结构的has方法,是用来查找键名的,Set结构的has方法,是用来查找值的。

    9 函数的扩展

    (1)函数参数的默认值

    ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。

    function log(x, y = 'World') {
      console.log(x, y);
    }
    
    log('Hello') // Hello World
    log('Hello', 'China') // Hello China
    log('Hello', '') // Hello
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    除了简洁,ES6的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。

    参数变量是默认声明的,所以不能用let或const再次声明。指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。如果参数默认值是一个变量,则该变量所处的作用域,与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。

    利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

    (2)rest参数(…变量名)

    ES6引入rest参数(形式为“…变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

    (3)扩展运算符

    扩展运算符(spread)是三个点(…)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。

    用途:

    (a):替代数组的apply方法

    // ES5的写法
    Math.max.apply(null, [14, 3, 77])
    
    // ES6的写法
    Math.max(...[14, 3, 77])
    
    // 等同于
    Math.max(14, 3, 77);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (b)合并数组
    (c)与解构赋值结合
    (d)将字符串转为真正的数组

    (4)函数的name属性

    返回该函数的函数名。

    (5)箭头函数

    ES6允许使用“箭头”(=>)定义函数。

    var f = v => v;
    
    //上面的箭头函数等同于:
    
    var f = function(v) {
      return v;
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

    var f = () => 5;
    // 等同于
    var f = function () { return 5 };
    
    var sum = (num1, num2) => num1 + num2;
    // 等同于
    var sum = function(num1, num2) {
      return num1 + num2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    箭头函数使得表达更加简洁。它能很好的简化回调函数。

    // 正常函数写法
    [1,2,3].map(function (x) {
      return x * x;
    });
    
    // 箭头函数写法
    [1,2,3].map(x => x * x);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    箭头函数的使用注意点:

    (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

    (2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

    (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。

    (4)不可以使用yield命令,因此箭头函数不能用作Generator函数。

    上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

    (6)函数绑定(::)

    箭头函数可以绑定this对象,大大减少了显式绑定this对象的写法(call、apply、bind)。但是,箭头函数并不适用于所有场合,所以ES7提出了“函数绑定”(function bind)运算符,用来取代call、apply、bind调用。虽然该语法还是ES7的一个提案,但是Babel转码器已经支持。

    函数绑定运算符是并排的两个双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

    foo::bar;
    // 等同于
    bar.bind(foo);
    
    foo::bar(...arguments);
    // 等同于
    bar.apply(foo, arguments);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (7)尾调用与尾递归

    尾调用(Tail Call)是函数式编程的一个重要概念,是指某个函数的最后一步是调用另一个函数。

    尾调用优化:只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。

    尾递归:函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

    “尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6也是如此,第一次明确规定,所有ECMAScript的实现,都必须部署“尾调用优化”。这就是说,在ES6中,只要使用尾递归,就不会发生栈溢出,相对节省内存。

    ES6的尾调用优化只在严格模式下开启,正常模式是无效的。

    10 Symbol数据类型

    ES5的对象属性名都是字符串,这容易造成属性名冲突。比如,使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6引入Symbol的原因。

    ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

    Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

    let s = Symbol();
    
    typeof s
    // "symbol"
    • 1
    • 2
    • 3
    • 4

    上面代码中,变量s就是一个独一无二的值。typeof运算符的结果,表明变量s是Symbol数据类型,而不是字符串之类的其他类型。

    注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

    注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

    属性名的遍历

    Symbol作为属性名,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有Symbol属性名。

    Symbol.for()和Symbol.keyFor()

    有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。

    var s1 = Symbol.for('foo');
    var s2 = Symbol.for('foo');
    
    s1 === s2 // true
    • 1
    • 2
    • 3
    • 4

    上面代码中,s1和s2都是Symbol值,但是它们都是同样参数的Symbol.for方法生成的,所以实际上是同一个值。

    Symbol.for()与Symbol()这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for(“cat”)30次,每次都会返回同一个Symbol值,但是调用Symbol(“cat”)30次,会返回30个不同的Symbol值。

    11 Proxy和Reflect

    (1)Proxy:

    Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。

    ES6原生提供Proxy构造函数,用来生成Proxy实例。

    var proxy = new Proxy(target, handler)
    • 1

    (2)Reflect:

    Reflect对象与Proxy对象一样,是ES6为了操作对象而提供的新API。Reflect对象的设计目的有以下几点:

    将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。

    修改某些Object方法的返回结果,让其变得更合理。

    让Object操作都变成函数行为。

    Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。

    未完待续

    参考

  • 相关阅读:
    Java 7如何操纵文件属性
    MS Server中varchar与nvarchar的区别
    【Unity3D】【NGUI】UICamera
    2007LA 3902 网络(树+贪心)
    读取图片的几种方式
    AssetsLibrary 实现访问相册,选取多张照片显示
    UIImagePickerController的用法
    画板的实现
    最近的状态
    富文本的使用-----实现图文混排 文字的检索 (正则表达式)
  • 原文地址:https://www.cnblogs.com/yanan-boke/p/6840394.html
Copyright © 2011-2022 走看看