zoukankan      html  css  js  c++  java
  • ES6的基础知识(一)

    1.ECMAScript 6.0(以下简称ES6)。

    2.ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的其中一种实现。

    3.对ES6支持的浏览器:超过 90%的 ES6 语法特性都实现了。

    4.Node(nodejs)是 JavaScript 的服务器运行环境(runtime)。

    5.Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码。

    npm install --save-dev @babel/core  //在项目目录中安装命令

    (1)配置文件.babelrc,存放在项目的根目录下。用于设置转码规则和插件。所有 Babel 工具和模块的使用,都必须先写好.babelrc

    //基本格式
    {
    "presets": [], //设定转码规则的:如安装官方提供的最新规则集:npm install --save-dev @babel/preset-env
    "plugins": [] }

    将新安装的规则加入.babelrc

      {
        "presets": [
          "@babel/env"
        ],
        "plugins": []
      }

    (2)使用工具@babel/cli进行命令行转码

    npm install --save-dev @babel/cli  //安装
    #基本用法:

    # 转码结果输出到标准输出 npx babel example.js # 转码结果写入一个文件 #
    --out-file 或 -o 参数指定输出文件 npx babel example.js --out-file compiled.js //单个文件转码输出到compiled.js # 或者 npx babel example.js -o compiled.js # 整个目录转码 # --out-dir 或 -d 参数指定输出目录 npx babel src --out-dir lib //整个src目录转码输出到lib # 或者 npx babel src -d lib # -s 参数生成source map文件 npx babel src -d lib -s

    (3)@babel/node模块,实现ES6的REPL(Read Eval Print Loop:交互式解释器),可以直接运行ES6代码

    npm install --save-dev @babel/node   //安装
    npx babel-node  //进入REPL环境命令
    npx babel-node
    > (x => x * 2)(1)  //直接执行表达式代码
    2
    # es6.js 的代码
    # console.log((x => x * 2)(1));
    npx babel-node es6.js  //直接运行脚本文件
    2

    (4)使用@babel/register改写require命令。目的是每当使用require加载.js,.jsx,.es和.es6后缀的文件,会自动先用babel转码。它是实时转码,所以只适合在开发环境使用。

    npm install --save-dev @babel/register  //安装
    
    // index.js //在一个名为index.js的文件引入es6.js
    require('@babel/register');  //要首先加载@babel/register
    require('./es6.js');  //引入上面的es6.js文件
    
    //使用
    node index.js   //直接执行es6.js中的es6代码,不需要手动对index.js转码
    

    (5)babel API:如果代码需要调用babel API进行转码,那么需要用到@babel/core模块,参考这里

    (6)使用@babel/polyfill,为当前环境提供垫片。babel默认只转换新的javas句法(syntax),不转换新的API,如Set,Map等全局对象都不会转码。

    ES6在Array对象新增了Array.from方法。babel就不会进行转码,如果想运行这个方法,必须使用babel-polyfill。

    http://babeljs.io/docs/usage/options/   //安装
    
    //在脚本头部引入
    import '@babel/polyfill';
    // 或者
    require('@babel/polyfill');

    (7)使用@babel/standalone模块,将babel用于浏览器环境。

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>  //将代码插入网页
    <script type="text/babel">
    // Your ES6 code
    </script>

    6.let 和 const 命令声明变量

    (1)let

    let 类似于var,但声明的变量只在所在的代码块内有效

    {
      let a = 10;
      var b = 1;
    }
    
    a // ReferenceError: a is not defined.
    b // 1

    for循环计数器中就很适合使用let命令

    如下例子:

    var a = [];
    for (var i = 0; i < 10; i++) { //循环变量会泄露为全局变量
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 10
    var a = [];
    for (let i = 0; i < 10; i++) {  //let 声明的i每次循环都是一个从新声明的变量,不过javascript引擎内部会记住上一轮循环的值
      a[i] = function () {
        console.log(i);
      };
    }
    a[6](); // 6

    另外:

    for (let i = 0; i < 3; i++) { //正确运行是会输出3次abc,函数内部变量i与循环变量i不在同一个作用域,各自有单独的作用域
      let i = 'abc';
      console.log(i);
    }
    // abc
    // abc
    // abc

    let 不存在变量提升的问题(变量可以在声明之前使用)

    // var 的情况
    console.log(foo); // 输出undefined
    var foo = 2;
    
    // let 的情况
    console.log(bar); // 报错ReferenceError
    let bar = 2;

    let的暂时性死区(TDZ):只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

    var tmp = 123;
    
    if (true) {
      tmp = 'abc'; // ReferenceError,
      let tmp; //在块中暂形成了tmp的封闭作用域tmp,使得tmp不受全局tmp影响,造成了tmp='abc'出现未定义先使用的错误
    }
    if (true) {
      // TDZ开始
      tmp = 'abc'; // ReferenceError
      console.log(tmp); // ReferenceError
    
      let tmp; // TDZ结束
      console.log(tmp); // undefined
    
      tmp = 123;
      console.log(tmp); // 123
    }

    “暂时性死区”也意味着typeof不再是一个百分之百安全的操作。

    typeof x; // ReferenceError 
    let x;

    let 不允许在相同的作用域内重复声明同一变量for(var i =0;i<10;i++){a[i] = function(){console.log(i);}console.log(i);console.log(a[i]);console.log(a[i]())

    // 报错
    function func() {
      let a = 10;
      var a = 1;
    }
    
    // 报错
    function func() {
      let a = 10;
      let a = 1;
    }

    let实际为javascript增加了块级作用域。有了块级作用域,使得原本广泛应用的立即执行函数表达式IIFE不再必要

    // IIFE 写法
    (function () {
      var tmp = ...;
      ...
    }());
    
    // 块级作用域写法
    {
      let tmp = ...;
      ...
    }
    

    ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。ES5是不允许的,不过浏览器为了兼容旧代码,并不会报错。

    考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句

    ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

     (2)const

    const声明一个只读常量,一旦声明,值不能改变,所以一旦声明就要立即初始化

    const PI = 3.1415;
    PI // 3.1415
    
    PI = 3; // TypeError: Assignment to constant variable.

    const的作用域与let命令相同,块级内有效。

    const声明的常量也不提升。

    const声明同样存在暂时性死区TDZ。

    const声明的常量也是不能重复。

    const的本质:并不是变量的值不能改动,而是变量指向的那个内存地址所保存的数据不得改动。

    对于简单类型:数值,字符串,布尔值,值就保存再变量指向的那个内存地址,因此等同于常量。

    对于复杂类型:对象和数组,变量指向的内存地址,保存的只是一个指向实际数据的指针,保证的是指针不变,而指针指向的实际数据是可以改变的。

    const foo = {};
    // 为 foo 添加一个属性,可以成功
    foo.prop = 123;  //如果想禁止添加属性,将对象冻结,可以使用const foo = Object.freeze({})
    foo.prop // 123  //使用冻结,在严格模式时,该语句会报错。
    
    // 将 foo 指向另一个对象,就会报错
    foo = {}; // TypeError: "foo" is read-only


    //对数组同理
    const a = [];
    a.push('Hello'); // 可执行
    a.length = 0;    // 可执行
    a = ['Dave'];    // 报错

    除了对象冻结,也应将对象属性冻结,下面是将对象彻底冻结的函数

    var constantize = (obj) => {
        Object.freeze(obj);
        Object.keys(obj).forEach((key,i) => {
            if(typeof obj[key] === 'object'){
                constantize(obj[key]);
            }
        });
    }

    7.变量的解构赋值:从数组和对象中提取值,对变量赋值。

    (1)

    let a = 1;
    let b = 2;
    let c = 3;
    
    //以上等价于以下代码
    
    let [a,b,c] = [1,2,3];
    //属于模式匹配解构赋值
    let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 let [ , , third] = ["foo", "bar", "baz"]; third // "baz" let [x, , y] = [1, 2, 3]; x // 1 y // 3 let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; // x // "a" y // undefined z // []

    //解构不成功的,变量值就是undefined,如下都是:
    let [foo] = [];
    let [bar,foo] = [1]; //等同于let [bar,foo]=[1,],结果都是foo解构失败,为undefine
    let [x, y] = [1, 2, 3];
    x // 1
    y // 2
    
    let [a, [b], d] = [1, [2, 3], 4];
    a // 1
    b // 2
    d // 4
    //以上是不完全解构

    如果右边不是可遍历结构,将报错。

    // 报错
    let [foo] = 1;
    let [foo] = false;
    let [foo] = NaN;
    let [foo] = undefined;
    let [foo] = null;
    let [foo] = {};

    只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

    (2)

    解构默认值

    let [foo = true] = [];
    foo // true
    
    let [x, y = 'b'] = ['a']; // x='a', y='b'
    let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
    let [x = 1] = [undefined];
    x // 1
    
    let [x = 1] = [null];  //因为null不严格等于undefined
    x // null

    (3)对象的解构赋值,与数组不一样的是,对象的解构赋值依据的属性同名,而数组依据的位置次序

    let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
    foo // "aaa"
    bar // "bbb"

    这实际上说明,对象的解构赋值是下面形式的简写:

    let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
    let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; //foo是匹配模式,baz才是变量
    baz // "aaa"
    foo // error: foo is not defined

    对象解构同样有默认值。

    // 错误的写法
    let x;
    {x} = {x: 1};
    // SyntaxError: syntax error

    (4)字符串解构

    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
    let {length : len} = 'hello';
    len // 5

    还有数值布尔值解构,函数参数解构赋值。

    (5)变量解构赋值用途

    • 交换变量的值
    let x = 1;
    let y = 2;
    
    [x, y] = [y, x];
    • 从函数返回多个值
    // 返回一个数组
    
    function example() {
      return [1, 2, 3];
    }
    let [a, b, c] = example();
    
    // 返回一个对象
    
    function example() {
      return {
        foo: 1,
        bar: 2
      };
    }
    let { foo, bar } = example();
    • 函数参数的定义
    // 参数是一组有次序的值
    function f([x, y, z]) { ... }
    f([1, 2, 3]);
    
    // 参数是一组无次序的值
    function f({x, y, z}) { ... }
    f({z: 3, y: 2, x: 1});
    • 提取JSON数据
    let jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]
    };
    
    let { id, status, data: number } = jsonData;
    
    console.log(id, status, number);
    // 42, "OK", [867, 5309]
    • 函数参数的默认值//避免在函数体内再写var foo= config.foo ||'default'这样的语句
    jQuery.ajax = function (url, {
      async = true,
      beforeSend = function () {},
      cache = true,
      complete = function () {},
      crossDomain = false,
      global = true,
      // ... more config
    } = {}) {
      // ... do stuff
    };
    •  遍历Map结构
    const map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    
    for (let [key, value] of map) {
      console.log(key + " is " + value);
    }
    // first is hello
    // second is world
    //特别地
    // 获取键名
    for (let [key] of map) {
      // ...
    }
    
    // 获取键值
    for (let [,value] of map) {
      // ...
    }
    • 输入模块的指定方法
    const { SourceMapConsumer, SourceNode } = require("source-map")

    https://www.cnblogs.com/libin-1/p/6716470.html

    http://caibaojian.com/es6/

    https://www.jianshu.com/p/87008f4f8513

    https://www.runoob.com/w3cnote/es6-tutorial.html

    http://es6.ruanyifeng.com/

    好记性不如烂笔头,每天记录一点点
  • 相关阅读:
    .NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记
    《ASP.NET Core 与 RESTful API 开发实战》-- (第10章)-- 读书笔记
    《ASP.NET Core 与 RESTful API 开发实战》-- (第9章)-- 读书笔记(下)
    《ASP.NET Core 与 RESTful API 开发实战》-- (第9章)-- 读书笔记(上)
    《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(尾)
    解决win10桌面图标变成白图标的问题
    微信小程序封装http请求
    Java如何控制某个方法允许并发访问的线程个数----Semaphore
    java实现获取用户IP地址
    shell有减号开头的参数的问题
  • 原文地址:https://www.cnblogs.com/wayneliu007/p/10960453.html
Copyright © 2011-2022 走看看