ES6 - 運算符的擴展

1.指數(shù)運算符

ES2016 新增了一個指數(shù)運算符(**)妓湘。

2 ** 2 // 4
2 ** 3 // 8

這個運算符的一個特點是右結(jié)合鞍历,而不是常見的左結(jié)合。多個指數(shù)運算符連用時背蟆,是從最右邊開始計算的鉴分。

// 相當(dāng)于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512

上面代碼中哮幢,首先計算的是第二個指數(shù)運算符,而不是第一個志珍。

指數(shù)運算符可以與等號結(jié)合橙垢,形成一個新的賦值運算符(**=)。

let a = 1.5;
a **= 2;
// 等同于 a = a * a;

let b = 4;
b **= 3;
// 等同于 b = b * b * b;

2.鏈判斷運算符

編程中伦糯,如果讀取對象內(nèi)部的某個屬性柜某,往往需要判斷一下,屬性的上層對象是否存在敛纲。比如喂击,讀取message.body.user.firstName這個屬性,安全的寫法是寫成下面這樣淤翔。

// 錯誤的寫法
const  firstName = message.body.user.firstName || 'default';

// 正確的寫法
const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';

上面例子中翰绊,firstName屬性在對象的第四層,所以需要判斷四次旁壮,每一層是否有值监嗜。

三元運算符?:也常用于判斷對象是否存在。

const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined

上面例子中寡具,必須先判斷fooInput是否存在秤茅,才能讀取fooInput.value

這樣的層層判斷非常麻煩童叠,因此 ES2020 引入了“鏈判斷運算符”(optional chaining operator)?.,簡化上面的寫法课幕。

const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value

上面代碼使用了?.運算符厦坛,直接在鏈式調(diào)用的時候判斷,左側(cè)的對象是否為nullundefined乍惊。如果是的杜秸,就不再往下運算,而是返回undefined润绎。

下面是判斷對象方法是否存在铅忿,如果存在就立即執(zhí)行的例子喂江。

iterator.return?.()

上面代碼中,iterator.return如果有定義,就會調(diào)用該方法譬胎,否則iterator.return直接返回undefined,不再執(zhí)行?.后面的部分钦讳。

對于那些可能沒有實現(xiàn)的方法鲫剿,這個運算符尤其有用。

if (myForm.checkValidity?.() === false) {
  // 表單校驗失敗
  return;
}

上面代碼中涂佃,老式瀏覽器的表單對象可能沒有checkValidity()這個方法励翼,這時?.運算符就會返回undefined蜈敢,判斷語句就變成了undefined === false,所以就會跳過下面的代碼汽抚。

鏈判斷運算符?.有三種寫法抓狭。

  • obj?.prop // 對象屬性是否存在
  • obj?.[expr] // 同上
  • func?.(...args) // 函數(shù)或?qū)ο蠓椒ㄊ欠翊嬖?/li>

下面是obj?.[expr]用法的一個例子。

let hex = "#C0FFEE".match(/#([A-Z]+)/i)?.[1];

上面例子中造烁,字符串的match()方法辐宾,如果沒有發(fā)現(xiàn)匹配會返回null,如果發(fā)現(xiàn)匹配會返回一個數(shù)組膨蛮,?.運算符起到了判斷作用叠纹。

下面是?.運算符常見形式,以及不使用該運算符時的等價形式敞葛。

a?.b
// 等同于
a == null ? undefined : a.b

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()

上面代碼中誉察,特別注意后兩種形式,如果a?.b()a?.()惹谐。如果a?.b()里面的a.b有值持偏,但不是函數(shù),不可調(diào)用氨肌,那么a?.b()是會報錯的鸿秆。a?.()也是如此,如果a不是nullundefined怎囚,但也不是函數(shù)卿叽,那么a?.()會報錯。

使用這個運算符恳守,有幾個注意點考婴。

(1)短路機制

本質(zhì)上,?.運算符相當(dāng)于一種短路機制催烘,只要不滿足條件沥阱,就不再往下執(zhí)行。

a?.[++x]
// 等同于
a == null ? undefined : a[++x]

上面代碼中伊群,如果aundefinednull考杉,那么x不會進行遞增運算。也就是說舰始,鏈判斷運算符一旦為真崇棠,右側(cè)的表達式就不再求值。

(2)括號的影響

如果屬性鏈有圓括號蔽午,鏈判斷運算符對圓括號外部沒有影響易茬,只對圓括號內(nèi)部有影響。

(a?.b).c
// 等價于
(a == null ? undefined : a.b).c

上面代碼中,?.對圓括號外部沒有影響抽莱,不管a對象是否存在范抓,圓括號后面的.c總是會執(zhí)行。

一般來說食铐,使用?.運算符的場合匕垫,不應(yīng)該使用圓括號,否則失去了鏈判斷運算的意義虐呻。

(3)報錯場合

以下寫法是禁止的象泵,會報錯。

// 構(gòu)造函數(shù)
new a?.()
new a?.b()

// 鏈判斷運算符的右側(cè)有模板字符串
a?.`斟叼`
a?.b`{c}`

// 鏈判斷運算符的左側(cè)是 super
super?.()
super?.foo

// 鏈運算符用于賦值運算符左側(cè)
a?.b = c

(4)右側(cè)不得為十進制數(shù)值

為了保證兼容以前的代碼偶惠,允許foo?.3:0被解析成foo ? .3 : 0,因此規(guī)定如果?.后面緊跟一個十進制數(shù)字朗涩,那么?.不再被看成是一個完整的運算符忽孽,而會按照三元運算符進行處理,也就是說谢床,那個小數(shù)點會歸屬于后面的十進制數(shù)字兄一,形成一個小數(shù)。

3.Null 判斷運算符

讀取對象屬性的時候识腿,如果某個屬性的值是nullundefined出革,有時候需要為它們指定默認值。常見做法是通過||運算符指定默認值渡讼。

const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;

上面的三行代碼都通過||運算符指定默認值骂束,但是這樣寫是錯的。開發(fā)者的原意是硝全,只要屬性的值為nullundefined栖雾,默認值就會生效,但是屬性的值如果為空字符串或false0伟众,默認值也會生效。

為了避免這種情況召廷,ES2020 引入了一個新的 Null 判斷運算符??凳厢。它的行為類似||,但是只有運算符左側(cè)的值為nullundefined時竞慢,才會返回右側(cè)的值先紫。

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

上面代碼中,默認值只有在左側(cè)屬性值為nullundefined時筹煮,才會生效遮精。

這個運算符的一個目的,就是跟鏈判斷運算符?.配合使用,為nullundefined的值設(shè)置默認值本冲。

const animationDuration = response.settings?.animationDuration ?? 300;

上面代碼中准脂,如果response.settingsnullundefined,或者response.settings.animationDurationnullundefined檬洞,就會返回默認值300狸膏。也就是說,這一行代碼包括了兩級屬性的判斷添怔。

這個運算符很適合判斷函數(shù)參數(shù)是否賦值湾戳。

function Component(props) {
  const enable = props.enabled ?? true;
  // …
}

上面代碼判斷props參數(shù)的enabled屬性是否賦值,基本等同于下面的寫法广料。

function Component(props) {
  const {
    enabled: enable = true,
  } = props;
  // …
}

??本質(zhì)上是邏輯運算砾脑,它與其他兩個邏輯運算符&&||有一個優(yōu)先級問題,它們之間的優(yōu)先級到底孰高孰低艾杏。優(yōu)先級的不同韧衣,往往會導(dǎo)致邏輯運算的結(jié)果不同。

現(xiàn)在的規(guī)則是糜颠,如果多個邏輯運算符一起使用汹族,必須用括號表明優(yōu)先級,否則會報錯其兴。

// 報錯
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs

上面四個表達式都會報錯顶瞒,必須加入表明優(yōu)先級的括號。

(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);

(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);

(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);

(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);

4.邏輯賦值運算符

ES2021 引入了三個新的邏輯賦值運算符(logical assignment operators)元旬,將邏輯運算符與賦值運算符進行結(jié)合榴徐。

// 或賦值運算符
x ||= y
// 等同于
x || (x = y)

// 與賦值運算符
x &&= y
// 等同于
x && (x = y)

// Null 賦值運算符
x ??= y
// 等同于
x ?? (x = y)

這三個運算符||=&&=匀归、??=相當(dāng)于先進行邏輯運算坑资,然后根據(jù)運算結(jié)果,再視情況進行賦值運算穆端。

它們的一個用途是袱贮,為變量或?qū)傩栽O(shè)置默認值。

// 老的寫法
user.id = user.id || 1;

// 新的寫法
user.id ||= 1;

上面示例中体啰,user.id屬性如果不存在攒巍,則設(shè)為1,新的寫法比老的寫法更緊湊一些荒勇。

下面是另一個例子柒莉。

function example(opts) {
  opts.foo = opts.foo ?? 'bar';
  opts.baz ?? (opts.baz = 'qux');
}

上面示例中,參數(shù)對象opts如果不存在屬性foo和屬性baz沽翔,則為這兩個屬性設(shè)置默認值兢孝。有了“Null 賦值運算符”以后,就可以統(tǒng)一寫成下面這樣。

function example(opts) {
  opts.foo ??= 'bar';
  opts.baz ??= 'qux';
}

5.#!命令

Unix 的命令行腳本都支持#!命令跨蟹,又稱為 Shebang 或 Hashbang雳殊。這個命令放在腳本的第一行,用來指定腳本的執(zhí)行器喷市。

比如 Bash 腳本的第一行相种。

#!/bin/sh

Python 腳本的第一行。

#!/usr/bin/env python

ES2023 為 JavaScript 腳本引入了#!命令品姓,寫在腳本文件或者模塊文件的第一行寝并。

// 寫在腳本文件第一行
#!/usr/bin/env node
'use strict';
console.log(1);

// 寫在模塊文件第一行
#!/usr/bin/env node
export {};
console.log(1);

有了這一行以后,Unix 命令行就可以直接執(zhí)行腳本腹备。

# 以前執(zhí)行腳本的方式
$ node hello.js

# hashbang 的方式
$ ./hello.js

對于 JavaScript 引擎來說衬潦,會把#!理解成注釋,忽略掉這一行植酥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镀岛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子友驮,更是在濱河造成了極大的恐慌漂羊,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卸留,死亡現(xiàn)場離奇詭異走越,居然都是意外死亡,警方通過查閱死者的電腦和手機耻瑟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門旨指,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喳整,你說我怎么就攤上這事谆构。” “怎么了框都?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵搬素,是天一觀的道長。 經(jīng)常有香客問我魏保,道長蔗蹋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任囱淋,我火速辦了婚禮,結(jié)果婚禮上餐塘,老公的妹妹穿的比我還像新娘妥衣。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布税手。 她就那樣靜靜地躺著蜂筹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芦倒。 梳的紋絲不亂的頭發(fā)上艺挪,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音兵扬,去河邊找鬼麻裳。 笑死,一個胖子當(dāng)著我的面吹牛器钟,可吹牛的內(nèi)容都是我干的津坑。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼傲霸,長吁一口氣:“原來是場噩夢啊……” “哼疆瑰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起昙啄,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤穆役,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后梳凛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耿币,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年伶跷,在試婚紗的時候發(fā)現(xiàn)自己被綠了掰读。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡叭莫,死狀恐怖蹈集,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雇初,我是刑警寧澤拢肆,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站靖诗,受9級特大地震影響郭怪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刊橘,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一鄙才、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧促绵,春花似錦攒庵、人聲如沸嘴纺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栽渴。三九已至,卻和暖如春稳懒,著一層夾襖步出監(jiān)牢的瞬間闲擦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工场梆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留墅冷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓辙谜,卻偏偏與公主長得像俺榆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子装哆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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