淺談ECMAScript新特性

1.新的標準規(guī)范

ECMAScript2015 是 js 的一種的新的標準規(guī)范念祭,就是對 js 的寫法上提出了新的語法要求和寫法格式。

2.ECMAScript 和 javaScript 關(guān)系

ECMAScript 和 JavaScript 的關(guān)系是群嗤,前者是后者的規(guī)格,后者是前者的一種實現(xiàn)获黔。javascript 是 netscape 創(chuàng) 造的并交給了國際標準化組織 ECMA痹雅,之所以不叫做 JavaScript 由于商標的問題仰担,java 是 sun 公司的商標,根據(jù) 授權(quán)協(xié)議只有 Netscape 公司可以合法使用 JavaScript 這個名字绩社,另外就是為了體現(xiàn) JavaScript 的標準的制定者 不是 ECMA 所以取名為 ECMAScript惰匙。


web.png
node.png

3.ES6 與 ECMAScript 2015 的關(guān)系

ES6 是 ECMA 為 JavaScript 制定的第 6 個版本的標準技掏,標準委員會最終決定铃将,標準在每年的 6 月份正式發(fā)布一 次项鬼,作為當年的正式版本。ECMAscript 2015 是在 2015 年 6 月份發(fā)布的 ES6 的第一個版本劲阎。依次類推 ECMAscript 2016 是 ES7, ECMAscript 2017 是 ES8, 后續(xù)版本都以年份為命名

塊級作用域

1.塊級作用域的種類

ECMAScript2015 為 js 提出的第三個作用域绘盟,凡是帶{}的都是一個塊級作用域。

if 語句的{},for 循環(huán)中的{},while 中的{},或者是我們單獨寫的{} try{}catch(error){}這些都提供了塊級作用域悯仙。

塊級作用域

為什么需要塊級作用域
  • 內(nèi)層變量會覆蓋外層變量

    var a = 'glh';
    function fn() {
      console.log(a); //undefined
      if (false) {
        var a = 'hello';
      }
    }
    fn();
    

這是因為 fn 函數(shù)體內(nèi)以及有 var 聲明的變量 a龄毡,只是還沒有賦值,默認為 undefined锡垄。

  • 用來計數(shù)的循環(huán)變量泄露為全局變量

    for (var i = 0; i < 10; i++) {}
    // 10
    console.log(i);
    

let const

1.let

基本用法

ECMAScript2015 新增了let命令沦零,用來聲明變量。它的用法類似于var货岭,但是所聲明的變量路操,只在let命令所在的代碼塊內(nèi)有效。

let 主要聲明塊級作用域下的成員千贯,這個成員變量的作用域范圍只能在當前塊級作用域以及子作用域鏈中屯仗。

2.const

const 聲明變量的同時必須要賦值。

const 聲明之后搔谴,不允許去修改它的值魁袜,這里面的值說的是不允許修改它,是聲明之后不允許重新指向一個新

的內(nèi)存地址敦第,可以去修改內(nèi)存地址中的屬性成員峰弹。

數(shù)組

1.數(shù)組解構(gòu)

ES6 允許按照一定模式,從數(shù)組和對象中提取值芜果,對變量進行賦值鞠呈,這被稱為解構(gòu)。

  • 完全解構(gòu) 將數(shù)組中的每一個值都對應(yīng)上相應(yīng)的變量师幕。

    var arr = ['a', 'b', 'c'];
    let [com, ind, work] = arr;
    console.log(work);  // c
    
  • 不完全解構(gòu) 數(shù)組中的部分值對應(yīng)上了相應(yīng)的變量粟按。

    var arr = ['a', 'b', 'c'];
    let [, , work] = arr;
    console.log(work);  // c
    

    注意:模式?jīng)]有匹配上的可以不填,但是必須要加逗號隔開霹粥。

  • 解構(gòu)不成功

    右邊的變量的個數(shù)超過了等號左邊中數(shù)組的元素

    let [a, b, c] = [12];
    console.log(b); //undefined
    

    如果解構(gòu)沒有成功灭将,則變量的值是 undefined,如果是展開運算的變量則是空數(shù)組后控。

    let [a, b, ...c] = [12];
    console.log(c); // []
    

2.數(shù)組的擴展

  • 展開運算符說明

    • 三個點(…)是一個展開運算符庙曙,其功能為對三個點后面的變量進行展開操作

    • 三個點(…)展開運算符:只能對具有 Iterator 接口的對象進行展開操作

  • 替代 apply()的使用技巧

    我們之前在求一個數(shù)組中的最大值得時候采用得方式是 Math.max.apply(null,[12,34,56,43]) ==56

    var max = Math.max.apply(null, [12, 34, 56, 43]);
    console.log(max); //56
    var max2 = Math.max(...[12, 34, 56, 43]);
    console.log(max2); //56
    
  • Array 類的擴展方法

    • Array.from()

    Array.from 方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數(shù)據(jù)結(jié)構(gòu) Set 和 Map)

    var arraylike = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3,
    };
    var arr = Array.from(arraylike);
    ['a', 'b', 'c'];
    

    Array.from 還可以接受第二個參數(shù),作用類似于數(shù)組的map方法浩淘,用來對每個元素進行處理捌朴,將處理后的值放入返回的數(shù)組

    var arr = Array.from([1, 2, 3], function (x) {
      return x * x;
    });
    console.log(arr); //[1,4,9]
    
    • Array.of 方法用于將一組值吴攒,轉(zhuǎn)換為數(shù)組,這個方法的主要目的砂蔽,是彌補數(shù)組構(gòu)造函數(shù)Array()的不足洼怔。因為參數(shù)個數(shù)的不同,會導(dǎo)致Array()的行為有差異左驾。

      var arr = Array(3); //[emptyx3]
      

      這里面 3 表示數(shù)組中元素的長度镣隶。

      var arr = Array(2, 3, 4); //[2,3,4]
      

      Array()里的參數(shù)個數(shù)大于 1 的時候,表示的是數(shù)組元素诡右。

      Array.of()方法不管里面參數(shù)的個數(shù)多少安岂,都將其轉(zhuǎn)為數(shù)組的元素。

      var arr = Array.of(3);
      console.log(arr); //[3]
      

對象

1.對象解構(gòu)賦值

解構(gòu)不僅可以用于數(shù)組帆吻,還可以用于對象域那。對象的解構(gòu)與數(shù)組有一個重要的不同。數(shù)組的元素是按次序排列的猜煮,變量的取值由它的位置決定次员;而對象的屬性沒有次序,變量必須與屬性同名友瘤,才能取到正確的值翠肘。

let { name, work } = { name: 'glh', work: 'code' };
console.log(name, work); // glh code

2.對象的擴展

  • 對象的簡寫

    • 當變量名和屬性名同名式,省略同名的屬性值

      const foo = 'bar';
      const baz = { foo };
      // 等同于
      const baz = { foo: foo };
      
    • 省略方法中的 function

      const obj = {
        method() {
          return 'hello';
        },
      };
      // 等同于
      const obj = {
        method: function () {
          return 'hello';
        },
      };
      
    • 屬性的賦值器(setter)和取值器(getter)

      const obj = {
        name: 'glh',
        get com() {
          return this.name;
        },
        set work(value) {
          this.name = this.name + value;
        },
      };
      console.log(obj.com); // glh
      obj.work = 'hello';
      console.log(obj.name); //glhhello
      
    • 屬性名表達式

      es5 中定義對象的屬性有兩種方法辫秧,一種是用標識符(本質(zhì)為字符串)做屬性束倍,一種是用表達式做屬性

      方法一;
      obj.name = 'a';
      // 方法二
      obj['name'] = 'a';
      
      var obj = {
        name: 'a',
      };
      

      如果使用大括號定義對象,那么在 es5 中只能使用標識符定義屬性盟戏。

      var obj = {
        name: 'a',
      };
      

      但是 ECMAScript2015 在使用大括號定義對象的時候绪妹,允許使用表達式定義屬性,把表達式放在方括號中柿究。

      let name = 'a';
      const obj = {
        [name]: 'glh',
      };
      console.log(obj); //{a: "glh"}
      

3.擴展運算符

  • 用于對象的解構(gòu)

    • 對象的解構(gòu)賦值用于從一個對象取值邮旷,相當于將目標對象自身的所有可遍歷的(enumerable)、但尚未被讀取的屬性蝇摸,分配到指定的對象上面婶肩。所有的鍵和它們的值,都會拷貝到新對象上面貌夕。

      let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
      console.log(z); //{a: 3, b: 4}
      

      上面代碼中律歼,變量z是解構(gòu)賦值所在的對象。它獲取等號右邊的所有尚未讀取的鍵(ab)啡专,將它們連同值一起拷貝過來险毁。

      注意: 1.解構(gòu)賦值必須是最后一個參數(shù)。2.解構(gòu)賦值的拷貝是淺拷貝。

  • 用于擴展運算

    • 對象的擴展運算符(...)用于取出參數(shù)對象的所有可遍歷屬性畔况,拷貝到當前對象之中鲸鹦。

      let z = { name: 'a', work: 'b' };
      let n = { ...z };
      n; // { name: "a", work: "b" }
      

字符串

1.字符串模板

  • 傳統(tǒng)的字符串里不能使用換行符,必須使用轉(zhuǎn)義符\n 替代跷跪,字符串模板里可以使用馋嗜。模板字符串(template string)是增強版的字符串,用反引號(`)標識域庇。它可以當作普通字符串使用嵌戈,也可以用來定義多行字符串,或者在字符串中嵌入變量听皿。

  • 模板字符串中嵌入變量,需要將變量名寫在${}之中宽档。大括號內(nèi)部可以放入任意的 JavaScript 表達式尉姨,可以進行運算,以及引用對象屬性

    var name = 'glh';
    var newName = `歡迎來到${name}`;
    console.log(newName);
    

2.標簽?zāi)0?/h4>
  • 模板字符串的功能吗冤,不僅僅是上面這些又厉。它可以緊跟在一個函數(shù)名后面,該函數(shù)將被調(diào)用來處理這個模板字符串椎瘟。這被稱為“標簽?zāi)0濉惫δ堋?/p>

    console.log`hello`;
    等同于;
    console.log(['hello']);
    

    標簽?zāi)0迤鋵嵅皇悄0甯仓拢呛瘮?shù)調(diào)用的一種特殊形式》挝担“標簽”指的就是函數(shù)煌妈,緊跟在后面的模板字符串就是它的參數(shù)。

    注意:如果模板字符里面有變量宣羊,就不是簡單的調(diào)用了璧诵,而是會將模板字符串先處理成多個參數(shù),再調(diào)用函數(shù)仇冯。

const name = 'Tony';
const age = 18;

// const string = `My name is ${name}, Age is ${age}`;
// console.log(string);

// 標簽函數(shù)
// const str = console.log`My name is ${name}, Age is ${age}`;

// 標簽函數(shù)作用是對字符串加工 返回一個新值之宿。利用這個特性可以做語言切換(國際化),模板引擎插件
const Tag = (string, ...rest) => {
  console.log(string);
  console.log(rest);
  return string.join();
};
const result = Tag`My name is ${name}, Age is ${age}`;
console.log(result);
  

3.擴展的方法

  • 字符串實例的方法

    • includes()

      返回布爾值苛坚,表示是否找到了參數(shù)字符串

      const name = 'glh';
      const a = `Error: foo is not defined: ${name}`;
      var b = a.includes('Err');
      console.log(b); // true
      
    • startsWith()

      返回布爾值比被,表示參數(shù)字符串是否在原字符串的頭部

      const name = 'glh';
      const a = `Error: foo is not defined: ${name}`;
      const b = a.startsWith('Err');
      console.log(b); / /true
      
    • endsWith()

      返回布爾值,表示參數(shù)字符串是否在原字符串的尾部

      const name = 'glh';
      const a = `Error: foo is not defined: ${name}`;
      const b = a.endsWith('glh');
      console.log(b); // true
      

函數(shù)

參數(shù)默認值

  • ES6 允許為函數(shù)的參數(shù)設(shè)置默認值泼舱,即直接寫在參數(shù)定義的后面等缀。

    function fn(a, b = 'glh ') {
      console.log(a + b);
    }
    fn('hello'); //hello glh
    
  • 注意:

    • 參數(shù)變量是默認聲明的,所以不能用letconst再次聲明.
    • 使用參數(shù)默認值時柠掂,函數(shù)不能有同名參數(shù)
  • 參數(shù)默認值的位置

    • 通常情況下项滑,定義了默認值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因為這樣比較容易看出來枪狂,到底省略了哪些參數(shù)危喉。如果非尾部的參數(shù)設(shè)置默認值,實際上這個參數(shù)是沒法省略的州疾。

      function f(x = 1, y) {
        return [x, y];
      }
      
      f() // [1, undefined]
      f(2) // [2, undefined]
      f(, 1) // 報錯
      

rest 參數(shù)

  • ES6 引入 rest 參數(shù)(形式為...變量名)辜限,用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對象了严蓖。rest 參數(shù)搭配的變量是一個數(shù)組薄嫡,該變量將多余的參數(shù)放入數(shù)組中。

    function add(...values) {
      console.log(values);
    }
    add(2, 5, 3); // [2, 5, 3]
    
  • rest 參數(shù)和函數(shù)中的參數(shù)解構(gòu)有什么區(qū)別

    • rest 參數(shù)是發(fā)生在函數(shù)的定義階段颗胡,函數(shù)的額參數(shù)解構(gòu)是發(fā)生在函數(shù)的調(diào)用階段
    • 二者互為逆運算
function add(...values) {
  //這是rest參數(shù)
  console.log(values);
}
add(2, 5, 3); // [2, 5, 3]

var arr = [1, 2, 3];
function fn(a, b, c) {
  console.log(a + b + c);
}
fn(...arr); //6  這是參數(shù)的解構(gòu)

箭頭函數(shù)

  • ES6 允許使用“箭頭”(=>)定義函數(shù)毫深。
var f = v => v;
// 等同于
var f = function (v) {
  return v;
};

如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分毒姨。

var f = () => 5;
// 等同于
var f = function () {
  return 5;
};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function (num1, num2) {
  return num1 + num2;
};

如果箭頭函數(shù)的代碼塊部分多于一條語句哑蔫,就要使用大括號將它們括起來,并且使用return語句返回弧呐。

var sum = (num1, num2) => {
  return num1 + num2;
};

由于大括號被解釋為代碼塊闸迷,所以如果箭頭函數(shù)直接返回一個對象,必須在對象外面加上括號俘枫,否則會報錯腥沽。

let getItem = id => { id: id, name: "Temp" }; //報錯

let getItem = id => ({ id: id, name: "Temp" });//不報錯
  • 箭頭函數(shù)有幾個使用注意點
    • 函數(shù)體內(nèi)的this對象,就是定義時所在的對象鸠蚪,而不是調(diào)用時所在的對象今阳。
    • 不可以當作構(gòu)造函數(shù),也就是說邓嘹,不可以使用new命令酣栈,否則會拋出一個錯誤。
    • 不可以使用arguments對象汹押,該對象在函數(shù)體內(nèi)不存在矿筝。如果要用,可以用 rest 參數(shù)代替棚贾。
var name = "web"
var obj={
    name: "a",
    fn(){
        var t = setTimeout(function() {
            console.log(this.name)// web  this是window(瀏覽器環(huán)境)
        },1000)
    }
}
obj.fn()
-----------------------------------
var name = "web"
var obj={
    name: "a",
    fn(){
        var t = setTimeout(() => {
            console.log(this.name)// a this是obj
        },1000)
    }
}
obj.fn()

Object

Object.assign()

  • Object.assign方法用于對象的合并窖维,將源對象(source)的所有可枚舉屬性,復(fù)制到目標對象(target)妙痹。

    const target = {
      a: 123,
      b: 123,
    };
    const sourcel = {
      a: 456,
      c: 456,
    };
    const result = Object.assign(target, sourcel);
    console.log(target); //{a: 456, b: 123, c: 456}
    console.log(target === result); //true
    

    如果目標對象與源對象有同名屬性铸史,則后面的屬性會覆蓋前面的屬性。且assign()的返回值就是第一個對象怯伊。

    如果有多個源對象有同名屬性琳轿,依然是后面的會覆蓋前面的屬性

    const target = { a: 1, b: 1 };
    const source1 = { b: 2, c: 2 };
    const source2 = { c: 3 };
    Object.assign(target, source1, source2);
    target; // {a:1, b:2, c:3}
    
  • 利用Object.assign()復(fù)制一個對象,且其中一個對象的修改不會影響到另一個對象

    const source = {
      a: 123,
    };
    var obj = Object.assign({}, source);
    obj.a = 456;
    console.log(obj); //{a: 456}
    console.log(source); //{a: 123}
    

Object.is()

  • Object.is就是用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行為基本一致崭篡。

    ES5 比較兩個值是否相等挪哄,只有兩個運算符:相等運算符(==)和嚴格相等運算符(===)。它們都有缺點琉闪,前者會自動轉(zhuǎn)換數(shù)據(jù)類型迹炼,后者的NaN不等于自身,以及+0等于-0颠毙。JavaScript 缺乏一種運算斯入,在所有環(huán)境中,只要兩個值是一樣的蛀蜜,它們就應(yīng)該相等刻两。

    console.log(Object.is(+0, -0)); //false
    console.log(+0 === -0); //true
    
    console.log(Object.is(NaN, NaN)); //true
    console.log(NaN === NaN); //false
    

Proxy

概述

Proxy 可以理解成一個快遞員,我們發(fā)快遞還是接受快遞涵防,都需要這個快遞員充當一個代理的作用闹伪。ES6 原生提供 Proxy 構(gòu)造函數(shù),用來生成 Proxy 實例壮池,這個實例就是一個代理對象(快遞員)。

 var proxy = new Proxy(target, handler);

目標對象

這個代理對象有兩個參數(shù)杀怠,一個是代理的目標對象椰憋,第二個也是一個對象,它是配置對象赔退,用來定制代理的攔截行為橙依。

const person = {
  name: 'glh',
  age: 20,
};
const personProxy = new Proxy(person, {
  get(target, property) {
    console.log(target, property); //person{name:"glh",age:20}
    return 100;
  },
  set() {},
});

配置對象

  • 配置對象中一般有兩個方法getset,get是用來攔截對目標對象屬性的訪問請求。get方法中有兩個參數(shù)硕旗,第一個參數(shù)是目標對象窗骑,第二個參數(shù)是訪問的那個屬性。

    const person = {
      name: 'glh',
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property) {
        console.log(target, property);
        return 100;
      },
      set() {},
    });
    
    console.log(personProxy.name); //100
    

注意漆枚,這個get方法的返回值就是我們獲取的這個屬性的返回值创译。

  • 這個get方法中有三個參數(shù),一個是代理的目標對象墙基,一個是代理的處理對象软族,第三個參數(shù)是 proxy 實例本身,且第三個參數(shù)是可選參數(shù)残制。

    const person = {
      name: 'glh',
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property, o) {
        console.log(o); //proxy {name:"glh",age:20}
        return property in target ? target[property] : undefined;
      },
      set() {},
    });
    
    console.log(personProxy.age); //20
    
  • 這個set方法用來攔截某個屬性的賦值操作立砸,可以接受四個參數(shù),依次為目標對象初茶、屬性名颗祝、屬性值和 Proxy 實例本身,其中最后一個參數(shù)可選

    const person = {
      name: 'glh',
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property, o) {
        return property in target ? target[property] : undefined;
      },
      set(obj, pro, value, o) {
        console.log(obj, pro, value, o);
      },
    });
    
    console.log((personProxy.name = 'x'));
    //{name: "glh", age: 20} "name" "x" Proxy {name: "zce", age: 20}
    

    可以去設(shè)置一些屬性或修改

    const person = {
      name: 'glh',
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property, o) {
        return property in target ? target[property] : undefined;
      },
      set(target, pro, value, o) {
        //可以做一些內(nèi)部校驗
        target[pro] = value;
      },
    });
    console.log((personProxy.name = 'hello'));
    person; // {name:"glh",age:20}
    

Reflect

概述

Reflect對象與Proxy對象一樣,也是 ES6 為了操作對象而提供的新 API螺戳。Reflect對象的設(shè)計目的有這樣幾個搁宾。

  • Object對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty),放到Reflect對象上∥虑停現(xiàn)階段猛铅,某些方法同時在ObjectReflect對象上部署,未來的新方法將只部署在Reflect對象上凤藏。也就是說奸忽,從Reflect對象上可以拿到語言內(nèi)部的方法

  • 修改某些Object方法的返回結(jié)果,讓其變得更合理揖庄。比如栗菜,Object.defineProperty(obj, name, desc)在無法定義屬性時,會拋出一個錯誤蹄梢,而Reflect.defineProperty(obj, name, desc)則會返回false

    // 老寫法
    try {
      Object.defineProperty(target, property, attributes);
      // success
    } catch (e) {
      // failure
    }
    
    // 新寫法
    if (Reflect.defineProperty(target, property, attributes)) {
      // success
    } else {
      // failure
    }
    
  • Object操作都變成函數(shù)行為疙筹。某些Object操作是命令式,比如name in objdelete obj[name]禁炒,而Reflect.has(obj, name)Reflect.deleteProperty(obj, name)讓它們變成了函數(shù)行為而咆。

    // 老寫法
    'assign' in Object; // true
    
    // 新寫法
    Reflect.has(Object, 'assign'); // true
    

靜態(tài)方法

  • Reflect.get

    • Reflect.get(target, name, receiver),Reflect.get方法查找并返回target對象的name屬性,如果沒有該屬性幕袱,則返回undefined

      var myObject = {
        foo: 1,
        bar: 2,
        get baz() {
          return this.foo + this.bar;
        },
      };
      
      Reflect.get(myObject, 'foo'); // 1
      Reflect.get(myObject, 'bar'); // 2
      Reflect.get(myObject, 'baz'); // 3
      
    • 如果name屬性部署了讀取函數(shù)(getter)暴备,則讀取函數(shù)的this綁定receiver

      var myObject = {
        foo: 1,
        bar: 2,
        get baz() {
          return this.foo + this.bar;
        },
      };
      
      var myReceiverObject = {
        foo: 4,
        bar: 4,
      };
      
      Reflect.get(myObject, 'baz', myReceiverObject); // 8
      
    • 如果第一個參數(shù)不是對象们豌,Reflect.get方法會報錯涯捻。

      Reflect.get(1, 'foo'); // 報錯
      Reflect.get(false, 'foo'); // 報錯
      
  • Reflect.set

    • Reflect.set(target, name, value, receiver),Reflect.set方法設(shè)置target對象的name屬性等于value

      var myObject = {
        foo: 1,
      };
      
      myObject.foo; // 1
      
      Reflect.set(myObject, 'foo', 2);
      myObject.foo; // 2
      
    • 如果name屬性設(shè)置了賦值函數(shù),則賦值函數(shù)的this綁定receiver望迎。

      var myObject = {
        foo: 4,
        set bar(value) {
          return (this.foo = value);
        },
      };
      
      var myReceiverObject = {
        foo: 0,
      };
      
      Reflect.set(myObject, 'bar', 1, myReceiverObject);
      myObject.foo; // 4
      myReceiverObject.foo; // 1
      
  • Reflect.has

    • Reflect.has(obj, name),Reflect.has方法對應(yīng)name in obj里面的in運算符

      var myObject = {
        foo: 1,
      };
      
      // 舊寫法
      'foo' in myObject; // true
      
      // 新寫法
      Reflect.has(myObject, 'foo'); // true
      

      如果Reflect.has()方法的第一個參數(shù)不是對象障癌,會報錯。

  • Reflect.deleteProperty

    • Reflect.deleteProperty(obj, name),Reflect.deleteProperty方法等同于delete obj[name]辩尊,用于刪除對象的屬性

      const myObj = { foo: 'bar' };
      
      // 舊寫法
      delete myObj.foo;
      
      // 新寫法
      Reflect.deleteProperty(myObj, 'foo');
      

      該方法返回一個布爾值涛浙。如果刪除成功,或者被刪除的屬性不存在对省,返回true蝗拿;刪除失敗,被刪除的屬性依然存在蒿涎,返回false哀托。如果Reflect.deleteProperty()方法的第一個參數(shù)不是對象,會報錯.

Promise

概述

Promise 是異步編程的一種解決方案劳秋,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大仓手。它由社區(qū)最早提出和實現(xiàn)胖齐,ES6 將其寫進了語言標準,統(tǒng)一了用法嗽冒,原生提供了Promise對象呀伙。

所謂Promise,簡單說就是一個容器添坊,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果剿另。從語法上說,Promise 是一個對象贬蛙,從它可以獲取異步操作的消息雨女。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進行處理阳准。

promise 特點

Promise對象有以下兩個特點氛堕。

(1)對象的狀態(tài)不受外界影響。Promise對象代表一個異步操作野蝇,有三種狀態(tài):pending(進行中)讼稚、fulfilled(已成功)和rejected(已失敗)绕沈。只有異步操作的結(jié)果锐想,可以決定當前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)乍狐。這也是Promise這個名字的由來痛倚,它的英語意思就是“承諾”,表示其他手段無法改變澜躺。

(2)一旦狀態(tài)改變,就不會再變抒蚜,任何時候都可以得到這個結(jié)果掘鄙。Promise對象的狀態(tài)改變,只有兩種可能:從pending變?yōu)?code>fulfilled和從pending變?yōu)?code>rejected嗡髓。只要這兩種情況發(fā)生操漠,狀態(tài)就凝固了,不會再變了饿这,會一直保持這個結(jié)果浊伙,這時就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了长捧,你再對Promise對象添加回調(diào)函數(shù)嚣鄙,也會立即得到這個結(jié)果。這與事件(Event)完全不同串结,事件的特點是哑子,如果你錯過了它舅列,再去監(jiān)聽,是得不到結(jié)果的卧蜓。

有了Promise對象帐要,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù)弥奸。此外榨惠,Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易盛霎。

promise 使用方法

  • ES6 規(guī)定赠橙,Promise對象是一個構(gòu)造函數(shù),用來生成Promise實例摩渺。

  • Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù)简烤,該函數(shù)的兩個參數(shù)分別是resolvereject。它們是兩個函數(shù)摇幻,由 JavaScript 引擎提供横侦,不用自己部署。

    var p = new Promise(function (resolve, reject) {
      if (true) {
        resolve(data);
      } else {
        reject(data);
      }
    });
    

    resolve函數(shù)的作用是绰姻,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved)枉侧,在異步操作成功時調(diào)用葫慎,并將異步操作的結(jié)果担巩,作為參數(shù)傳遞出去;reject函數(shù)的作用是酝陈,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected)帜矾,在異步操作失敗時調(diào)用翼虫,并將異步操作報出的錯誤,作為參數(shù)傳遞出去屡萤。

  • Promise實例生成以后珍剑,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)。

    p.then(
      function (value) {
        // success業(yè)務(wù)處理
      },
      function (error) {
        // failure
      }
    );
    

    then方法可以接受兩個回調(diào)函數(shù)作為參數(shù)死陆。第一個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)?code>resolved時調(diào)用招拙,第二個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)?code>rejected時調(diào)用。其中措译,第二個函數(shù)是可選的别凤,不一定要提供。這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù)领虹。

    • 看一個簡單的例子

      function time(ms) {
        return new Promise((resolve, reject) => {
          setTimeout(resolve, ms);
        });
      }
      time(1000).then(value => {
        console.log(value);
      });
      
  • Promise 新建后就會立即執(zhí)行规哪。

    let promise = new Promise(function (resolve, reject) {
      console.log('Promise');
      resolve();
    });
    
    promise.then(function () {
      console.log('resolved.');
    });
    
    console.log('Hi!');
    //Promise
    //Hi
    //resolved
    

class

概述

ES6 提供了更接近傳統(tǒng)語言的寫法,引入了 Class(類)這個概念掠械,作為對象的模板由缆。通過class關(guān)鍵字注祖,可以定義類【Γ基本上是晨,ES6 的class可以看作只是一個語法糖,它的絕大部分功能舔箭,ES5 都可以做到罩缴,新的class寫法只是讓對象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已层扶。上面的代碼用 ES6 的class改寫箫章,就是下面這樣。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

基本介紹

  • constructor()

    • constructor方法是類的默認方法镜会,通過new命令生成對象實例時檬寂,自動調(diào)用該方法。一個類必須有constructor方法戳表,如果沒有顯式定義桶至,一個空的constructor方法會被默認添加。
  • 類的實例

    • 生成類的實例的寫法匾旭,與 ES5 完全一樣镣屹,也是使用new命令。前面說過价涝,如果忘記加上new女蜈,像函數(shù)那樣調(diào)用Class,將會報錯色瘩。

      class Point {
        // ...
      }
      // 報錯
      var point = Point(2, 3);
      // 正確
      var point = new Point(2, 3);
      
    • 與 ES5 一樣伪窖,實例的屬性除非顯式定義在其本身(即定義在this對象上),否則都是定義在原型上(即定義在class上)居兆。

      class Point {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        toString() {
          return '(' + this.x + ', ' + this.y + ')';
        }
      }
      
      var point = new Point(2, 3);
      
      point.toString(); // (2, 3)
      
      point.hasOwnProperty('x'); // true
      point.hasOwnProperty('y'); // true
      point.hasOwnProperty('toString'); // false
      point.__proto__.hasOwnProperty('toString'); // true
      
    • 與 ES5 一樣惰许,類的所有實例共享一個原型對象

      var p1 = new Point(2, 3);
      var p2 = new Point(3, 2);
      
      p1.__proto__ === p2.__proto__;
      //true
      
  • getter 和 setter

    • 與 ES5 一樣,在“類”的內(nèi)部可以使用getset關(guān)鍵字史辙,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為佩伤。

      class MyClass {
        constructor() {
          // ...
        }
        get prop() {
          return 'getter';
        }
        set prop(value) {
          console.log('setter: ' + value);
        }
      }
      
      let inst = new MyClass();
      
      inst.prop = 123;
      // setter: 123
      
      inst.prop;
      // 'getter'
      
  • 屬性表達式

    • 類的屬性名聊倔,可以采用表達式

      let methodName = 'getArea';
      
      class Square {
        constructor(length) {
          // ...
        }
      
        [methodName]() {
          // ...
        }
      }
      

      上面代碼中,Square類的方法名getArea生巡,是從表達式得到的

static

  • 類相當于實例的原型耙蔑,所有在類中定義的方法,都會被實例繼承孤荣。如果在一個方法前甸陌,加上static關(guān)鍵字须揣,就表示該方法不會被實例繼承,而是直接通過類來調(diào)用钱豁,這就稱為“靜態(tài)方法”

    class Foo {
      static classMethod() {
        return 'hello';
      }
    }
    
    Foo.classMethod(); // 'hello'
    
    var foo = new Foo();
    foo.classMethod();
    // TypeError: foo.classMethod is not a function
    

    解說:上面代碼中耻卡,Foo類的classMethod方法前有static關(guān)鍵字,表明該方法是一個靜態(tài)方法牲尺,可以直接在Foo類上調(diào)用(Foo.classMethod())卵酪,而不是在Foo類的實例上調(diào)用。如果在實例上調(diào)用靜態(tài)方法谤碳,會拋出一個錯誤溃卡,表示不存在該方法。

  • 注意蜒简,如果靜態(tài)方法包含this關(guān)鍵字瘸羡,這個this指的是類,而不是實例

    class Foo {
      static bar() {
        this.baz();
      }
      static baz() {
        console.log('hello');
      }
      baz() {
        console.log('world');
      }
    }
    
    Foo.bar(); // hello
    

    解說:上面代碼中搓茬,靜態(tài)方法bar調(diào)用了this.baz犹赖,這里的this指的是Foo類,而不是Foo的實例垮兑,等同于調(diào)用Foo.baz冷尉。另外,從這個例子還可以看出系枪,靜態(tài)方法可以與非靜態(tài)方法重名雀哨。

  • 父類的靜態(tài)方法,可以被子類繼承

    class Foo {
      static classMethod() {
        return 'hello';
      }
    }
    
    class Bar extends Foo {}
    
    Bar.classMethod(); // 'hello'
    
  • 靜態(tài)屬性

    靜態(tài)屬性指的是 Class 本身的屬性私爷,即Class.propName雾棺,而不是定義在實例對象(this)上的屬性

    ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法衬浑,沒有靜態(tài)屬性“坪疲現(xiàn)在有一個提案提供了類的靜態(tài)屬性,寫法是在實例屬性的前面工秩,加上static關(guān)鍵字尸饺。

    class MyClass {
      static myStaticProp = 42;
    
      constructor() {
        console.log(MyClass.myStaticProp); // 42
      }
    }
    

繼承

  • 簡介

    • Class 可以通過extends關(guān)鍵字實現(xiàn)繼承,這比 ES5 的通過修改原型鏈實現(xiàn)繼承助币,要清晰和方便很多浪听。

      class Point {}
      
      class ColorPoint extends Point {}
      

      解說:上面代碼定義了一個ColorPoint類,該類通過extends關(guān)鍵字眉菱,繼承了Point類的所有屬性和方法迹栓。但是由于沒有部署任何代碼,所以這兩個類完全一樣俭缓,等于復(fù)制了一個Point

    • 子類必須在constructor方法中調(diào)用super方法克伊,否則新建實例時會報錯酥郭。這是因為子類自己的this對象,必須先通過父類的構(gòu)造函數(shù)完成塑造愿吹,得到與父類同樣的實例屬性和方法不从,然后再對其進行加工,加上子類自己的實例屬性和方法洗搂。如果不調(diào)用super方法消返,子類就得不到this對象。

      class Point {
        /* ... */
      }
      
      class ColorPoint extends Point {
        constructor() {}
      }
      
      let cp = new ColorPoint(); // ReferenceError
      
    • 在子類的構(gòu)造函數(shù)中耘拇,只有調(diào)用super之后撵颊,才可以使用this關(guān)鍵字,否則會報錯惫叛。這是因為子類實例的構(gòu)建倡勇,基于父類實例,只有super方法才能調(diào)用父類實例嘉涌。

      class Point {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      }
      
      class ColorPoint extends Point {
        constructor(x, y, color) {
          this.color = color; // ReferenceError
          super(x, y);
          this.color = color; // 正確
        }
      }
      
  • super

    • super這個關(guān)鍵字妻熊,既可以當作函數(shù)使用,也可以當作對象使用仑最。在這兩種情況下扔役,它的用法完全不同

      • 第一種情況,super作為函數(shù)調(diào)用時警医,代表父類的構(gòu)造函數(shù)亿胸。ES6 要求,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù)预皇。

        class A {}
        
        class B extends A {
          constructor() {
            super();
          }
        }
        

        解說:上面代碼中侈玄,子類B的構(gòu)造函數(shù)之中的super(),代表調(diào)用父類的構(gòu)造函數(shù)吟温。這是必須的序仙,否則 JavaScript 引擎會報錯。super雖然代表了父類A的構(gòu)造函數(shù)鲁豪,但是返回的是子類B的實例潘悼,即super內(nèi)部的this指的是B的實例,因此super()在這里相當于A.prototype.constructor.call(this)`

      • 第二種情況爬橡,super作為對象時挥等,在普通方法中,指向父類的原型對象堤尾;在靜態(tài)方法中,指向父類

        class A {
          p() {
            return 2;
          }
        }
        
        class B extends A {
          constructor() {
            super();
            console.log(super.p()); // 2
          }
        }
        
        let b = new B();
        
      • 由于super指向父類的原型對象(prototype)迁客,所以定義在父類實例上的方法或?qū)傩怨Γ菬o法通過super調(diào)用的

        class A {
          constructor() {
            this.p = 2;
          }
        }
        
        class B extends A {
          get m() {
            return super.p;
          }
        }
        
        let b = new B();
        b.m; // undefined
        

Set

基本用法

  • ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set辞槐。它類似于數(shù)組,但是成員的值都是唯一的粘室,沒有重復(fù)的值榄檬。

  • Set本身是一個構(gòu)造函數(shù),用來生成 Set 數(shù)據(jù)結(jié)構(gòu)衔统。

  • Set函數(shù)可以接受一個數(shù)組(或者具有 iterable 接口的其他數(shù)據(jù)結(jié)構(gòu))作為參數(shù)鹿榜,用來初始化。

    const set = new Set([1, 2, 3, 4, 4]);
    console.log(set); //[1,2,3,4]
    
    • 數(shù)組去重

      [...new Set([1, 2, 3, 2, 4, 5])]; //[1,2,3,4,5]
      
    • 字符串去重

      [...new Set('ababbc')].join(''); //"abc"
      

屬性和方法

  • Set 結(jié)構(gòu)的實例有以下屬性锦爵。

    • Set.prototype.constructor:構(gòu)造函數(shù)舱殿,默認就是Set函數(shù)。
    • Set.prototype.size:返回Set實例的成員總數(shù)险掀。
  • Set 實例的方法分為兩大類

    • 操作方法(用于操作數(shù)據(jù))

      • Set.prototype.add(value):添加某個值沪袭,返回 Set 結(jié)構(gòu)本身

        const items = new Set([]);
        items.add(1).add(2).add(3);
        console.dir(items); //[1,2,3]
        
      • Set.prototype.delete(value):刪除某個值,返回一個布爾值樟氢,表示刪除是否成功

        const items = new Set([12, 23, 34]);
        var b = items.delete(12);
        console.log(b); //true
        console.log(items); //Set(2) {23, 34}
        
      • Set.prototype.has(value):返回一個布爾值冈绊,表示該值是否為Set的成員。

      • Set.prototype.clear():清除所有成員埠啃,沒有返回值

        const items = new Set([12, 23, 34]);
        var b = items.clear(12);
        console.log(b); //undefined
        console.log(items); //Set(0) {}
        
    • 遍歷方法(用于遍歷成員)

      • Set.prototype.keys():返回鍵名的遍歷器

      • Set.prototype.values():返回鍵值的遍歷器

      • Set.prototype.entries()

        let set = new Set(['red', 'green', 'blue']);
        for (let item of set.keys()) {
          console.log(item);
        }
        // red
        // green
        // blue
        for (let item of set.values()) {
          console.log(item);
        }
        // red
        // green
        // blue
        for (let item of set.entries()) {
          console.log(item);
        }
        // ["red", "red"]
        // ["green", "green"]
        // ["blue", "blue"]
        
      • Set.prototype.forEach()

        let set = new Set([1, 4, 9]);
        set.forEach((value, key) => console.log(key + ' : ' + value));
        // 1 : 1
        // 4 : 4
        // 9 : 9
        

Map

概述

JavaScript 的對象(Object)死宣,本質(zhì)上是鍵值對的集合(Hash 結(jié)構(gòu)),但是傳統(tǒng)上只能用字符串當作鍵碴开。這給它的使用帶來了很大的限制毅该。為了解決這個問題,ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)叹螟。它類似于對象鹃骂,也是鍵值對的集合,但是“鍵”的范圍不限于字符串罢绽,各種類型的值(包括對象)都可以當作鍵畏线。也就是說,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng)良价,Map 結(jié)構(gòu)提供了“值—值”的對應(yīng)寝殴,是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)。如果你需要“鍵值對”的數(shù)據(jù)結(jié)構(gòu)明垢,Map 比 Object 更合適蚣常。

基本用法

  • 作為構(gòu)造函數(shù),Map 也可以接受一個數(shù)組作為參數(shù)痊银。該數(shù)組的成員是一個個表示鍵值對的數(shù)組

    const map = new Map([
      ['name', '張三'],
      ['title', 'Author'],
    ]);
    
    map.size; // 2
    map.has('name'); // true
    map.get('name'); // "張三"
    map.has('title'); // true
    map.get('title'); // "Author"
    

屬性和方法

  • size 屬性抵蚊,size屬性返回 Map 結(jié)構(gòu)的成員總數(shù)。

    const map = new Map();
    map.set('foo', true);
    map.set('bar', false);
    
    map.size; // 2
    
  • Map.prototype.set(key, value) set方法設(shè)置鍵名key對應(yīng)的鍵值為value,然后返回整個 Map 結(jié)構(gòu)贞绳。如果key已經(jīng)有值谷醉,則鍵值會被更新,否則就新生成該鍵冈闭, set()方法返回的是 set 對象可以采用鏈式寫法

    const m = new Map();
    
    m.set('edition', 6); // 鍵是字符串
    m.set(262, 'standard'); // 鍵是數(shù)值
    m.set(undefined, 'nah'); // 鍵是 undefined
    
  • Map.prototype.get(key) get方法讀取key對應(yīng)的鍵值俱尼,如果找不到key,返回undefined萎攒。

    const m = new Map();
    
    const hello = function () {
      console.log('hello');
    };
    m.set(hello, 'Hello ES6!'); // 鍵是函數(shù)
    
    m.get(hello); // Hello ES6!
    
  • Map.prototype.has(key)has方法返回一個布爾值遇八,表示某個鍵是否在當前 Map 對象之中

    const m = new Map();
    
    m.set('edition', 6);
    m.set(262, 'standard');
    m.set(undefined, 'nah');
    
    m.has('edition'); // true
    m.has('years'); // false
    m.has(262); // true
    m.has(undefined); // true
    
  • Map.prototype.delete(key)delete方法刪除某個鍵,返回true耍休。如果刪除失敗刃永,返回false

    const m = new Map();
    m.set(undefined, 'nah');
    m.has(undefined); // true
    
    m.delete(undefined);
    m.has(undefined); // false
    
  • Map.prototype.clear()clear方法清除所有成員羹应,沒有返回值

    let map = new Map();
    map.set('foo', true);
    map.set('bar', false);
    
    map.size; // 2
    map.clear();
    map.size; // 0
    

遍歷

  • Map.prototype.keys():返回鍵名的遍歷器揽碘。

  • Map.prototype.values():返回鍵值的遍歷器

  • Map.prototype.entries():返回所有成員的遍歷器

  • Map.prototype.forEach():遍歷 Map 的所有成員

    const map = new Map([
      ['F', 'no'],
      ['T', 'yes'],
    ]);
    
    for (let key of map.keys()) {
      console.log(key);
    }
    // "F"
    // "T"
    
    for (let value of map.values()) {
      console.log(value);
    }
    // "no"
    // "yes"
    
    for (let item of map.entries()) {
      console.log(item[0], item[1]);
    }
    // "F" "no"
    // "T" "yes"
    
    // 或者
    for (let [key, value] of map.entries()) {
      console.log(key, value);
    }
    // "F" "no"
    // "T" "yes"
    map.forEach(function (value, key, map) {
      console.log('Key: %s, Value: %s', key, value);
    });
    

    forEach方法還可以接受第二個參數(shù),用來綁定this园匹。

Symbol

概述

ES5 的對象屬性名都是字符串雳刺,這容易造成屬性名的沖突。比如裸违,你使用了一個他人提供的對象掖桦,但又想為這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現(xiàn)有方法產(chǎn)生沖突供汛。如果有一種機制枪汪,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的沖突怔昨。這就是 ES6 引入Symbol的原因雀久。ES6 引入了一種新的原始數(shù)據(jù)類型Symbol,表示獨一無二的值趁舀。它是 JavaScript 語言的第七種數(shù)據(jù)類型赖捌,前六種是:undefinednull矮烹、布爾值(Boolean)越庇、字符串(String)、數(shù)值(Number)奉狈、對象(Object)卤唉。Symbol 值通過Symbol函數(shù)生成。這就是說仁期,對象的屬性名現(xiàn)在可以有兩種類型桑驱,一種是原來就有的字符串竭恬,另一種就是新增的 Symbol 類型。凡是屬性名屬于 Symbol 類型熬的,就都是獨一無二的萍聊,可以保證不會與其他屬性名產(chǎn)生沖突。

var obj = {
  say: 'a',
};
var say = Symbol(); //say 是symbol類型
obj[say] = 'web';
console.log(obj); //{say: "a", Symbol(): "web"}

語法

  • Symbol函數(shù)前不能使用new命令悦析,否則會報錯

  • Symbol函數(shù)可以接受一個字符串作為參數(shù),表示對 Symbol 實例的描述此衅,主要是為了在控制臺顯示强戴,或者轉(zhuǎn)為字符串時,比較容易區(qū)分

  • 每一個 Symbol 值都是不相等的挡鞍,這意味著 Symbol 值可以作為標識符骑歹,用于對象的屬性名,就能保證不會出現(xiàn)同名的屬性

    var a = Symbol();
    var b = Symbol();
    console.log(a === b); //false
    var a = Symbol('a');
    var b = Symbol('b');
    console.log(a === b); //false
    

    注意:Symbol函數(shù)的參數(shù)只是表示對當前 Symbol 值的描述墨微,因此相同參數(shù)的Symbol函數(shù)的返回值是不相等的

    var a = Symbol('a');
    var b = Symbol('a');
    console.log(a === b); //false
    
  • Symbol 值不能與其他類型的值進行運算道媚,會報錯

    let sym = Symbol('My symbol');
    
    'your symbol is ' +
      sym // TypeError: can't convert symbol to string
      `your symbol is ${sym}`;
    // TypeError: can't convert symbol to string
    
  • Symbol 值作為對象屬性名時,不能用點運算符

    const mySymbol = Symbol();
    const a = {};
    a.mySymbol = 'Hello!';
    console.log(a[mySymbol]); //undefined
    console.log(a['mySymbol']); //hello
    
  • Symbol 作為屬性名翘县,遍歷對象的時候最域,該屬性不會出現(xiàn)在for...infor...of循環(huán)中锈麸,也不會被Object.keys()镀脂、Object.getOwnPropertyNames()JSON.stringify()返回忘伞。但是薄翅,它也不是私有屬性,有一個Object.getOwnPropertySymbols()方法氓奈,可以獲取指定對象的所有 Symbol 屬性名翘魄。該方法返回一個數(shù)組,成員是當前對象的所有用作屬性名的 Symbol 值舀奶。

    const obj = {};
    let a = Symbol('a');
    let b = Symbol('b');
    obj[a] = 'Hello';
    obj[b] = 'World';
    const objectSymbols = Object.getOwnPropertySymbols(obj);
    console.log(objectSymbols); //[Symbol(a), Symbol(b)]
    
  • 有時暑竟,我們希望重新使用同一個 Symbol 值,Symbol.for()方法可以做到這一點伪节。它接受一個字符串作為參數(shù)光羞,然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值。如果有怀大,就返回這個 Symbol 值纱兑,否則就新建一個以該字符串為名稱的 Symbol 值,并將其注冊到全局化借。

    let s1 = Symbol.for('foo');
    let s2 = Symbol.for('foo');
    
    s1 === s2; // true
    

    Symbol.for()Symbol()這兩種寫法潜慎,都會生成新的 Symbol。它們的區(qū)別是,前者會被登記在全局環(huán)境中供搜索铐炫,后者不會垒手。Symbol.for()不會每次調(diào)用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key是否已經(jīng)存在倒信,如果不存在才會新建一個值科贬。比如,如果你調(diào)用Symbol.for("cat")30 次鳖悠,每次都會返回同一個 Symbol 值榜掌,但是調(diào)用Symbol("cat")30 次,會返回 30 個不同的 Symbol 值

    Symbol.for('bar') === Symbol.for('bar');
    // true
    
    Symbol('bar') === Symbol('bar');
    

    由于Symbol()寫法沒有登記機制乘综,所以每次調(diào)用都會返回一個不同的值憎账。Symbol.for()為 Symbol 值登記的名字,是全局環(huán)境的卡辰,不管有沒有在全局環(huán)境運行

    function foo() {
      return Symbol.for('bar');
    }
    
    const x = foo();
    const y = Symbol.for('bar');
    console.log(x === y); // true
    

可迭代接口

Iterater 的概念

  • 簡單介紹

    JavaScript 原有的表示“集合”的數(shù)據(jù)結(jié)構(gòu)胞皱,主要是數(shù)組(Array)和對象(Object),ES6 又添加了MapSet九妈。這樣就有了四種數(shù)據(jù)集合反砌,用戶還可以組合使用它們,定義自己的數(shù)據(jù)結(jié)構(gòu)允蚣,比如數(shù)組的成員是Map于颖,Map的成員是對象。這樣就需要一種統(tǒng)一的接口機制嚷兔,來處理所有不同的數(shù)據(jù)結(jié)構(gòu)森渐。

    遍歷器(Iterator)就是這樣一種機制。它是一種接口冒晰,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制同衣。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)壶运。

    Iterator 的作用有三個:一是為各種數(shù)據(jù)結(jié)構(gòu)耐齐,提供一個統(tǒng)一的、簡便的訪問接口蒋情;二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列埠况;三是 ES6 創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費棵癣。

  • Iterator 的遍歷過程

    • 創(chuàng)建一個指針對象辕翰,指向當前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說狈谊,遍歷器對象本質(zhì)上喜命,就是一個指針對象沟沙。

    • 第一次調(diào)用指針對象的next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員

    • 第二次調(diào)用指針對象的next方法壁榕,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個成員

    • 不斷調(diào)用指針對象的next方法矛紫,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置

      每一次調(diào)用next方法,都會返回數(shù)據(jù)結(jié)構(gòu)的當前成員的信息牌里。具體來說颊咬,就是返回一個包含valuedone兩個屬性的對象。其中牡辽,value屬性是當前成員的值贪染,done屬性是一個布爾值,表示遍歷是否結(jié)束催享。

      簡單的Iterator遍歷器的實現(xiàn);
      var it = easyIterator(['a', 'b']);
      
      it.next(); // { value: "a", done: false }
      it.next(); // { value: "b", done: false }
      it.next(); // { value: undefined, done: true }
      
      function easyIterator(array) {
        var nextIndex = 0;
        return {
          next: function () {
            return nextIndex < array.length
              ? { value: array[nextIndex++], done: false }
              : { value: undefined, done: true };
          },
        };
      }
      

Iterater 接口

  • 字符串 數(shù)組 set map arguments 都有 iterater 接口,nodelist 集合哟绊,都可以用 for of 遍歷

    var st = 'glh';
    for (i of st) {
      console.log(i); // g l h
    }
    var arr = [1, 2];
    for (v of arr) {
      console.log(v); //1 2
    }
    function fn(a, b, c) {
      for (i of arguments) {
        console.log(i); //1 2 3
      }
    }
    fn(1, 2, 3);
    

Modules

概述

  • JavaScript 一直沒有模塊(module)體系因妙,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來票髓。其他語言都有這項功能攀涵,比如 Ruby 的require、Python 的import洽沟,甚至就連 CSS 都有@import以故,但是 JavaScript 任何這方面的支持都沒有,這對開發(fā)大型的裆操、復(fù)雜的項目形成了巨大障礙怒详。ES6 在語言標準的層面上,實現(xiàn)了模塊功能踪区,而且實現(xiàn)得相當簡單昆烁,完全可以取代 CommonJS 和 AMD 規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案缎岗。

語法

  • export

    • export命令用于規(guī)定模塊的對外接口

    • 一個模塊就是一個獨立的文件静尼。該文件內(nèi)部的所有變量,外部無法獲取传泊。如果你希望外部能夠讀取模塊內(nèi)部的某個變量鼠渺,就必須使用export關(guān)鍵字輸出該變量。下面是一個 JS 文件眷细,里面使用export命令輸出變量

      //demo.js
      export var firstName = 'Michael';
      export var lastName = 'Jackson';
      export var year = 1958;
      //或者
      var firstName = 'Michael';
      var lastName = 'Jackson';
      var year = 1958;
      
      export { firstName, lastName, year };
      
  • import

    • import命令用于輸入其他模塊提供的功能

    • import命令接受一對大括號拦盹,里面指定要從其他模塊導(dǎo)入的變量名。大括號里面的變量名薪鹦,必須與被導(dǎo)入模塊(profile.js)對外接口的名稱相同

      // main.js
      import { firstName, lastName, year } from './profile.js';
      
      function setName(element) {
        element.textContent = firstName + ' ' + lastName;
      }
      
  • export default

    • 為了給用戶提供方便掌敬,就要用到export default命令惯豆,為模塊指定默認輸出

    • 本質(zhì)上,export default就是輸出一個叫做default的變量或方法奔害,然后系統(tǒng)允許你為它取任意名字

    • import命令后面楷兽,不再使用大括號

    • export default命令用于指定模塊的默認輸出。顯然华临,一個模塊只能有一個默認輸出芯杀,因此export default命令只能使用一次

      // export-default.js
      export default function () {
        console.log('foo');
      }
      
      // import-default.js
      import customName from './export-default';
      customName(); // 'foo'
      

瀏覽器端加載實現(xiàn)

  • 瀏覽器加載 ES6 模塊,也使用標簽雅潭,但是要加入type="module"屬性

    // 01.js
    export var a = 123;
    
    //demo.html
    <script type="module">import {a} from "./01.js"; console.log(a)//123</script>
    
  • 腳本異步加載

    <script src="path/to/myModule.js" defer></script>
    <script src="path/to/myModule.js" async></script>
    

    解說:上面代碼中揭厚,標簽打開deferasync屬性,腳本就會異步加載扶供。渲染引擎遇到這一行命令筛圆,就會開始下載外部腳本,但不會等它下載和執(zhí)行椿浓,而是直接執(zhí)行后面的命令太援。defer 與 async 的區(qū)別是:defer 要等到整個頁面在內(nèi)存中正常渲染結(jié)束(DOM 結(jié)構(gòu)完全生成,以及其他腳本執(zhí)行完成)扳碍,才會執(zhí)行提岔;async 一旦下載完,渲染引擎就會中斷渲染笋敞,執(zhí)行這個腳本以后碱蒙,再繼續(xù)渲染。一句話夯巷,defer 是“渲染完再執(zhí)行”赛惩,async 是“下載完就執(zhí)行”。另外趁餐,如果有多個 defer 腳本坊秸,會按照它們在頁面出現(xiàn)的順序加載,而多個 async 腳本是不能保證加載順序的澎怒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褒搔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子喷面,更是在濱河造成了極大的恐慌星瘾,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惧辈,死亡現(xiàn)場離奇詭異琳状,居然都是意外死亡,警方通過查閱死者的電腦和手機盒齿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門念逞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來困食,“玉大人,你說我怎么就攤上這事翎承∷俄铮” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵叨咖,是天一觀的道長瘩例。 經(jīng)常有香客問我,道長甸各,這世上最難降的妖魔是什么垛贤? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮趣倾,結(jié)果婚禮上聘惦,老公的妹妹穿的比我還像新娘。我一直安慰自己儒恋,他們只是感情好部凑,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碧浊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘟仿。 梳的紋絲不亂的頭發(fā)上箱锐,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音劳较,去河邊找鬼驹止。 笑死,一個胖子當著我的面吹牛臊恋,可吹牛的內(nèi)容都是我干的抖仅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渡紫!你這毒婦竟也來了莉测?” 一聲冷哼從身側(cè)響起悔雹,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤腌零,失蹤者是張志新(化名)和其女友劉穎益涧,沒想到半個月后闲询,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扭弧,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碘箍。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡严望,死狀恐怖像吻,靈堂內(nèi)的尸體忽然破棺而出复隆,到底是詐尸還是另有隱情姆涩,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響雷激,放射性物質(zhì)發(fā)生泄漏籍琳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闷游。 院中可真熱鬧脐往,春花似錦阳懂、人聲如沸希太。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亡脑,卻和暖如春堕澄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背霉咨。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工蛙紫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人途戒。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓坑傅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親喷斋。 傳聞我的和親對象是個殘疾皇子唁毒,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353