ES五、六、7淺析

ECMA Script

  • 它是一種由ECMA組織(前身爲歐洲計算機制造商協會)制定和發佈的腳本語言規範
  • 而咱們學的JavaScript是ECMA的實現, 但術語ECMAScript和JavaScript平時表達同一個意思
  • JS包含三個部分:
    • ECMAScript(核心)
    • 瀏覽器端擴展
      • DOM(文檔對象模型)
      • BOM(瀏覽器對象模型)
    • 服務器端擴展
      • Node
  • ES的幾個重要版本
    • ES5 : 09年發佈
    • ES6(ES2015) : 15年發佈, 也稱爲ECMA2015
    • ES7(ES2016) : 16年發佈, 也稱爲ECMA2016  (變化不大)
  • 教程
    • ES5
      • http://www.zhangxinxu.com/wordpress/2012/01/introducing-ecmascript-5-1/
      • http://www.ibm.com/developerworks/cn/web/wa-ecma262/
    • ES6
      • http://es6.ruanyifeng.com/
    • ES7
      • http://www.w3ctech.com/topic/1614

ES5

ES5兼容性

  • IE8只支持defineProperty、getOwnPropertyDescriptor的部分特性和JSON的新特性
  • IE9不支持嚴格模式, 其它均可以
  • IE10和其餘主流瀏覽器都支持了
  • PC端開發須要注意IE9如下的兼容, 但移動端開發時不須要

嚴格模式

  • 在ES5中,除了正常運行模式(混雜模式),ES5還添加了第二種運行模式:"嚴格模式"(strict mode),這種模式使得Javascript在更嚴格的語法條件下運行
  • 嚴格模式的做用
    • 消除Javascript語法的一些不合理、不嚴謹之處,減小一些怪異行爲
    • 消除代碼運行的一些不安全之處,保證代碼運行的安全
    • 爲將來新版本的Javascript作好鋪墊
  • 使用嚴格模式
    • 在全局或函數的第一條語句定義爲: 'use strict';若是瀏覽器不支持, 只解析爲一條簡單的語句, 沒有任何反作用
    • 語法和行爲改變:
      • 必須用var聲明變量
      • 建立eval做用域
      • 禁止this指向window
      • 對象不能有重名的屬性
      • 函數不能有重名的形參
  • 教程:http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html
<script type="text/javascript">
    'use strict'; // 使用嚴格模式

    var str = 'string'; // 必須使用var來聲明變量

    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    // Person(); // 禁止自定義的函數中的this指向window
    new Person('zh', 18);

    var str = '123'; 
    eval('var str = "234"; alert(str)'); // 建立eval做用域不會污染全局變量
    alert(str);

    // 對象不能有重名的屬性
    var obj = {
        name: '123',
        name: '234'
    }
<script
View Code

JSON對象

  • JSON.stringify(obj/arr)
    • 將js對象(數組)轉換爲json對象(數組)
  • JSON.parse(json)
    • 將json對象(數組)轉換爲js對象(數組)

ES5對Object的擴展

  • Object.create(prototype[, descriptors]) : 建立一個新的對象
    • 做用: 以指定對象爲原型建立新的對象
    • 爲新的對象指定新的屬性, 並對屬性進行描述
    • value : 指定值
    • writable : 標識當前屬性值是不是可修改的, 默認爲false
    • configurable: 標識當前屬性是否能夠被刪除 默認爲false
    • enumerable: 標識當前屬性是否能用for in 枚舉 默認爲false
    • 例:
      var obj = { uername: 'zh', age: 18 };
          var obj1 = Object.create(obj, {
            sex: {
              value: '女',
              writable: true,
              configurable: true,
              enumerable: true
            }
      })Object.defineProperties(object, descriptors)
  • Object.defineProperties(object, descriptors)
    • 做用:爲指定對象定義擴展多個屬性
    • get:用來獲取當前屬性值得回調函數
    • set:修改當前屬性值得觸發的回調函數,而且實參即爲修改後的值
    • 存取器屬性:setter,getter一個用來存值,一個用來取值
    • 例:
      var obj2 = { firstName: '123', lastName: '234' }
      Object.defineProperties(obj2, { 
            fullName: {
              get: function () { // 惰性求值,獲取擴展屬性的值,獲取擴展屬性值的get方法自動調用
                return this.firstName + ' '+ this.lastName;
              },
              set: function (data) { // 監聽擴展屬性,當擴展屬性發生變化的時候會自動調用,會將變化的值做爲實參注入到set函數
                console.log('Set()---' + data);
                var names = data.split(' ');
                this.firstName = names[0];
                this.lastName = names[1];
             }
          }
      })
      obj2.firstName = '456'
      console.log(obj2.fullName); // 456 234
      console.log(obj2); // {firstName: "456", lastName: "234"}
      
      obj2.fullName = 'change fullName'
      console.log(obj2.fullName); // change fullName
      View Code
  • get propertyName(){}
    • 用來獲得當前屬性值的回調函數
  • set propertyName(){}
    • 用來監視當前屬性值變化的回調函數
  • 例:
    var obj = {
        firstName: '111',
        lastName: '222',
        get fullName() {
            return this.firstName + ' ' + this.lastName;
        },
        set fullName(data) {
            var names = data.split(' ');
            this.firstName = names[0];
            this.lastName = names[1];
        }
    };
    console.log(obj);
    obj.fullName = '333 444';
    console.log(obj);
    View Code

Array擴展

  • Array.prototype.indexOf(value) : 獲得值在數組中的第一個下標
  • Array.prototype.lastIndexOf(value) : 獲得值在數組中的最後一個下標
  • Array.prototype.forEach(function(item, index){}) : 遍歷數組
  • Array.prototype.map(function(item, index){ }) : 遍歷數組返回一個新的數組
    • var arr = [1, 2, 3, 4, 5, 6, 7, 8];
      var newArr = arr.map(function (item, index) {
        return item + 10;
      })
      console.log(newArr);  // [11, 12, 13, 14, 15, 16, 17, 18]
      View Code
  • Array.prototype.filter(function(item, index){ }) : 遍歷過濾出一個新的子數組
    • var arr = [1, 2, 3, 4, 5, 6, 7, 8];
      var newArr1 = arr.filter((item, index) => item > 3 )
      console.log(newArr1); // [4, 5, 6, 7, 8]
      View Code

Function擴展

  • Function.prototype.bind(obj)
    • 做用: 將函數內的this綁定爲obj, 並將函數返回
    • 特色:綁定完this不會當即調用當前函數,而是將函數返回
    • bind傳參的方式和call同樣,一般用來指定回調函數的this
  • bind()與call()和apply()的區別
    • bind()與call()和apply()都能指定函數中的this
    • call()/apply()是當即執行函數
    • bind()是將函數返回
    • call()和apply的傳參形式不一樣
      • foo.call(obj, 123); // 直接從第二個參數開始,依次傳入
      • foo.apply(obj, [123]); // 第二參數必須是數組,傳入放在數組裏

ES6

關鍵字拓展

let關鍵字

  • 做用:與var相似, 用於聲明一個變量
  • 特色:
    • 在塊做用域內有效
    • 不能重複聲明
    • 不會預處理, 不存在提高
  • 例:
    {
      // console.log(sum); // 不會預處理(預解析),不存在變量提高
      let sum = 123;
      // let sum = 123; // 不能重複聲明
    }
    // console.log(sum); // 在塊級做用域內有效,在ES5及以前沒有塊級做用域
    View Code
  • 應用
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>01_let關鍵字</title>
    </head>
    <body>
      <button>測試1</button>
      <br>
      <button>測試2</button>
      <br>
      <button>測試3</button>
      <br>
      <script type="text/javascript">
        let btns = document.getElementsByTagName('button');
    
        // 閉包
        // for (let i = 0; i < btns.length; i++) {
        //   (function (i) {
        //     btns[i].onclick = function () {
        //       alert(i);
        //     }
        //   })(i)
        // }
    
        // 可代替閉包
        for (let i = 0; i < btns.length; i++) {
          btns[i].onclick = function () {
            alert(i);
          }
        }
      </script>
    </body>
    </html>
    View Code

const關鍵字

  • 做用:定義一個常量
  • 特色:
    • 不能修改
    • 塊做用域有效
  • 應用:保存應用須要的常量數據

變量的解構賦值

  • 從數組或對象中提取值, 對多個變量進行賦值
    • 數組的解構賦值
      // 解構數組
      let arr = [1, 3, 5, 'abc', true];
      let [, , a, b] = arr // 5 "abc"
      View Code
    • 對象的解構賦值
      // 對象的解構賦值
      let { username, age, xxx } = obj;
      // 至關於
      // let username = obj.username; // 因爲let不能重複聲明變量,故會在控制檯中報錯
      // let age = obj.age;
      // let xxx = obj.xxx;
      View Code

 

模板字符串

  • 模板字符串簡化了字符串的拼接
  • 模板字符串必須用 `` 包含
  • 變化的部分使用${xxx}定義
  • 例:
    let obj = { userName: 'zh', age: 18 };
    let str = '個人名字叫:' + obj.userName + ',個人年齡是' + obj.age;
    let tempalteStr = `個人名字叫:${obj.userName},個人年齡是${obj.age}`;
    View Code

簡化的對象寫法

  • 省略同名的屬性值
  • 省略方法的function
  • 例:
    let username = 'zh';
    let age = 18;
    let obj = { // 同名屬性可省略不寫
      username,
      age,
      getName() { // 能夠省略函數的function
        return this.username;
      }
    };
    View Code

函數擴展

箭頭函數

  • 做用: 定義匿名函數
  • 基本語法:
    • 沒有參數:  () => console.log('xxxx')
    • 一個參數:  i => i+2
    • 大於一個參數: (i,j) => i+j
    • 函數體不用大括號:默認返回結果
    • 函數體若是有多個語句: 須要用{}包圍,如有須要返回的內容,須要手動返回
  • 箭頭函數的特色:
    • 簡潔
    • 箭頭函數沒有本身的this,箭頭函數的this不是調用的時候決定的,而是在定義的時候處在的對象就是它的this
      • 箭頭函數的this看外層的是否有函數。若是有,外層函數的this就是內部箭頭函數的this,若是沒有,則this是window。
      • let obj2 = {
          name: '123',
          age: 18,
          text: () => {
            btn3.onclick = () => {
              console.log(this); // window
              console.log(this === window); // true
            }
          }
        }
        obj2.text();
        View Code

形參的默認值

  • 當不傳入參數的時候默認使用形參裏的默認值
  • 例:
    function Point(x = 1 , y = 1) {
      this.x = x;
      this.y = y;
    }
    View Code

三點運算符

rest(可變)參數

  • 用來取代arguments 但比 arguments 靈活,只能是最後部分形參參數
  • 使用三點運算符時,只能把三點運算符放在最後面
  • function foo(a, ...value) {
      console.log(arguments); // 僞數組 Arguments(4) [1, 2, 3, 4, callee: (...)]
      console.log(value); // 數組 [2, 3, 4]
    }
    foo(1, 2, 3, 4);
    View Code

擴展運算符

  • 例:
    var arr = [1,6]
    var arr1 = [2,3,4,5];
    arr = [1,...arr1,6];
    console.log(arr); // [1, 2, 3, 4, 5, 6]
    console.log(...arr); // 1 2 3 4 5 6
    View Code

class類

  • 經過class定義類/實現類的繼承
  • 在類中經過constructor定義構造方法
  • 經過new來建立類的實例
  • 經過extends來實現類的繼承
  • 經過super調用父類的構造方法
  • 重寫從父類中繼承的通常方法
  • // 定義一我的物的類
      class Person {
        constructor (name,age) { // 表示類的構造方法
          this.name = name;
          this.age = age;
        }
        // 類的通常方法
        showName() {
          console.log(this.name);
        }
      }
    
      let person = new Person ('000', 18);
      console.log(person);
      person.showName();
    
      // 子類
      class Child extends Person {
        constructor (name,age,salary) {
          super(name, age); // 調用父類的構造方法
          this.salary = salary;
        }
        showName () {
          console.log(this.name, this.age, this.salary)
        }
      }
    
      let child = new Child('www', 18, 10000 );
      console.log(child); 
      child.showName();
    View Code

Promise對象

  • Promise對象
    • 表明了將來某個將要發生的事件(一般是一個異步操做)
    • 有了promise對象, 能夠將異步操做以同步的流程表達出來, 避免了層層嵌套的回調函數(俗稱'回調地獄')
    • ES6的Promise是一個構造函數, 用來生成promise實例
  • 使用promise基本步驟(2步)
    • 建立promise對象
      let promise = new Promise((resolve, reject) => {
          //初始化promise狀態爲 pending
        //執行異步操做
        if(異步操做成功) {
          resolve(value);//修改promise的狀態爲fullfilled
        } else {
          reject(errMsg);//修改promise的狀態爲rejected
        }
      })
      View Code
    • 調用promise的then()
      promise.then(function(
        result => console.log(result),
        errorMsg => alert(errorMsg)
      ))
      View Code
  • promise對象的3個狀態
    • pending: 初始化狀態
    • fullfilled: 成功狀態
    • rejected: 失敗狀態
  • 例:
    // 建立promiise對象
    let promise = new Promise((resolve, reject) => {
      //  初始化promise狀態:pending
      console.log(111);
      // 執行異步操做,一般是發送ajax請求,開啓定時器
      setTimeout(() => {
        console.log(333);
        // 根據異步任務的返回結果取修改promise的狀態
        // 異步任務執行成功
        // resolve('成功標記'); // 修改promise的狀態爲 fullfilled:成功的狀態
        // 異步任務執行失敗
        reject('失敗標記');// 修改promise的狀態爲 rejected:失敗的狀態
      }, 2000)
    })
    console.log(222);
    promise.then((data) => { // 成功的回調
      console.log(data,'成功了')
    }, (error) => { // 失敗的回調
      console.log(error,'失敗了')
    View Code

Symbol

  • ES5中對象的屬性名都是字符串,容易形成重名,污染環境。ES6中的添加了一種原始數據類型symbol(已有的原始數據類型:String, Number, boolean, null, undefined, 對象)
  • 特色:
    • Symbol屬性值對應的值是惟一的,解決命名衝突問題(id)
    • Symbol值不能與其餘數據進行計算,包括同字符串拼串
    • for in, for of遍歷時不會遍歷symbol屬性。
  • 使用
    • 調用Symbol函數獲得symbol值
      let symbol = Symbol();
      let obj = {};
      obj[symbol] = 'hello';
      View Code
    • 傳參標識
      let symbol = Symbol('one');
      let symbol2 = Symbol('two');
      console.log(symbol);// Symbol('one')
      console.log(symbol2);// Symbol('two')
      View Code
    • 內置Symbol值
      • 除了定義本身使用的Symbol值之外,ES6還提供了11個內置的Symbol值,指向語言內部使用的方法。
      • Symbol.iterator:對象的Symbol.iterator屬性,指向該對象的默認遍歷器方法
    • 例:
      // 建立symbol屬性值
      let symbol = Symbol();
      console.log(symbol);
      let obj = {username: 'zh', age: 18};
      obj[symbol] = 'symbol'; // 不能使用obj.symbol = '111'
      console.log(obj[symbol]);
      // for in, for of 不能遍歷symbol屬性
      for (let i in obj) {
        console.log(i);
      }
      // 每建立一個symbol的值都須要經過
      let symbol2 = Symbol('one');
      let symbol3 = Symbol('two');
      console.log(symbol2 == symbol3); // false
      console.log(symbol2, symbol3); // Symbol(one) Symbol(two)
      // 能夠去定義常量
      const Person_key = Symbol('person_key');
      console.log(Person_key)
      View Code

Iterator遍歷器

  • iterator是一種接口機制,爲各類不一樣的數據結構提供統一的訪問機制
  • 做用:
    • 爲各類數據結構,提供一個統一的、簡便的訪問接口;
    • 使得數據結構的成員可以按某種次序排列
    • ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of遍歷。
  • 工做原理:
    • 建立一個指針對象(遍歷器對象),指向數據結構的起始位置。
    • 第一次調用next方法,指針自動指向數據結構的第一個成員
    • 接下來不斷調用next方法,指針會一直日後移動,直到指向最後一個成員
    • 每調用next方法返回的是一個包含value和done的對象,{value: 當前成員的值,done: 布爾值}
      • value表示當前成員的值,done對應的布爾值表示當前的數據的結構是否遍歷結束。
      • 當遍歷結束的時候返回的value值是undefined,done值爲false
    • 當使用for of去遍歷某一個數據結構的時候,首先去找Symbol.iterator,找到了就去遍歷,沒找到就不能
    • 使用三點運算符、解構賦值,就是默認去調用iterator接口
  • 原生具有iterator接口的數據(可用for of遍歷)
    • Array
    • arguments
    • set容器
    • map容器
    • String
    • 注意:普通的對象(Object)沒有部署Iterator接口
  • 爲普通的對象部署iterator接口
    let targetData = {
      [Symbol.iterator]: function () {
        let nextIndex = 0; // 記錄指針的位置
        return { // 遍歷器對象
          next: () => {
            return nextIndex < this.length ?
              { value: this[nextIndex++], done: false } :
              { value: undefined, done: true };
          }
        }
      }
    }
    View Code

Generator函數

  • 概念:
    • ES6提供的解決異步編程的方案之一
    • Generator函數是一個狀態機,內部封裝了不一樣狀態的數據,
    • 用來生成遍歷器對象
    • 可暫停函數(惰性求值), yield可暫停,next方法可啓動。每次返回的是yield後的表達式結果
  • 特色:
    • function 與函數名之間有一個星號
    • 內部用yield表達式來定義不一樣的狀態
      function* generatorExample(){
        let result = yield 'hello';  // 狀態值爲hello
        yield 'generator'; // 狀態值爲generator
      }
      View Code
    • generator函數返回的是指針對象(iterator),而不會執行函數內部邏輯
    • 調用next方法函數內部邏輯開始執行,遇到yield表達式中止,返回{value: yield後的表達式結果/undefined, done: false/true}
    • 再次調用next方法會從上一次中止時的yield處開始,直到最後
    • yield語句返回結果一般爲undefined, 當調用next方法時傳參內容會做爲啓動時yield語句的返回值。
  • 例:
    // generator函數
    function* myGenerator() {
      console.log('第一次執行');
      let result = yield 'hello'; // 當調用第一次 MG.next() 時,指針停在此處
      console.log('第二次執行');
      console.log(result); // aaaaa
      yield 'generator'
      console.log('遍歷完成')
      return "完成"
    }
    let MG = myGenerator(); // 返回一個遍歷器對象/遍歷器對象(Iterator)
    console.log(MG.next()); //done屬性反映當前generator是否遍歷完成,false表示沒有遍歷完
    console.log(MG.next('aaaaa')); // next()調用的時候傳參,傳入的參數會做爲next方法啓動時候的返回值
    console.log(MG.next()); //
    View Code

async函數

  • 概念:真正意義上去解決異步回調的問題,同步流程表達異步操做
  • 本質:Generator的語法糖
  • 語法:
    async function foo(){
      await 異步操做;
      await 異步操做;
    }
    View Code
  • 特色:
    • 不須要像Generator去調用next方法,遇到await等待,當前的異步操做完成就往下執行
    • 返回的老是Promise對象,能夠用then方法進行下一步操做
    • async取代Generator函數的星號*,await取代Generator的yield
    • 語意上更爲明確,使用簡單

字符串擴展

  • includes(str) : 判斷是否包含指定的字符串
  • startsWith(str) : 判斷是否以指定字符串開頭
  • endsWith(str) : 判斷是否以指定字符串結尾
  • repeat(count) : 重複指定次數

Number擴展

  • 二進制與八進制數值表示法: 二進制用0b, 八進制用0o
  • Number.isFinite(i) : 判斷是不是有限大的數
  • Number.isNaN(i) : 判斷是不是NaN
  • Number.isInteger(i) : 判斷是不是整數
  • Number.parseInt(str) : 將字符串轉換爲對應的數值
  • Math.trunc(i) : 直接去除小數部分

Array擴展

  • Array.from(v) : 將僞數組對象或可遍歷對象轉換爲真數組
  • Array.of(v1, v2, v3) : 將一系列值轉換成數組
  • find(function(value, index, arr){return true}) : 找出第一個知足條件返回true的元素
    let arr2 = [1,2,3,4,5,6,7,8];
    var result = arr2.find((item, index) => {
      return item > 4;
    })
    console.log(result) // 5
    View Code
  • findIndex(function(value, index, arr){return true}) : 找出第一個知足條件返回true的元素下標

對象擴展

  • Object.is(v1, v2):判斷2個數據是否徹底相等。Object.is() 是根據字符串來進行判斷
  • Object.assign(target, source1, source2..):將源對象的屬性複製到目標對象上
    let obj = {};
    let obj1 = {username:"anverson", age: 42};
    let obj2 = {sex: "女"};
    Object.assign(obj, obj1); // 將源對象的屬性複製到目標對象上
    Object.assign(obj, obj2);
    console.log(obj); // {username: "anverson", age: 42, sex: "女"}
    View Code
  • 直接操做 __proto__ 屬性(ES6以前不能直接操做__proto__而須要經過prototype操做)
    let obj2 = {};
    obj2.__proto__ = obj1;
    View Code

深度克隆

  • 拷貝數據
    • 基本數據類型:拷貝後會生成一份新的數據,修改拷貝之後的數據不會影響原數據,存儲的是該對象的實際數據
    • 對象/數組:拷貝後不會生成新的數據,而是引用。修改拷貝之後的數據會影響原來的數據,存儲的是該對象在棧中引用(引用地址),真實的數據存放在堆內存裏
  • 淺拷貝(對象/數組):拷貝引用,拷貝後的數據會影響原來的數據,使得原數據不安全
  • 深拷貝(深度克隆):拷貝的時候生成新數據,拷貝後的數據不會影響原來的數據
  • 拷貝數據的方法:
    • 直接賦值給一個變量:基本數據類型爲深拷貝,對象/數組爲淺拷貝
    • Object.assign():淺拷貝
    • Array.prototye.concat():深拷貝,數組中有對象時對象爲淺拷貝
    • Array.prototype.slice():深拷貝,數組中有對象時對象爲淺拷貝
    • JSON.parse(JSON.stringfiy()):完全的深拷貝(深度克隆),但拷貝的數據中不能有函數
  • 深度克隆實現
    // 檢測數據類型函數
     function checkedType(target) {
       return Object.prototype.toString.call(target).slice(8, -1);
     }
     // 實現深度克隆 ---> 對象/數組
     function clone(target) {
       let result, targetType = checkedType(target);
       if (targetType === 'Object') {
         result = {};
       } else if (targetType === 'Array') {
         result = []
       } else {
         return target;
       }
       // 遍歷目標數據
       for (i in target) {
         let value = target[i];
         // 判斷目標結構裏的每一項值是否存在對象/數組
         if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
           // 繼續遍歷獲取到的value
           result[i] = clone(value);
         } else {
           result[i] = value;
         }
       }
       return result;
     }
    View Code

Set和Map

  • Set容器 : 無序不可重複的多個value的集合體
    • Set()
    • Set(array)
    • add(value)
    • delete(value)
    • has(value)
    • clear()
    • size
  • Map容器 : 無序的 key不重複的多個key-value的集合體
    • Map()
    • Map(array)
    • set(key, value):添加
    • get(key)
    • delete(key)
    • has(key)
    • clear()
    • size
  • // set
    let set = new Set([1, 3, 5, 5, 4, 8]); // 會將重複的5刪除
    console.log(set.size, set);
    set.add(7);
    console.log(set.size, set);
    console.log(set.has(8));
    console.log(set.has(0));
    set.clear();
    console.log(set.size, set);
    // map
    let map = new Map([['aaa', 'username'], ['ccc', 'username']]);
    console.log(map.size, map);
    map.set('age', 111);
    console.log(map.size, map);
    map.delete('aaa');
    console.log(map.size, map);
    View Code

 for of循環

  • 做用
    • 遍歷數組
    • 遍歷Set
    • 遍歷Map
    • 遍歷字符串
    • 遍歷僞數組
  • // 利用set給數組去重
    function removeSame(arr) {
      let newArr = [];
      let set = new Set(arr);
      for (i of set) {
        newArr.push(i)
      }
      return newArr;
    }
    View Code

ES7

指數運算符(冪)

  • 指數運算符(冪): **
  • 例:
    console.log(3**3); // 3的3次冪  --- 9
    View Code

Array.prototype.includes(value)

  • 判斷數組中是否包含指定value
  • let arr = [1,2,6,'abc'];
    console.log(arr.includes('a')); // false
    View Code
相關文章
相關標籤/搜索