ES6學習筆記篇一之基礎篇

一、ES6 和 JavaScript

ECMAScript、JavaScript、NodeJs,它們的區別是什麼?

ECMAScript:簡稱ES,是一個語言標準(循環、判斷、變量、數組等數據類型)

JavaScript:運行在瀏覽器端的語言,該語言使用ES標準。 ES + web api = JavaScript

NodeJs:運行在服務器端的語言,該語言使用ES標準。 ES + node api = JavaScript

無論JavaScript,還是NodeJs,它們都是ES的超集(super set)

ES6.0: 2015, 從該版本開始,不再使用數字作爲編號,而使用年份

二、let和const命令

1、使用var聲明變量

(1)允許重複的變量聲明:導致數據被覆蓋

var a = 100
//一萬行代碼
var a = 1

(2)變量提升:怪異的數據訪問、閉包問題(只有函數級作用域,沒有塊級作用域)

//先聲明一個var a,再執行if-else
if(Math.random()<0.5){
    var a = "abc"
    console.log(a)
}else{
    console.log(a)
}
console.log(a)

(3)全局變量掛載到全局對象:全局對象成員污染問題

var a = 10
console.log(window.a)//10

2、使用let聲明變量

塊級作用域:代碼執行時遇到花括號,會創建一個塊級作用域,花括號結束,銷燬塊級作用域。

let命令,用來聲明變量。但是所聲明的變量,只在let命令所在的代碼塊內有效。

(1)let聲明的變量不會掛載到全局對象

頂層對象

頂層對象,在瀏覽器環境指的是window對象,在 Node 指的是global對象。ES5 之中,頂層對象的屬性與全局變量是等價的。

ES6 規定,爲了保持兼容性,var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;let命令、const命令、class命令聲明的全局變量,不屬於頂層對象的屬性。全局變量將逐步與頂層對象的屬性脫鉤。

var a = 1;
console.log(window.a) // 1

let a = "ll"
console.log(window.a)//undefined

(2)不允許當前作用域範圍內重複聲明。在塊級作用域中用let定義的變量,在作用域外不能訪問

console.log(name)//會報錯
let name = "ll"
console.log(name)//"ll"
let name = "lk" //報錯,檢查到當前作用域已經聲明過,不允許重複聲明

(3)使用let不會有變量提升,因此,不能在定義let變量之前使用它

// var 的情況
console.log(a); // 輸出undefined
var a = 2;

// let 的情況
console.log(b); // 報錯ReferenceError
let b = 2;

暫時性死區

var a  = 10
if(true){
	a = 5
	let a = 10
}
存在全局變量a,但是if後的{}塊級作用域內let又聲明瞭一個局部變量a,導致後者綁定這個塊級作用域,所以在let聲明變量前,對a賦值會報錯。

ES6 明確規定,如果區塊中存在letconst命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。

底層實現上,let聲明的變量實際上也會有提升,但是,提升後會將其放入到“暫時性死區”,如果訪問的變量位於暫時性死區,則會報錯:“Cannot access ‘a’ before initialization”。當代碼運行到該變量的聲明語句時,會將其從暫時性死區中移除。

在循環中,用let聲明的循環變量,會特殊處理,每次進入循環體,都會開啓一個新的作用域,並且將循環變量綁定到該作用域(每次循環,使用的是一個全新的循環變量)。在循環中使用let聲明的循環變量,在循環結束後會銷燬

3、使用const聲明常量

constlet完全相同,僅在於const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。

實際上,在開發中,應該儘量使用const來聲明變量,以保證變量的值不會隨意篡改,原因如下:

1. 根據經驗,開發中的很多變量,都是不會更改,也不應該更改的。
2. 後續的很多框架或者是第三方JS庫,都要求數據不可變,使用常量可以一定程度上保證這一點。

(1)const聲明的變量不得改變值,這意味着,const一旦聲明變量,就必須立即初始化,不能留到以後賦值。

const a = 1
a = 3;// TypeError: Assignment to constant variable.

const b;// SyntaxError: Missing initializer in const declaration

(2)常量不可變,是指聲明的常量的內存空間不可變,並不保證內存空間中的地址指向的其他空間不可變。

const a = {};
// 爲 a 添加一個屬性,可以成功
a.prop = 123;
console.log(a.prop) // 123

// 將 a 指向另一個對象,就會報錯
a = {}; // TypeError: "a" is read-only

(3)命名規範

1. 特殊的常量:該常量從字面意義上,一定是不可變的,比如圓周率、月地距地或其他一些絕不可能變化的配置。通常,該常量的名稱全部使用大寫,多個單詞之間用下劃線分割
2. 普通的常量:使用和之前一樣的命名即可

(4)在for循環中,循環變量不可以使用常量,for-in循環可以

三、字符串的擴展

1、更好的Unicode支持

早期,由於存儲空間寶貴,Unicode使用16位二進制來存儲文字。我們將一個16位的二進制編碼叫做一個碼元(Code Unit)。後來,由於技術的發展,Unicode對文字編碼進行了擴展,將某些文字擴展到了32位(佔用兩個碼元),並且,將某個文字對應的二進制數字叫做碼點(Code Point)。

一個字符對應一個碼點(且每個字符的碼點都是唯一的),但一個碼點可能需要一個代碼單元或者兩個代碼單元才能表示。

charCodeAt():讀取一個碼元

例子 𠮷:碼點是0x20BB7,UTF-16 編碼爲0xD842 0xDFB7

const text = "𠮷"; //佔用了兩個碼元(32位)
console.log("字符串長度:", text.length); //2
console.log("使用正則測試:", /^.$/.test(text));//false 讀到了兩個碼元,希望匹配一個 

console.log("得到第一個碼元:", text.charCodeAt(0));//55362
console.log("得到第二個碼元:", text.charCodeAt(1));//57271

codePointAt():根據字符串碼元的位置得到其碼點。能夠正確處理 4 個字節儲存的字符,返回一個字符的碼點。

//𠮷:\ud842\udfb7
console.log("得到第一個碼點:", text.codePointAt(0));//134071  獲取完整的
console.log("得到第二個碼點:", text.codePointAt(1));//57271   

同時,ES6爲正則表達式添加了一個flag: u,如果添加了該配置,則匹配時,使用碼點匹配

console.log("使用正則測試:", /^.$/u.test(text));
/**
 * 判斷字符串char,是32位,還是16位
 * @param {*} char 
 */
function is32bit(char, i) {
    //如果碼點大於了16位二進制的最大值,則其是32位的
    return char.codePointAt(i) > 0xffff;
}

/**
 * 得到一個字符串碼點的真實長度
 * @param {*} str 
 */
function getLengthOfCodePoint(str) {
    var len = 0;
    for (let i = 0; i < str.length; i++) {
        //i在索引碼元
        if (is32bit(str, i)) {
            //當前字符串,在i這個位置,佔用了兩個碼元
            i++;
        }
        len++;
    }
    return len;
}

console.log("𠮷 是否是32位的:", is32bit("𠮷", 0))
console.log("ab𠮷ab 的碼點長度:", getLengthOfCodePoint("ab𠮷ab"))

字符串的遍歷器for...of循環遍歷

const text = "𠮷as";
for(let i=0;i<text.length;i++)
{
	console.log(text[i]) //� � a s
}
//普通for循環無法正確識別。一次只能讀取一個代碼單元


//for-of可以正確識別一個完整的符號
const text = "𠮷as";
for(const item of text)
{
	console.log(item) //𠮷 a s
}

2、字符串新增API

以下均爲字符串的實例方法

(1)includes():判斷字符串中是否包含指定的子字符串,第二個參數指定從哪個位置開始查找,返回true和false

const str = "今天天氣很好啊!"
const result1 = str.includes("氣")
const result2 = str.includes("氣",5)
console.log(result1,result2)

(2)startsWith():判斷字符串中是否以指定的字符串開始,也可以傳第二個參數

const str = "今天天氣很好啊!"
const result = str.startsWith("氣")
console.log(result)

(3)endsWith():判斷字符串中是否以指定的字符串結尾

const str = "今天天氣很好啊!"
const result = str.endsWith("!")
console.log(result)

(4)repeat():將字符串重複指定的次數,然後返回一個新字符串。

const str = "我很帥!"
const result = str.repeat(4)
console.log(result)//我很帥!我很帥!我很帥!我很帥!

//如果repeat的參數是負數或者Infinity,會報錯。
const result = 'a'.repeat(Infinity)// RangeError
const result = 'a'.repeat(-1)// RangeError

補充

ES2017 引入了字符串補全長度的功能。如果某個字符串不夠指定長度,會在頭部或尾部補全。padStart()用於頭部補全,padEnd()用於尾部補全。實例方法

(5)padStart():第一個參數是字符串補全生效的最大長度,第二個參數是用來從起始位置補全的字符串。

'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(4) // 'x   '

(6)padEnd() :第一個參數是字符串補全生效的最大長度,第二個參數是用來從結束位置補全的字符串。

'x'.padEnd(5, 'ab') // 'xabab'
'12'.padStart(10, '0') // "0000000012"

注意:省略第二個參數,則默認用空格補全。padEnd方法常用來數字前補零

ES2019對字符串實例新增了trimStart()trimEnd()這兩個方法。它們的行爲與trim()一致,trimStart()消除字符串頭部的空格,trimEnd()消除尾部的空格。它們返回的都是新字符串,不會修改原始字符串。

(7)trimStart()trimEnd():消除頭部/尾部空格

const str = '  abc  ';

consst r1 = str.trim() // "abc"
consst r2 = str.trimStart() // "abc  "
consst r3 = str.trimEnd() // "  abc"

注意:除了空格鍵,這兩個方法對字符串頭部(或尾部)的 tab 鍵、換行符等不可見的空白符號也有效。瀏覽器還部署了額外的兩個方法,trimLeft()trimStart()的別名,trimRight()trimEnd()的別名。

3-3. [擴展]正則中的粘連標記 幾乎用不到

標記名:y

含義:匹配時,完全按照正則對象中的lastIndex位置開始匹配,並且匹配的位置必須在lastIndex位置。

const text = "Hello World!!!"
const reg = /w\w+/y;//以w開頭的單詞
reg.lastIndex = 6;
//從lastIndex的位置必須匹配,一開始lastIndex爲0
console.log(reg.test(text))

3、模板字符串

模板字符串是增強版的字符串,用反引號(`)標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。

const text = 
`我很帥
我真的很帥 `;
console.log(text);

如果要在字符串中拼接js表達式,只需要在模板字符串中使用${JS表達式}

const age = 18
console.log(`我今年${age}歲`)
console.log(`計算結果:${1+2+3*2}`)

4、模板字符串標記

在模板字符串書寫之前,可以加上標記:

標記名`模板字符串`

標記是一個函數,函數參數如下:

  1. 參數1:被插值分割的字符串數組
  2. 後續參數:所有的插值
const name = "llx"
const age = 18
// 相當於 text = myTag(["我的名字叫:",",我今年","歲"],"llx",18)
var text = myTag`我的名字叫:${name},我今年${age}歲`
function myTag(parts,arg1,arg2){
    console.log(parts)
    console.log(arg1)
    console.log(arg2)
}
console.log(text)//(3) ["我的名字叫:", ",我今年", "歲", raw: Array(3)]
const name = "llx"
const age = 18
var text = myTag`我的名字叫:${name},我今年${age}歲`
// 相當於
// text = myTag(["我的名字叫:",",我今年","歲"],"llx",18)
function myTag(parts){
    console.log(parts)
    //先將arguments轉化爲真實數組,再除去第一個參數,parts
    const values = Array.prototype.slice.apply(arguments).splice(1)
    console.log(values)
    let str = ""
    for(let i=0;i<parts.length;i++){
        if(parts.length-1 == i){
            str += parts[i]
        }else{
            str += parts[i]+values[i]
        }
    }
    return str
}
console.log(text)

String.raw將轉義字符原樣輸出

const text = String.raw`abc\ndef`
console.log(text)

四、函數的擴展

1、 參數默認值

在書寫形參時,直接給形參賦值,附的值即爲默認值。這樣一來,當調用函數時,如果沒有給對應的參數賦值(給它的值是undefined),則會自動使用默認值。

//c的默認值是2
function sum(a = 0,b = 0,c = 2){
    return a + b + c;
}
console.log(sum(1,undefined,3))

注意:參數變量是默認聲明的,所以不能用letconst再次聲明。

對arguments的影響

只要給函數加上參數默認值,該函數會自動變量嚴格模式下的規則:arguments和形參脫離

比較二者區別

function test(a,b){
    console.log("arguments:",arguments[0],arguments[1])
    console.log("a:",a," ,b:",b)
    a = 3
    console.log("arguments:",arguments[0],arguments[1])
    console.log("a:",a," ,b:",b)
}
test(1,2)
function test(a,b=1){
    console.log("arguments:",arguments[0],arguments[1])
    console.log("a:",a," ,b:",b)
    a = 3
    console.log("arguments:",arguments[0],arguments[1])
    console.log("a:",a," ,b:",b)
}
test(1,2)

暫時性死區

形參和ES6中的let或const聲明一樣,具有作用域,並且根據參數的聲明順序,存在暫時性死區。

function test(a,b = a){
    console.log(a,b)
}
test(1,2)
test(undefined,1)

function test1(a = b,b){
    console.log(a,b)
}
test1(1,2)
test1(undefined,1)//報錯 Cannot access 'b' before initialization

2、rest參數

ES6的rest參數用於獲取函數的多餘參數,這樣就不需要使用arguments對象了。rest 參數會將多餘的參數放入數組中。形如:...形參名

arguments對象不是數組,而是一個類似數組的對象。所以爲了使用數組的方法,必須使用Array.prototype.slice.call先將其轉爲數組。rest 參數就不存在這個問題,它就是一個真正的數組,數組特有的方法都可以使用。

function test(a,...args){
    console.log(a,args)//1  [2, 3, 4, 5]
}
test(1,2,3,4,5)
function sum(...args){
   let result = 0;
   for(let i=0;i<args.length;i++){
    result += args[i];
   }
   return result
}
console.log(sum(1,2,3,4,5))

注意:一個函數,僅能出現一個剩餘參數。一個函數,如果有剩餘參數,剩餘參數必須是最後一個參數

補充:從 ES5 開始,函數內部可以設定爲嚴格模式。ES2016 做了一點修改,規定只要函數參數使用了默認值、解構賦值、或者擴展運算符,那麼函數內部就不能顯式設定爲嚴格模式,否則會報錯。

3、展開運算符

使用方式:...要展開的東西

(1)對數組展開 ES6

//克隆數組arr1到arr2
const arr1 = [1,2,3,4,5]
const arr2 = [...arr1]

例子:

function sum(...args){
   let result = 0;
   for(let i=0;i<args.length;i++){
    result += args[i];
   }
   return result
}
function getRandomNumbers(length){
    let arr = [] 
    for(let i=0;i<length;i++){
        arr.push(Math.random())
    }
    return arr
}
const numbers = getRandomNumbers(10)
//展開的內容是numbers數組中的,相當於傳遞了10個參數
const result = sum(...numbers)
console.log(result)

(2)對對象展開 (ES2017)

const obj1 = {
    name:"lkx",
    age:18,
    address:{
    	country:"山東"
    }
}
//淺克隆
const obj2 = {
    ...obj1,
    age:10
}
//克隆深度對象
const obj3 = {
    ...obj,
    age:10,
    address:{
    	...obj1.address
    }
}

柯里化:用戶固定某個函數的前面的參數,得到一個新的函數,新的函數調用時,接收剩餘的參數

function cal(a,b,c,d){
    return a + b *c -d
}
// 柯里化函數
function curry(func,...args){
    return function(...subArgs){
        const allArgs = [...args,...subArgs]
        if(allArgs.length>=func.length){
            //參數夠了
            return func(...allArgs)
        }else{
            //參數不夠,繼續固定
            return curry(func,...allArgs)
        }
    }
}
const newCal = curry(cal,1,2)
console.log(newCal(2,4))

4、 明確函數的雙重用途

ES6提供了一個特殊的API,可以使用該API在函數內部,判斷該函數是否使用了new來調用

new.target
//該表達式,得到的是:如果沒有使用new來調用函數,則返回undefined
//如果使用new調用函數,則得到的是new關鍵字後面的函數本身 
function Person(firstName,lastName){
    //判斷是否是使用new的方式來調用的函數
    //過去的判斷方式,判斷this指向,但是用call和apply可能繞開
    if(this instanceof Person){
        throw new Error("該函數沒有使用new創建")
    }
    //es6提供的判斷方式
    if(new.target === undefined){
        throw new Error("該函數沒有使用new創建")
    }
    this.firstName = firstName
    this.lastName = lastName
    this.fullName = firstName + lastName
}
const p1 = new Person("張","飛")
console.log(p1)
const p2 = Person("張","飛")
console.log(p2)

5、箭頭函數

ES6 允許使用“箭頭”(=>)定義函數。形如:(參數1, 參數2, ...)=>{ //函數體 }

var func = value => value;

// 等同於
var func = function (value) {
  return value;
};

箭頭函數是一個函數表達式,理論上,任何使用函數表達式的場景都可以使用箭頭函數

const printNums = (num1,num2,num3) =>{
    const result = num1 + num2 * num3
    console.log(result)
}

如果參數只有一個,可以省略小括號

const print = num =>{
    num += 1
    console.log(num)
}

如果箭頭函數只有一條返回語句,可以省略大括號,和return關鍵字

//返回true和false
const isOdd = num => num%2 !== 0 

注意:由於大括號被解釋爲代碼塊,所以如果箭頭函數直接返回一個對象,必須在對象外面加上括號,否則會報錯。

// 報錯
let getObject = id => { id: id, name: "ll" };

// 不報錯
let getObject = id => ({ id: id, name: "ll" });

關於this指向

在以往的學習中,對於this指向

1. 通過對象調用函數,this指向對象
2. 直接調用函數,this指向全局對象
3. 如果通過new調用函數,this指向新創建的對象
4. 如果通過apply、call、bind調用函數,this指向指定的數據
5. 如果是DOM事件函數,this指向事件源
//想要在定時器,事件中使用this,需要提前保存起來
const obj = {
    count : 0,
    start:function(){
        let _this = this
        setInterval(function(){
            _this.count++
            console.log(_this.count)
        }, 1000);
    },
    regEvent:function(){
        let _this = this
        window.onclick = function(){
            console.log(_this.count)
        }
    }
}
obj.start()
obj.regEvent()

而箭頭函數中的this指向,他是固定的。函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

箭頭函數有幾個使用注意點。

(1)函數體內的`this`對象,就是定義時所在的對象,而不是使用時所在的對象。
(2)不可以當作構造函數,也就是說,不可以使用`new`命令,否則會拋出一個錯誤。
(3)不可以使用`arguments`對象,該對象在函數體內不存在。如果要用,可以用 rest 參數代替。
(4)不可以使用`yield`命令,因此箭頭函數不能用作 Generator 函數。
上面四點中,第一點尤其值得注意。`this`對象的指向是可變的,但是在箭頭函數中,它是固定的。

箭頭函數中,不存在this、arguments、new.target,如果使用了,則使用的是函數外層的對應的this、arguments、new.target

//箭頭函數中的this取決於聲明箭頭函數的位置
const obj = { 
    count: 0,
    start:function(){
        // let _this = this
        setInterval(() => {
            this.count++
            console.log(this.count)
        }, 1000);
    },
    regEvent:function(){
        window.onclick = ()=>{
            console.log(this.count)
        }
    }
}
obj.start()
obj.regEvent()
const func = ()=>{
    console.log(this)//window
    console.log(arguments)//Uncaught ReferenceError
}
const obj = {
    method:func
}
obj.method(234)
const obj = {
method:function(){
    const func = ()=>{
        console.log(this)//obj
        console.log(arguments)//method的arguments
    }
    func()
}
}
obj.method(234)

應用場景

  1. 臨時性使用的函數,並不會可以調用它,比如:
    1. 事件處理函數
    2. 異步處理函數,settimeout,setinterval
    3. 其他臨時性的函數
  2. 爲了綁定外層this的函數
  3. 在不影響其他代碼的情況下,保持代碼的簡潔,最常見的,數組方法中的回調函數
const numbers = [3,5,6,32,78,24]
const result = numbers.filter(num=>num%2!==0).map(num=>num*2).reduce((a,b)=>a+b)
console.log(result)

6、Function.prototype.toString()

ES2019 對函數實例的**toString()**方法做出了修改。

toString()方法返回函數代碼本身,以前會省略註釋和空格。

7、catch 命令的參數省略

JavaScript 語言的try...catch結構,以前明確要求catch命令後面必須跟參數,接受try代碼塊拋出的錯誤對象。ES2019try...catch,允許catch語句省略參數。

try {
  // ...
} catch (err) {
  // 處理錯誤
}
//---Es2019以後
try {
  // ...
} catch {
  // ...
}

五、對象的擴展

1、新增的對象字面量語法

(1)成員速寫

如果對象字面量初始化時,成員的名稱來自於一個變量,並且和變量的名稱相同,則可以進行簡寫

const name = "張飛"
const age = 18
const obj = {
   name,
   age
}

(2)方法速寫

對象字面初始化時,方法可以省略冒號和function關鍵字

const obj = {
    sayHellow(){
        console.log("hello world")
    }
}

(3)計算屬性名

有的時候,初始化對象時,某些屬性名可能來自於某個表達式的值,在ES6,可以使用中括號來表示該屬性名是通過計算得到的。

const key = "name"
const name = "lkx"
const obj = {
    [key]:name
}

(4)對象屬性的遍歷

ES6 一共有 5 種方法可以遍歷對象的屬性。(在Es6後續中介紹)

(1)for…in

for...in循環遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)。

(2)Object.keys(obj)

Object.keys返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一個數組,包含對象自身的所有屬性(不含 Symbol 屬性,但是包括不可枚舉屬性)的鍵名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一個數組,包含對象自身的所有 Symbol 屬性的鍵名。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一個數組,包含對象自身的所有鍵名,不管鍵名是 Symbol 或字符串,也不管是否可枚舉。

以上的 5 種方法遍歷對象的鍵名,都遵守同樣的屬性遍歷的次序規則

  • 首先遍歷所有數值鍵,按照數值升序排列。
  • 其次遍歷所有字符串鍵,按照加入時間升序排列。
  • 最後遍歷所有 Symbol 鍵,按照加入時間升序排列。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]

上面代碼中,Reflect.ownKeys方法返回一個數組,包含了參數對象的所有屬性。這個數組的屬性次序是這樣的,首先是數值屬性210,其次是字符串屬性ba,最後是 Symbol 屬性。

2、鏈判斷運算符和Null 判斷運算符

(1)鏈判斷運算符

鏈判斷運算符有三種用法:

1、obj?.prop // 對象屬性
2、obj?.[expr] // 同上
3、func?.(...args) // 函數或對象方法的調用

在項目中經常會讀取某個對象中的屬性。例如:result.message.user.firstName

我們通常的寫法爲。

const firstName = (result && result.message && result.message.user&& result.message.user.firstName) || '暫無數據'; 

這樣的層層判斷非常麻煩,因此ES2020引入了鏈判斷運算符?.,簡化上面的寫法。

const firstName = result?.message?.user?.firstName || '暫無數據';

使用了?.運算符,直接在鏈式調用的時候判斷,左側的對象是否爲nullundefined。如果是的,就不再往下運算,而是返回undefined

(2)Null判斷運算符

我們在讀取對象屬性的時候,如果某個屬性的值是nullundefined,有時候需要爲它們指定默認值。我們經常用||運算符指定默認值。

const firstName = result.firstName || '暫無數據'; 

但是屬性的值如果爲空字符串或false0,默認值也會生效。爲了避免這種情況,ES2020 引入了一個新的 Null 判斷運算符??。它的行爲類似||,但是隻有運算符左側的值爲nullundefined時,纔會返回右側的值。

const firstName = result.firstName ?? '暫無數據'; 

該運算符一般配合.?使用

const firstName = result?.message?.user?.firstName ?? '暫無數據';

3、Object的新增API

(1)Object.is()

用於判斷兩個數據是否相等,基本上跟嚴格相等(===)是一致的,除了以下兩點:

  1. NaN和NaN相等
  2. +0和-0不相等
console.log(NaN === NaN) //false
console.log(+0 === -0) //true

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

(2)Object.assign(target,source1,source2…)

方法的第一個參數是目標對象,後面的參數都是源對象。將源對象(source)的所有可枚舉屬性,複製到目標對象(target)。

const obj1 = {
    name:"l"
}
const obj2 = {
    name:"lkx",
    age:18
}
const obj = Object.assign(obj1,obj2)
console.log(obj)
console.log(obj1 === obj) //true

注意:用於混合對象,後面的覆蓋前面的,會改變obj1

//通常我們會把第一個參數寫爲一個空對象
const lastObj = Object.assign({},obj1,obj2)

再注意:方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個屬性的值是對象,那麼目標對象拷貝得到的是這個對象的引用。一旦遇到同名屬性,Object.assign的處理方法是替換,而不是添加。

(3)Object.getOwnPropertyNames

Object.getOwnPropertyNames方法之前就存在,只不過,官方沒有明確要求,對屬性的順序如何排序,如何排序,完全由瀏覽器廠商決定。

ES6規定了該方法返回的數組的排序方式如下:

  • 先排數字,並按照升序排序
  • 再排其他,按照書寫順序排序
const obj = {
    a:"a",
    b:"b",
    c:"c",
    0:0,
    1:1
}
console.log(Object.getOwnPropertyNames(obj))
//["0", "1", "a", "b", "c"]

(4)Object.setPrototypeOf()和Object.getPrototypeOf()

setPrototypeOf() 該函數用於設置某個對象的隱式原型__proto__

比如: Object.setPrototypeOf(obj1, obj2),
相當於: obj1.__proto__ = obj2

const obj1 = {
   a:1
}
const obj2 = {
   b:2
}
Object.setPrototypeOf(obj1,obj2)
//將obj1的隱式原型改爲obj2

Object.getPrototypeOf():該方法與Object.setPrototypeOf方法配套,用於讀取一個對象的原型對象。

(5)Object.keys(),Object.values(),Object.entries() ,Object.fromEntries()

Object.keys():返回一個數組,返回對象的所有屬性的鍵名。(ES5 )

var obj = { a: 1, b: 2 };
Object.keys(obj) // ["a", "b"]

Object.values():方法返回一個數組,返回對象的所有屬性的鍵值。(ES2017)

var obj = { a: 1, b: 2 };
Object.values(obj) // ["1", "2"]

Object.entries():方法返回一個數組,返回對象的所有屬性鍵值對數組。(ES2017)

var obj = { a: 1, b: 2 };
Object.entries(obj) // [["a","1"], ["b","2"]]

Object.fromEntries() :是Object.entries()的逆操作,用於將一個鍵值對數組轉爲對象。

var arr = [["a","1"], ["b","2"]]
Object.fromEntries(arr)//{ a: 1, b: 2 }

六、變量的解構賦值

解構:使用ES6的一種語法規則,將一個對象或數組的某個屬性提取到某個變量中。解構不會對被解構的目標造成任何影響。

1、對象解構

const user = {
    name:"lkx",
    age:18,
    address:{
        provice:"山東",
        city:"濟南"
    }
}
const {name,age,sex,address} = user
console.log(name,age,address) //lkx 18 {provice: "山東", city: "濟南"}
在解構中使用默認值
//{同名變量 = 默認值}
const {name,age,address,sex="男"} = user
console.log(name,age,address,sex)//lkx 18 {provice: "山東", city: "濟南"} 男

2、非同名屬性解構

{屬性名:變量名}
const {name,age:nianling=20,address,sex="男"} = user
console.log(name,nianling,address,sex)

3、數組解構

const numbers = ["a","b","c","d"]
const [n0,n1] = numbers
console.log(n0,n1)

const {2:n2,3:n3} = numbers
console.log(n2,n3)

4、參數解構

function print({ name, age, sex, address: {province,city} }) {
    console.log(`姓名:${name}`)
    console.log(`年齡:${age}`)
    console.log(`性別:${sex}`)
    console.log(`省份:${province}`)
    console.log(`城市:${city}`)
}
const user = {
    name: "lkx",
    age: 18,
    sex: "男",
    address: {
        province: "山東",
        city: "濟南"
    }
}
print(user)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章