學(xué)習(xí)筆記-ECMAScript新特性

ECMAScript

ECMAScript是一門腳本語言柜思,一般縮寫為ES姑隅,通忱晨玻看做 JavaScript 的標(biāo)準(zhǔn)化規(guī)范蛛蒙。實際上 JavaScript 是 ECMAScript 的擴(kuò)展語言糙箍。因為在 ECMAScript 中只是提供了最基本的語法,約定了代碼該如何編寫牵祟,例如如何定義變量和函數(shù)等深夯,它只是停留在語言層面,并不能直接用來完成應(yīng)用中的實際功能開發(fā)诺苹。而我們經(jīng)常使用的 JavaScript 實現(xiàn)了 ECMAScript 語言的標(biāo)準(zhǔn)咕晋,并且在這個基礎(chǔ)上做了擴(kuò)展,使得我們可以在瀏覽器環(huán)境當(dāng)中去操作 DOM 和 BOM收奔, 在 node 環(huán)境中可以做讀寫文件等操作掌呜。

在瀏覽器中的 JavaScript 就等于 ECMAScript 加上 web 提供的API, 也就是DOM和BOM坪哄,在node環(huán)境中的 JavaScript 就等于 ECMAScript 加上 node 所提供的一系列API质蕉,例如 fs 或 net 這樣的內(nèi)置模塊所提供的 API。所以說 Javascript 語言本身指的就是 ECMAScript翩肌。


image.png
image.png

從2015年開始模暗,ES 保持每年一個大版本的迭代,伴隨著這些新版本的迭代念祭,很多新特性陸續(xù)出現(xiàn)兑宇,這也就導(dǎo)致我們現(xiàn)如今 JavaScript 這門語言的本身也就變的越來越高級,越來越便捷棒卷。

image.png

圖中展示了 ECMAScript 每個迭代的名稱,版本號和發(fā)行時間顾孽。從 ES5 到 ES2015祝钢,是前端發(fā)展的黃金時期比规,這個時期也新增了很多顛覆式的新功能,相較之前有了很大的改變拦英,從 ES2015 后蜒什,開始每年一個迭代,符合當(dāng)下互聯(lián)網(wǎng)小步快走的精神疤估,而且開始不按照版本號命名灾常,按照年份命名霎冯。很多人也把 ES2015 稱之為 ES6,但是也有很多人用 ES6 泛指所有的新標(biāo)準(zhǔn)钞瀑。

詳見ECMAScript標(biāo)準(zhǔn)規(guī)范http://www.ecma-international.org/ecma-262/6.0/

下面按以下分類講解 ES6 的新特性沈撞。

  • 解決原有語法上的一些問題或者不足
  • 對原有語法進(jìn)行增強(qiáng)
  • 全新的對象,全新的方法雕什,全新的功能
  • 全新的數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)

let const

作用域 - 某個成員能夠起作用的范圍缠俺。

在ES2015之前,ES中只有兩種作用域贷岸,全局作用域和函數(shù)作用域壹士。
在ES2015中新增了塊級作用域。指的是用{}括起來的作用域偿警,例如 if 語句和 for 循環(huán)語句{}括號括起來的部分躏救。

以前塊沒有單獨的作用域,所以螟蒸,在塊中定義的成員外部也可以訪問盒使。eg:

if(true){
  var foo = 'aaa'
}
console.log(foo) // aaa

let

如果使用 let 來聲明變量,它只能在聲明的塊中被訪問到七嫌。

if(true){
  let foo = 'aaa'
}
console.log(foo) // foo is not defined

這就表示在塊中聲明的成員忠怖,在外部是無法訪問的。

let 聲明的變量不能提升抄瑟。

// 變量提升
// foo已經(jīng)被聲明了凡泣,只是還沒有賦值
console.log(foo)
var foo = 'aaa'

// let 取消了變量提升
console.log(bar)  // 控制臺報錯
let bar = 'bar'

const

const 聲明一個只讀的恒量/常量。只是在let基礎(chǔ)上多了一個只讀皮假。

最佳實踐:不用 var鞋拟,主用 const,配合 let惹资。

總結(jié)

  • let 和 const 聲明的變量贺纲,只能在聲明的塊中被訪問到,外部無法訪問
  • let 和 const 聲明的變量褪测,不做變量提升
  • let 和 const 聲明的變量猴誊,不能在同一個作用域內(nèi)多次聲明
  • const 聲明的變量,無法修改

數(shù)組的解構(gòu)

ES2015 新增了從數(shù)組和對象中快速獲取元素的方法侮措,這種方法叫解構(gòu)懈叹。

數(shù)組結(jié)構(gòu)的用法:

const arr = [100, 200, 300]
// 按照變量名的位置,分配數(shù)組中對應(yīng)位置的值
const [foo, bar, baz] = arr
console.log(foo, bar, baz) // 100 200 300

// 如果想獲取最后一個值分扎,可以把前邊的變量刪掉澄成,但是要保留逗號
const [, , f] = arr
console.log(f) // 300


// 變量前加 ...   表示提取從當(dāng)前位置開始往后的所有成員,成員放在數(shù)組中
// ... 的用法只能在最后位置使用
const [a, ...rest] = arr
console.log(rest) // [ 200, 300 ]

// 如果變量少于數(shù)組長度,就會按順序提取墨状,后邊的成員不會被提取
const [y] = arr
console.log(y) // 100


// 如果變量長度大于數(shù)組長度卫漫,多出來的變量就是 undefined
const [z, x, c, v] = arr
console.log(v) // undefined

// 可以給變量默認(rèn)值,如果數(shù)組中沒有這個成員肾砂,就用默認(rèn)值
const [q, w, e = 1, r = 2] = arr
console.log(e, r) // 300 2

對象的解構(gòu)

對象的解構(gòu)是通過屬性名來解構(gòu)提取列赎。

使用方法:

const obj = { name: 'aaa', age: 18 }

const { name } = obj
console.log(name)

const age = 20
// 如果要提取的變量名已經(jīng)在外部聲明過,可以將變量賦值給另一個變量名
// 可以設(shè)置默認(rèn)值
const { age: objAge = 33 } = obj
console.log(age, objAge)  // 20 18

模板字符串字面量

const name = 'es2015'
const str = `hello ${name},this is a \'string\'`
console.log(str) // hello es2015,this is a 'string'
// 可以給模板字符串添加一個 標(biāo)簽函數(shù)
const str = console.log`hello world` // [ 'hello world' ]


const name = 'tom'
const gender = true
// 定義一個標(biāo)簽函數(shù)
// 標(biāo)簽函數(shù)的第一個參數(shù)是模板字符串中的內(nèi)容 按照表達(dá)式 分割后的靜態(tài)內(nèi)容的數(shù)組
// 后邊的參數(shù)就是模板字符串中出現(xiàn)的表達(dá)式的變量
// 返回值就是字符串最終的內(nèi)容
// 可以在標(biāo)簽函數(shù)中對變量進(jìn)行加工
function tag1(string, name, gender) {
  console.log(string) // [ 'hey, ', ' is a ', '' ]
  console.log(name, gender) // tom true
  const sex = gender ? 'man' : 'woman'
  return string[0] + name + string[1] + sex + string[2]
}
const str1 = tag1`hey, ${name} is a ${gender}`
console.log(str1) // hey, tom is a man
  • 傳統(tǒng)字符串不支持換行镐确,模板字符串支持換行
  • 模板字符串支持嵌入變量粥谬,比字符串拼接
  • 模板字符串可以使用標(biāo)簽函數(shù)對字符串進(jìn)行加工

字符串的擴(kuò)展方法

  • includes() 查找字符串中是否包含某個字符
  • startsWith() 字符串是否以某字符開頭
  • endsWith() 字符串是否以某字符結(jié)尾
const message = 'Error: foo is not defined.'
console.log(message.startsWith('Error')) // true
console.log(message.endsWith('.'))  // true
console.log(message.includes('foo'))  // true

函數(shù)的擴(kuò)展

參數(shù)默認(rèn)值

// 在此之前給函數(shù)參數(shù)設(shè)置默認(rèn)值是在函數(shù)內(nèi)部通過判斷來給參數(shù)默認(rèn)值
function foo(enable) {
  enable = enable === undefined ? true : enable
  console.log(enable)
}

// 現(xiàn)在只需要在形參的后面設(shè)置一個 = 就可以設(shè)置默認(rèn)值
// 這個默認(rèn)值在沒有傳遞實參或者實參是undefined的時候使用
// 如果傳遞多個參數(shù),帶默認(rèn)值的參數(shù)放在最后,不然可能沒法工作
function foo(bar, enable = true) {
  console.log(bar, enable) // 111 true
}
foo(111)

剩余參數(shù)

// 以前接收未知個數(shù)的參數(shù)都通過 arguments辫塌, arguments 是給類數(shù)組
function foo() {
  console.log(arguments)
}

// ES2015 中新增了 ... 接收剩余參數(shù)
// 形參以數(shù)組的形式接收從當(dāng)前位置開始往后所有的實參
// 只能出現(xiàn)在形參的最后一個漏策,且只可以使用一次
function foo(first, ...args) {
  console.log(args)
}
foo(1, 2, 3, 4)

展開數(shù)組

const arr = ['foo', 'bar', 'baz']
console.log.apply(console, arr) // es2015 前
console.log(...arr) // es2015 后
// foo bar baz

箭頭函數(shù)

  • 簡短易讀
const inc = n => n + 1
console.log(inc(100))
  • 不會改變this指向
const foo = {
  name: 'tom',
  sayName: function () {
    console.log(this.name)
  },
  sayName1: () => {
    console.log(this.name)
  },
  sayNameAsync: function () {
    setTimeout(() => {
      console.log(this.name)
    })
  }
}
foo.sayName() // tom
foo.sayName1() // undefined
foo.sayNameAsync() // tom

對象的擴(kuò)展

對象字面量的增強(qiáng)

const bar = '456'
const obj = {
  foo: 123,
  // 傳統(tǒng)寫法必須 : 后面跟上變量
  // bar: bar
  // es2015 這樣的寫法和上邊等價
  bar,
  // 傳統(tǒng)的方法后邊用 : 跟一個 function
  // method1: function(){},
  // 現(xiàn)在省略 : 直接 (){} 和上邊也是等價的
  // 但是背后其實就是普通的 function,這里的this也會指向當(dāng)前對象
  method1() { },
  // 之前表達(dá)式不能直接寫在對象的屬性里,需要obj[]的方式來聲明
  // 現(xiàn)在可以直接在變量中用 [] 來聲明屬性
  [1 + 1]: 456,
}
// 之前表達(dá)式不能直接寫在對象的屬性里臼氨,需要obj[]的方式來聲明
obj[Math.random()] = 123

對象擴(kuò)展方法

  • Object.assign() 將多個源對象中的屬性復(fù)制到一個目標(biāo)對象中掺喻,如果有相同的屬性,源對象中的屬性會覆蓋目標(biāo)對象中的屬性储矩。后邊的對象會覆蓋前邊的對象
const source1 = {
  a: 123,
  b: 123
}
const source2 = {
  b: 789,
  d: 789
}

const target = {
  a: 456,
  c: 456
}
const result = Object.assign(target, source1, source2)
console.log(target)  // { a: 123, c: 456, b: 789, d: 789 }
console.log(result === target) // true
  • Object.is() 判斷兩個值是否相等
console.log(0 == false)  //true
console.log(0 === false) // false
console.log(+0 === -0) // true
console.log(+0 === -0) // true
console.log(NaN === NaN) // false

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

Proxy 代理對象

如果想要監(jiān)視對象中的屬性讀寫感耙,可以使用es5中的 Object.defineProperty绰精,為對象添加屬性赃泡,這樣的話就可以捕獲到對象當(dāng)中屬性的讀寫過程延旧。這種方法應(yīng)用的非常廣泛指蚜,vue3.0以前的版本就是使用的這種方法實現(xiàn)的數(shù)據(jù)響應(yīng),從而實現(xiàn)的雙向數(shù)據(jù)綁定湃崩。

es2015 中全新設(shè)計了一個叫Proxy 的類型切揭,專門用來為對象設(shè)置訪問代理器的遍希。通過Proxy 可以輕松的監(jiān)視到對象的讀寫過程呀狼。相比于 Object.defineProperty裂允, 它的功能更為強(qiáng)大,使用起來也更為方便哥艇。

const person = {
  name: 'aaa',
  age: 20
}
// 創(chuàng)建一個Proxy 的實例绝编,第一個參數(shù)為要操作的對象
// 第二個參數(shù)也是一個對象 - 代理的處理對象
// 通過 get 方法監(jiān)視屬性的訪問,通過 set 方法監(jiān)視設(shè)置屬性的過程
const personProxy = new Proxy(person, {
  // get方法接收的參數(shù)分別為:目標(biāo)對象貌踏, 訪問的屬性名
  // 返回值作為外部訪問這個屬性的結(jié)果
  get(target, property) {
    // console.log(target, property)  // { name: 'aaa', age: 20 } name
    // return 'zhangsan'
    // 判斷target中是否有當(dāng)前屬性十饥,有就返回,沒有返回 undefined 或默認(rèn)值
    return property in target ? target[property] : 'default'
  },
  // set 方法參數(shù):目標(biāo)對象祖乳,設(shè)置的屬性逗堵,設(shè)置的值
  set(target, property, value) {
    // console.log(target, property, value) // { name: 'aaa', age: 20 } gender true
    if (property === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${value} is not an int`)
      }
    }
    target[property] = value
  }
})
console.log(personProxy.name) // aaa
console.log(personProxy.xxx) // default

personProxy.gender = true
// personProxy.age = '100' // TypeError: 100 is not an int
personProxy.age = 100
console.log(person) // { name: 'aaa', age: 100, gender: true }

Proxy vs. Object.defineProperty

image.png
  • defineProperty只能監(jiān)視屬性的讀寫,Proxy 能夠監(jiān)視到更多對象操作凡资,eg:delete操作砸捏,對對象當(dāng)中方法的調(diào)用等。
const person = {
  name: 'aaa',
  age: 20
}

const personProxy = new Proxy(person, {
  // 外部對這個proxy對象進(jìn)行delete操作的時候執(zhí)行
  // 參數(shù):代理目標(biāo)對象隙赁,要刪除的屬性名
  deleteProperty(target, property){
    console.log(target, property) // { name: 'aaa', age: 20 } age
    delete target[property]
  }
})
delete personProxy.age
console.log(person) // { name: 'aaa' }
  • Proxy 更好的支持?jǐn)?shù)組對象的監(jiān)視
const list = []
const listProxy = new Proxy(list, {
  set(target, property, value) {
    console.log('set', property, value) // set 0 100
    target[property] = value
    return true //表示設(shè)置成功
  }
})

listProxy.push(100)
console.log(list)  // [ 100 ]
  • Proxy 是以非侵入的方式監(jiān)管了對象的讀寫

Reflect

Reflect 是es2015中提供的統(tǒng)一的對象操作API垦藏。Reflect 屬于一個靜態(tài)類,不能通過 new 方法實例一個對象伞访,只能調(diào)用靜態(tài)類中的靜態(tài)方法掂骏,eg: Reflect.get().

Reflect 內(nèi)部封裝了一系列對對象的底層操作,提供了14個方法操作對象厚掷,其中一個已經(jīng)廢棄了弟灼。剩下的13個和Proxy中的方法是對應(yīng)的。Reflect成員方法就是Proxy處理對象方法內(nèi)部的默認(rèn)實現(xiàn)冒黑。

const obj = {
  foo:'123',
  bar:'456'
}
const proxy = new Proxy(obj,{
  // 沒有添加處理對象的方法 等同于 將方法原封不動的交給 Reflect 執(zhí)行
  get(target, property){
    return Reflect.get(target,property)
  }
})

為什么要有Reflect對象

提供一套統(tǒng)一的用于操作對象的API田绑。

以往操作對象時可能使用Object對象上的方法,也可能使用delete 或 in 這樣的操作符抡爹,這些對于新手來說實在是太亂了掩驱,沒有什么規(guī)律。Reflect 就是將對象的操作方式統(tǒng)一冬竟。

const obj = {
  name: 'aaa',
  age: 18
}

// 操作對象方法不統(tǒng)一
console.log('age' in obj)// 是否存在某個屬性
console.log(delete obj['age'])//刪除屬性
console.log(Object.keys(obj))//獲取屬性名

// 以上方法使用 Reflect 可以有統(tǒng)一的使用方法
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
  1. Reflect.get(target, name, receiver)
    查找并返回target對象的name屬性欧穴,沒有返回undefined.
const obj = {
  name: 'aaa',
  age: 18
}
console.log(Reflect.get(obj, 'name')) // aaa

如果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
  1. Reflect.set(target, name, value, receiver)
const obj = {
  name: 'aaa',
  age: 18
}
Reflect.set(obj, 'gender', 'man')
console.log(obj) // { name: 'aaa', age: 18, gender: 'man' }
  1. Reflect.has(obj, name)
    對應(yīng) name in obj 里面的 in 運(yùn)算符涮帘。
    代表 obj 對象里是否有 name 屬性。

  2. Reflect.deleteProperty(obj, name)
    等同于 delete obj[name],用于刪除對象的屬性

  3. Reflect.construct(target, args)
    等同于 new Gouzao(...args)

function Person(name){
  this.name = name
}

// new
const person1 = new Person('zhangsan')

// Reflect.construct
const person2 = Reflect.construct(Person, ['zhangsan'])
  1. Reflect.getPrototypeOf(obj)
    用于讀取對象的proto屬性笑诅,對應(yīng)Object.getPrototypeOf(obj)
const myObj = new FancyThing();

// 舊寫法
Object.getPrototypeOf(myObj) === FancyThing.prototype;

// 新寫法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype;
  1. Reflect.setPrototypeOf(obj, newProto)
    用于設(shè)置目標(biāo)對象的原型(prototype)调缨,對應(yīng) Object.setPrototypeOf(obj, newProto)
const myObj = {};

// 舊寫法
Object.setPrototypeOf(myObj, Array.prototype);

// 新寫法
Reflect.setPrototypeOf(myObj, Array.prototype);

myObj.length // 0
  1. Reflect.apply()

  2. Reflect.defineProperty(target, propertyKey, attribute)
    基本等同于Object.defineProperty,用來為對象定義屬性.

10.Reflect.getOwnPropertyDescriptor(target, propertyKey)
Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor,用于得到指定屬性的描述對象

  1. Reflect.isExtensible (target)
    Reflect.isExtensible方法對應(yīng)Object.isExtensible吆你,返回一個布爾值同蜻,表示當(dāng)前對象是否可擴(kuò)展。

12.Reflect.preventExtensions(target)
Reflect.preventExtensions對應(yīng)Object.preventExtensions方法早处,用于讓一個對象變?yōu)椴豢蓴U(kuò)展

  1. Reflect.ownKeys(target)
    返回對象的所有屬性

Promise

class 類

// 通過定義一個函數(shù)湾蔓,來定義一個類型
function Person(name){
  this.name = name
}
Person.prototype.say = function(){
  console.log(`hi, my name is ${this.name}`)
}

// es2015 使用 class 關(guān)鍵詞聲明一個類型
class Person {
  // constructor 是當(dāng)前類型的構(gòu)造函數(shù)
  constructor(name) {
    this.name = name
  }
  say() {
    console.log(`hi, my name is ${this.name}`)
  }
}
const p = new Person('tom')
p.say()

靜態(tài)方法

在類型當(dāng)中的方法一般分為實例方法和靜態(tài)方法。實例方法是通過這個類型的實例去調(diào)用砌梆,靜態(tài)方法是直接使用這個類型去調(diào)用默责。

使用函數(shù)定義類型時,靜態(tài)方法直接在函數(shù)內(nèi)部定義就可以咸包。而ES2015中新增添加靜態(tài)成員的 static 關(guān)鍵詞桃序。使用static定義的靜態(tài)方法中如果有 this,那它指向這個類型烂瘫,而不是實例媒熊。

class Person {
  constructor(name) {
    this.name = name
  }
  //  使用 static 關(guān)鍵詞定義靜態(tài)方法
  static create(name) {
    return new Person(name)
  }
}
// 直接使用類型來調(diào)用靜態(tài)方法
Person.create('tom')

繼承 extends

繼承是面向?qū)ο笾蟹浅V匾奶匦云媸剩ㄟ^這個特性可以抽象出來相似類型之間重復(fù)的地方。在es2015之前大多使用原型的方式實現(xiàn)繼承芦鳍,在es2015中使用extends實現(xiàn)繼承嚷往。

class Person {
  constructor(name) {
    this.name = name
  }
  say() {
    console.log(`hi, my name is ${this.name}`)
  }
}
// 使用 extends 繼承父類
// Student 繼承自 Person,Student中就有Person中所有的成員
class Student extends Person {
  constructor(name,number){
    // super 對象始終指向父類柠衅,調(diào)用它就是調(diào)用了父類的構(gòu)造函數(shù)
    super(name)
    this.number = number
  }
  hello(){
    // 可以使用 super 對象訪問父類的成員
    super.say()
    console.log(`my school number is ${this.number}`)
  }
}
const s = new Student('jack', '100')
s.hello()
// hi, my name is jack
// my school number is 100

Set 數(shù)據(jù)結(jié)構(gòu)

es2015中提供了一個叫做Set的全新的數(shù)據(jù)結(jié)構(gòu)皮仁,可以理解為集合。它與傳統(tǒng)數(shù)組類似菲宴,但是Set中的成員不能重復(fù)贷祈。

// Set 是一個類型,通過創(chuàng)建它的實例就可以存放不重復(fù)的數(shù)據(jù)
const s = new Set()

// 通過 add方法往集合中添加數(shù)據(jù)喝峦,返回集合本身势誊,所以還可以鏈?zhǔn)秸{(diào)用
// 如果向集合中添加一個已經(jīng)存在的值,則這個添加會被忽略
s.add(1).add(2).add(3).add(2)
console.log(s) // Set(3) { 1, 2, 3 }

// 想要遍歷集合中的數(shù)據(jù)可以使用集合的forEach方法
s.forEach(v => console.log(v)) // 1 2 3
// 遍歷集合中的數(shù)據(jù)也可以使用es2015中的 for(...of...)
for (let i of s) {
  console.log(i)
}
//  1 2 3

// 通過集合的 size 屬性獲取集合的長度谣蠢,相當(dāng)于數(shù)組中的length
console.log(s.size) // 3

// has 方法判斷集合中是否存在某個值
console.log(s.has(2))

// delete 方法刪除集合中的數(shù)據(jù),刪除成功返回 true
console.log(s.delete(1))

// clear 清除數(shù)組中的數(shù)據(jù)
s.clear()
console.log(s) // Set(0) {}

// Set 最常用的是給數(shù)組去重
const arr = [1, 2, 1, 3, 4, 2]
// Set 的實例接受一個數(shù)組键科,數(shù)組里的值作為這個實例的初始值,重復(fù)的值會被忽略
// 使用 Array.from() 方法或者 擴(kuò)展運(yùn)算符 將集合再次轉(zhuǎn)換為數(shù)組
// result = Array.from(new Set(arr))
const result = [...new Set(arr)]
console.log(result) // [ 1, 2, 3, 4 ]

Map 數(shù)據(jù)結(jié)構(gòu)

Map 數(shù)據(jù)結(jié)構(gòu)與對象類似漩怎,都是鍵值對集合勋颖,但是對象結(jié)構(gòu)中的鍵只能是字符串,存放復(fù)雜數(shù)據(jù)時會有問題勋锤,Map 和對象唯一的區(qū)別就是可以以任意類型作為鍵饭玲。

// 使用 new 創(chuàng)建一個 Map 的實例
const m = new Map()

const tom = { name: 'tom' }
const jack = { name: 'jack' }

// 可以使用實例的 set 方法為這個Map實例設(shè)置鍵值對
m.set(tom, 90)
m.set(jack, 95)
console.log(m) // Map(2) { { name: 'tom' } => 90, { name: 'jack' } => 95 }
// get 方法獲取某個屬性的值
console.log(m.get(tom)) // 90
// has 方法判斷是否存在某個屬性
m.has(tom)
// delete 方法刪除某個屬性
m.delete(tom)
// clear  方法清空Map實例中的屬性
// m.clear()

// forEach 方法遍歷這個實例對象
m.forEach((value, key) => {
  console.log(value, key) // 95 { name: 'jack' }
})

for (let i of m) {
  console.log(i) // [ { name: 'jack' }, 95 ]
}

Symbol

在es2015之前,對象的屬性名都是字符串叁执,而字符串是有可能重復(fù)的茄厘,重復(fù)就會產(chǎn)生沖突。
eg:如果 a.js 和 b.js 同時引用shared.js文件中的cache對象谈宛,a文件給cache添加了屬性foo次哈,b文件也添加了屬性foo,就造成了沖突吆录。

以往的解決方式是約定窑滞,例如a文件使用'a_'開頭命名,b文件使用'b_'命名恢筝。但是約定的方式只是規(guī)避了問題哀卫,并沒有解決問題,如果有人不遵守約定撬槽,問題還是會出現(xiàn)此改。

// shared.js ==========================
const cache = {}

// a.js ==============================
cache['foo'] = Math.random()
cache['a_foo'] = Math.random()
// b.js ==============================
cache['foo'] = '123'
cache['b_foo'] = '123'

console.log(cache) // { foo: '123' }

es2015為了解決這種問題,提出了一種全新的數(shù)據(jù)類型 Symbol侄柔,表示一個獨一無二的值共啃。

使用Symbol函數(shù)就能創(chuàng)建一個Symbol類型的數(shù)據(jù)占调,而且使用typeof打印出來的結(jié)果就是 symbol,說明 symbol 就是一個數(shù)據(jù)類型移剪。這個數(shù)據(jù)類型最大的特點就是獨一無二究珊,通過 Symbol 函數(shù)創(chuàng)建的每一個值都是唯一的,永遠(yuǎn)不會重復(fù)挂滓。

考慮到開發(fā)過程中的調(diào)試苦银,Symbol 函數(shù)允許傳入一個字符串作為這個值的描述文本啸胧,對于多次使用 Symbol 的情況赶站,從控制臺可以區(qū)分出是哪個 Symbol。

從es2015開始可以對象可以使用 Symbol 類型的值作為屬性名纺念,所以現(xiàn)在對象的屬性名可以為兩種類型贝椿,String 和 Symbol。

// 使用Symbol函數(shù)就能創(chuàng)建一個Symbol類型的數(shù)據(jù)
const s = Symbol()
console.log(s) // Symbol()
console.log(typeof s) // symbol
console.log(Symbol() === Symbol()) // false

console.log(Symbol('foo')) // Symbol(foo)
console.log(Symbol('bar')) // Symbol(bar)


const obj = {
  [Symbol()]: '789'
}
obj[Symbol()] = '123'
obj[Symbol()] = '456'

console.log(obj) // { [Symbol()]: '789', [Symbol()]: '123', [Symbol()]: '456' }

可以利用 Symbol 模擬實現(xiàn)對象的私有成員陷谱。以前定義私有成員都是靠約定烙博,例如約定''開頭就是私有成員,外部不可以訪問''開頭的成員⊙萄罚現(xiàn)在可以直接使用 Symbol:

// 模擬實現(xiàn)對象私有成員
// a.js =======================
// 使用Symbol 創(chuàng)建私有成員的屬性名
// 在對象內(nèi)部可以使用創(chuàng)建屬性時的 Symbol 拿到對應(yīng)的屬性成員
const name = Symbol()
const person = {
  [name]:'aaa',
  say(){
    console.log(this[name])
  }
}

// b.js =======================
// 在外部無法創(chuàng)建完全相同的 Symbol渣窜,所以無法直接訪問這個 Symbol成員的屬性,只能調(diào)用普通名詞的成員
person.say() /// aaa

Symbol最主要的作用就是為對象添加獨一無二的屬性名宪躯。

補(bǔ)充

  • Symbol 函數(shù)創(chuàng)建的值是唯一的乔宿,即使傳了一樣的參數(shù)也是唯一的。
console.log(Symbol('foo') === Symbol('foo')) // false
  • 如果想在全局復(fù)用一個相同的 Symbol 值访雪,可以使用全局變量的方式實現(xiàn)详瑞,或者也可以使用 Symbol.for() 方法實現(xiàn)。

Symbol.for() 方法傳遞一個字符串臣缀,傳遞相同的字符串就會返回相同的 Symbol 值坝橡。

這個方法內(nèi)部維護(hù)了一個全局的注冊表,為字符串和 Symbol 值提供了一個一一對應(yīng)的關(guān)系精置。需要注意的是计寇,內(nèi)部維護(hù)的是字符串和 Symbol 值對應(yīng)的關(guān)系,所以如果 for 方法傳的不是字符串類型脂倦,會轉(zhuǎn)換成字符串類型饲常。

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
console.log(Symbol.for(true) === Symbol.for(true))
  • Symbol 中內(nèi)置了很多 Symbol 常量,用來作為內(nèi)部方法的標(biāo)識狼讨,這些標(biāo)識可以讓js對象實現(xiàn)一些內(nèi)置的接口贝淤。
console.log(Symbol.iterator) // Symbol(Symbol.iterator)
console.log(Symbol.hasInstance) // Symbol(Symbol.hasInstance)

例如定義一個obj對象,調(diào)用對象的 toString() 方法政供,結(jié)果默認(rèn)就是 [object Object]播聪, 我們把這樣的字符串叫做對象的 toString 標(biāo)簽朽基,如果想要自定義對象的toString 標(biāo)簽,我們可以在對象中添加特定的成員來標(biāo)識离陶〖诨ⅲ考慮到如果使用字符串去添加這種標(biāo)識符,就有可能跟內(nèi)部成員產(chǎn)生重復(fù)招刨,所以ECMAScript 要求我們使用 Symbol 值實現(xiàn)這樣的接口霎俩。

給對象添加一個 Symbol.toStringTag 這樣的屬性,讓它等于 ‘XObject’,此時toString 方法打印出的就是 XObject沉眶。

const obj = {
  [Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString()) // [object XObject]
  • 對象中定義的 Symbol 屬性使用for in循環(huán)是無法拿到的打却,通過 Object.keys() 方法也獲取不到,使用 JSON.stringify(obj) 序列化對象谎倔,Symbol 也會被忽略柳击。這些特性都使 Symbol 屬性的值特別適合作為私有屬性。想要獲取 Symbol 屬性名可以使用 **Object.getOwnPropertySymbols() **方法片习,只能獲取對象中所有的 Symbol 類型的屬性名

for...of 循環(huán)

在 ECMAScript 中遍歷數(shù)組有很多的方法

  • for 循環(huán)捌肴,適合遍歷普通的數(shù)組
  • for...in 循環(huán),適合遍歷鍵值對
  • 一些對象的遍歷方法藕咏,eg:forEach

這些遍歷方式都有一定的局限性状知,所以 es2015 引入了全新的 for...of 循環(huán)。這種方式以后會作為遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一方式孽查。

  • for...of 循環(huán)可以使用 break 終止遍歷
  • 偽數(shù)組饥悴,Set實例,Map實例也可以使用 for...of 遍歷
  • 無法遍歷普通對象

Iterable

ES中能夠表示有結(jié)構(gòu)的數(shù)據(jù)類型越來越多卦碾,Object, Array, Ser, Map... 為了給各種各樣的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的遍歷方式铺坞,es2015提供了 Iterable 接口。實現(xiàn) Iterable 接口就是 for...of 的前提洲胖。

// 實現(xiàn)可迭代接口(Iterable)
const obj = {
  store: [1, 2, 3],
  [Symbol.iterator]: function () {
    let i = 0
    const self = this
    return {
      next: function () {
        return {
          value: self.store[i],
          done: i++ >= self.store.length
        }
      }
    }
  }
}

for (let item of obj) {
  console.log(item)
}

Generator

function* foo() {
  console.log('111')
  yield 100
  console.log('222')
  yield 200
}
const result = foo()
console.log(result.next()) // 111 { value: 100, done: false }
console.log(result.next()) // 222 { value: 200, done: false }

ES Modules

ES2016

ES2016 新增了兩個方法济榨。

  • Array.prototype.includes()
    ES2016之前查找數(shù)組中是否有某個元素使用的是 indexOf 方法,這個方法不存在時返回 -1绿映, 存在返回它的位置擒滑,但是它不能用于查找是否存在NaN。而includes 方法彌補(bǔ)了這個缺點叉弦,可以用于查找是否有 NaN丐一,返回結(jié)果為 true 或 false。

  • 指數(shù)運(yùn)算符 **
    ES2016之前想要進(jìn)行指數(shù)運(yùn)算使用的是Math.pow(3, 2)來計算得到10淹冰,ES2016新增了指數(shù)運(yùn)算符 ** 库车,console.log(3 ** 2) 可以得到9.

ES2017

  • Object.values() 獲得對象所有的值,對應(yīng) Object.keys() 方法可以獲得對象所有的鍵樱拴。
  • Object.entries() 方法可以獲得對象的鍵值對的數(shù)組
const obj = { a: 1, b: 2 }
console.log(Object.entries(obj))  // [ [ 'a', 1 ], [ 'b', 2 ] ]
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value)
  // a 1
  // b 2
}
console.log(new Map(Object.entries(obj))) // Map(2) { 'a' => 1, 'b' => 2 }
  • Object.getOwnPropertyDescriptors() 獲取對象中屬性的完整描述信息

  • String.prototype.padStart(num, str) / String.prototype.padEnd 用指定字符串填充原字符串開頭或結(jié)尾柠衍,知道達(dá)到指定位數(shù)

console.log('wl'.padEnd(10, '----------')) // wl--------
console.log('102'.padStart(4, '0')) // 0102
  • 可以在函數(shù)參數(shù)中添加尾逗號
function(
  a,
  b,
) {

}
  • Async / Await
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洋满,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子珍坊,更是在濱河造成了極大的恐慌牺勾,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阵漏,死亡現(xiàn)場離奇詭異驻民,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)履怯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門回还,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人虑乖,你說我怎么就攤上這事懦趋×缆牵” “怎么了疹味?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帜篇。 經(jīng)常有香客問我糙捺,道長,這世上最難降的妖魔是什么笙隙? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任洪灯,我火速辦了婚禮,結(jié)果婚禮上竟痰,老公的妹妹穿的比我還像新娘签钩。我一直安慰自己,他們只是感情好坏快,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布铅檩。 她就那樣靜靜地躺著,像睡著了一般莽鸿。 火紅的嫁衣襯著肌膚如雪昧旨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天祥得,我揣著相機(jī)與錄音兔沃,去河邊找鬼。 笑死级及,一個胖子當(dāng)著我的面吹牛乒疏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饮焦,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼怕吴,長吁一口氣:“原來是場噩夢啊……” “哼入偷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起械哟,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疏之,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后暇咆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锋爪,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年爸业,在試婚紗的時候發(fā)現(xiàn)自己被綠了其骄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡扯旷,死狀恐怖拯爽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钧忽,我是刑警寧澤毯炮,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站耸黑,受9級特大地震影響桃煎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜大刊,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一为迈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缺菌,春花似錦葫辐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛾绎,卻和暖如春昆箕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背租冠。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工鹏倘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顽爹。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓纤泵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捏题,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容