你在拼多多到處找人砍價,他在滴滴叫車求人助力,我在電子廠擰螺絲擰到凌晨,我們都有光明的未來!早安,打工人!
楔子
作為一名擁有鋼鐵般意志的前端打工人,裝逼是不可能的,這輩子都不可能裝逼。如果真要裝逼,那就大家一起裝逼,畢竟前端要講武德嘛,要耗子尾汁。遂決定寫下前端裝逼技巧108式,供諸君茶餘飯後一樂,時不時秀個騷操作,為打工的生活增添一抹亮色。
因作為打工人,時間、精力有限,目前大綱只有約50式,還請諸君有好的裝逼要點私信或者在評論區留言,也可在我的部落格頁面掃碼新增微信,大家共同探討裝逼大計、共同迎接打工人的光明未來!
系列文章力求通過一行程式碼或者一個小的切入點去理解一個知識點,文章風格所限,引用資料部分,將在對應小節末尾標出。
第一式:子曰,公欲裝逼好,工具少不了
- 程式碼太醜陋,carbon來相救:把你的程式碼轉換為精美圖片進行分享(點選圖片跳轉)
本文為便於程式碼複製,將奉行不首先裝逼原則,儘量減少此裝逼利器的使用。
第二式:console
除錯萬金油,學會開車更上頭
console.log()
在前端除錯中的地位自不必贅述,其實一代車神也對其五體投地,不信諸君細看(如真有不解其意者,建議發揚不恥下問的求知精神,問問你旁邊的同事):
是的,以上圖片是由console.log()
完成的,我沒有騙你,貼出程式碼以證清白,為便於諸君控制檯開車,此處我們忘掉第一式:
// 在此提醒,為免於生成醜陋的鋸齒背景圖片,請注意空格的個數,並保證console皮膚的寬度。
console.log(`%c
%c FBI WARNING %c
%c Federal Law provides severe civil and criminal penalties for
the unauthorized reproduction,distribution, or exhibition of
copyrighted motion pictures (Title 17, United States Code,
Sections 501 and 508). The Federal Bureau of Investigation
investigates allegations of criminal copyright infringement
(Title 17, United States Code, Section 506).
`,
'background: #000; font-size: 18px; font-family: monospace',
'background: #f33; font-size: 18px; font-family: monospace; color: #eee; text-shadow:0 0 1px #fff',
'background: #000; font-size: 18px; font-family: monospace',
'background: #000; font-size: 18px; font-family: monospace; color: #ddd; text-shadow:0 0 2px #fff'
)
為什麼會這樣呢?想必你還記得其他語言中的print()
。佔位符是print()
的專屬嗎?不,他們在console.log()
中同樣適用:
%s
:字串%d
:整數%i
:整數%f
:浮點數%o
:obj物件(DOM)%O
:obj物件%c
:CSS樣式
console.log()
可以通過以上這些特有的佔位符進行資訊的加工輸出。是的,你可能已經明白,上面程式碼的玄機就在四個%c
,第一個建立神祕而性感的純黑背景;第二個給“FBI WARNING”加上紅色的背景;第三個恢復純黑的性感;第四個配上白色的文字,如此,大事已成。
明白了以上原理,諸君就可以自由發揮,展示你們強大的css實力了,甚至還可以輸出gif背景圖,在裝逼的路上更上幾層樓。不裝了,我是css渣渣。
console.log(
'%c孤篷',
'text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);font-size:5em'
)
那麼,我們是否可以超越度娘,在自家公司官網控制檯完成精美的招聘文案投送呢?
擴充:console
物件都有哪些方法?
參考資料:小蝌蚪日記:通過console.log高仿FBI Warning | Using the F12 Tools Console to View Errors and Status?redirectedfrom=MSDN)
第三式:芙蓉面,楊柳腰,無物比妖嬈 —— 讓你看清UI的輪廓
UI輪廓哪裡尋,
outline
屬性來幫您。html * { outline: 1px solid red }
解析與思考
- 這裡沒有使用 border 的原因是 border 會增加元素的大小但是 outline 不會;
- 通過這個技巧不僅能幫助我們在開發中迅速瞭解元素所在的位置,還能幫助我們方便地檢視任意網站的佈局;
- 所有瀏覽器都支援 outline 屬性;outline (輪廓)是繪製於元素周圍的一條線,位於邊框邊緣的外圍,可起到突出元素的作用;
- 輪廓線不會佔據空間,也不一定是矩形(比如2D轉換等)。
去掉Chrome瀏覽器中輸入框以及其它表單控制元件獲得焦點時的帶顏色邊框
input { outline: none; }
通過一個開關實現任意網頁開啟關閉outline
- Chrome右上角三個點⇒書籤⇒書籤管理器⇒右上角三個點⇒「新增新書籤」;
- 名稱隨意,貼上以下程式碼到網址中;
然後我們就可以在任意網站上點選剛才建立的書籤,內部會判斷是否存在除錯的 style。存在的話就刪除,不存在的話就新增,通過這種方式我們就能很方便的通過這個技巧檢視任意網頁的佈局了。
javascript: (function() { var elements = document.body.getElementsByTagName('*'); var items = []; for (var i = 0; i < elements.length; i++) { if (elements[i].innerHTML.indexOf('html * { outline: 1px solid red }') != -1) { items.push(elements[i]); } } if (items.length > 0) { for (var i = 0; i < items.length; i++) { items[i].innerHTML = ''; } } else { document.body.innerHTML += '<style>html * { outline: 1px solid red }</style>'; } })();
參考資料:很好用的 UI 除錯技巧
第四式:角聲寒,夜闌珊,又改需求。難,難,難!—— 型別轉換助你不帶髒字的罵產品、優雅的誇自己
(!(~+[])+{})[--[~+""][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]*~+[]]]
:sb([][[]]+[])[+!![]]+([]+{})[!+[]+!![]]
:nb(+!![]*([]+{})+[]+{})[+[]]+([]+{})[!+[]+!![]]
:Nb
圖解:取型別轉換得到的字串裡的字母進行拼湊(看懂了原理,其實我們完全可以嘗試寫的更簡練一些)
外掛:zhuangbility,一個可以逆向操作,輸入文字,返回操作符的npm外掛
第五式:a == 1 && a == 2 && a == 3
,那你可以實現a === 1 && a === 2 && a === 3
嗎?
a == 1 && a == 2 && a == 3
:// 當然,你也可以把count作為屬性放在a物件上 let count = 1 let a = { valueOf: function(){return count++} } console.log(a==1 && a==2 && a==3) // true
- 物件在轉換基本型別時,會呼叫該物件上
valueOf
或toString
這兩個方法,該方法的返回值是轉換為基本型別的結果 - 具體呼叫以上哪個方法取決於內建的
toPrimitive
呼叫結果
- 物件在轉換基本型別時,會呼叫該物件上
a === 1 && a === 2 && a === 3
:
let count = 1;
Object.defineProperty(window, 'a', {
get: function() {
return count ++;
}
});
console.log(a===1 && a===2 && a===3) // true
Object.defineProperty()
方法會直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性,並返回此物件。同時,該API也是Vue 2.x資料繫結實現的核心,Vue 在 3.x 版本之後改用 Proxy 進行實現,本系列文章後續會進行簡單討論。原理可參考:譯:在JS中,如何讓(a===1 && a===2 && a === 3)(嚴格相等)的值為true? | 深入淺出Object.defineProperty() | ECMAScript7規範中的ToPrimitive抽象操作
第六式:最近有點兒火的 Web Components 可能並不是小鮮肉
Web Components原理:
- html很寬鬆,瀏覽器也可以識別不規則、不合法標籤(元素)(如
<custom-label>Web Components</custom-label>
會展示"Web Components"。); - 自定義繼承自
HTMLElement
的類,稱為自定義元素的類; - 經過
window.customElements.define
API定義和註冊自定義元素,使得不合法標籤(自定義元素)與自定義元素的類關聯,實現合法化; - 通過模板標籤
<template>
簡化類的定義過程並新增樣式; - 通過自定義元素的
attachShadow()
方法開啟 Shadow DOM(這部分 DOM 預設與外部 DOM 隔離,內部任何程式碼都無法影響外部),隱藏自定義元素的內部實現; - 新增事件監聽、進行元件化封裝等。
參考資料:Web Components 入門例項教程-阮一峰 | Window.customElements
第七式:Windows環境變數設定其實可以很簡單
使用Windows系統電腦進行開發的小夥伴也許經常會碰到需要手動設定環境變數的情況,其實設定環境變數也可以很簡單的通過命令列完成:
# 檢視當前所有可用的環境變數
set
# 檢視某個環境變數:檢視path變數的值
set path
# 修改環境變數(注意:這裡是覆蓋)
set 變數名=變數內容
# 設定為空
set 變數名=
# 給變數追加內容(%變數名%;代表以前的值)
set 變數名=%變數名%;變數內容
# 將C:\Go\bin\新增到path中
set path=%path%;C:\Go\bin\
參考資料:Windows使用cmd命令列檢視、修改、刪除與新增環境變數
第八式:1.toFixed()
、1.0.toFixed()
和1..toFixed()
,究竟哪個寫法是對的?
在數字字面量中,1.xxxxx
這樣的語法是浮點數表示法。所以1.toFixed()
這樣的語法在 JavaScript 中會報錯,這個錯誤來自於浮點數的字面量解析過程,而不是“.作為存取運算子”的處理過程。在 JavaScript 中,浮點數的小位數是可以為空的,因此“1.”和“1.0”將作為相同的浮點數被解析出來。所以會出現:
1. === 1; // true
1. === 1.0; // true
1 === 1.0; // true
1.; // 1
1.0; // 1
既然“1.”表示的是浮點數,那麼“1..toFixed”表示的就是該浮點數字面量的“.toFixed”屬性。當是數字字面量時,可通過類似Number(1).toFixed()
建立基本包裝型別(顯示裝箱),然後就可以進行屬性和方法的新增、讀取(或者可藉助小括號把字面量括起來,告訴瀏覽器引擎這是一個整體)。
- 裝箱:將基本資料型別轉換為對應的引用型別的操作(裝箱又分為隱式裝箱和顯式裝箱);
- 拆箱:把引用型別轉換成基本資料型別。
基本型別不能有屬性和方法,當給它們新增屬性的時候系統會自動進行包裝類並銷燬:
var num = 1;
num.len = 2;
//上一行程式碼會產生如下過程:
// new Number(1).len =2;
// delete len;
// 也就是會先新增len屬性,當前語句執行結束後即銷燬,所以下一行列印num還是1,沒有len屬性。
console.log(num, num.len);//1 undefined
var num = new Number(1);
num.len = 2;
console.log(num); // Number {1, len: 2}
參考擴充:談談JavaScript中裝箱和拆箱
第九式:typeof不靠譜,我們又該如何判斷型別?
typeof
之殤:我們應該都知道,使用typeof
可以準確判斷除null
以外的基本型別,以及function
、symbol
型別;null
會被typeof
判斷為object
。- 在 JavaScript 最初的實現中,JavaScript 中的值是由一個表示型別的標籤和實際資料值表示的。物件的型別標籤是 0。由於 null 代表的是空指標(大多數平臺下值為 0x00),因此,null 的型別標籤是 0,typeof null 也因此返回 "object";
- 在 ES 6 之前,typeof 總能保證對任何所給的運算元返回一個字串。即便是沒有宣告的識別符號,typeof 也能返回 'undefined'。使用 typeof 永遠不會丟擲錯誤。但在加入了塊級作用域的 let 和 const 之後,在其被宣告之前對塊中的 let 和 const 變數使用 typeof 會丟擲一個 ReferenceError。塊作用域變數在塊的頭部處於“暫存死區”,直至其被初始化,在這期間,訪問變數將會引發錯誤。
以前經常拿來判斷陣列的
instanceof
是怎麼實現的:使用a instanceof B
判斷的是a 是否為 B 的例項,即 a 的原型鏈上是否存在 B 建構函式(ES6之後可以通過Array.isArray()
來判斷是否是陣列)。// L 表示左表示式,R 表示右表示式 const customInstanceof = (L, R) => { if (typeof L !== 'object') return false while (true) { // 已經遍歷到了最頂端 if (L === null) return false // 利用原型鏈進行判斷 if (R.prototype === L.__proto__) return true L = L.__proto__ } }; customInstanceof([], Array) // true
constructor為什麼不是我們的選擇?
- constructor屬性是可以被修改的,會導致檢測出的結果不正確;
除了
undefined
和null
,其他型別的變數均能使用constructor
判斷出型別。let bool=true; bool.constructor==Boolean //true let num1=1; num1.constructor==Number //true let num2=new Number(); num2.constructor==Number //true // constructor屬性是可以被修改的 num2.constructor = Object num2.constructor==Number //false let str='hello world'; str.constructor==String //true
Object.prototype.toString竟如此萬能?
Object.prototype.toString.call(123) //"[object Number]" Object.prototype.toString.call('str') //"[object String]" Object.prototype.toString.call(true) //"[object Boolean]" Object.prototype.toString.call({}) //"[object Object]" Object.prototype.toString.call([]) //"[object Array]" // 定義getType方法,用來判斷型別 getType = (obj) => { return Object.prototype.toString.call(obj).slice(8, -1) } getType(12n) // BigInt getType(Symbol()) // Symbol getType(() => {}) // Function getType() // Undefined getType(null) // Null getType(NaN) // Number
資料參考:typeof | The history of “typeof null”
第十式:十進位制二進位制互轉,真的不用那麼麻煩
使用
NumberObject.toString(radix)
十進位制轉二進位制:// 如有補齊位數的需求,可通過判斷返回值的長度在前面加0 let num = 10; console.log(num.toString(2)); // 1010
使用
parseInt(string, radix);
二進位制轉十進位制:let num = 1010101; console.log(parseInt(num,2)); // 85
- Tips:由於以上程式碼都使用let定義了num變數,除了重新整理頁面外,該如何在控制檯分別執行呢?只需把程式碼放在一對花括號之間即可(塊級作用域)。
第十一式:沒有加減乘除,如何比較正整數字符串的大小?
在接手的部分專案中,存在需要前端拼接Elasticsearch查詢語句的情況,好不容易會了點Elasticsearch,卻發現問題並沒有那麼簡單:金額數量區間查詢你告訴我儲存的是字串?那豈不是會出現1<3000<5
的情況?天啦嚕,不要逗我好嗎?
那麼,在不改動ES的情況下,如何通過正規表示式查詢來實現正整數字符串大小的比較呢?直接說思路:數位更多或者從高位開始比,數值更大即是更大的數【一時間沒想到更好的解法,有更好的解法歡迎留言或者私信】。
// 通過正規表示式從字串陣列中篩選出大於某個數值的字串型別資料
const filterStrNumberByRegExp = (num, arr) => {
const strBaseNumber = num.toString();
const arrBaseNumber = strBaseNumber.split('');
const len = strBaseNumber.length;
// 生成正則:數位更多或者從高位開始比,數值更大
let strRegExp = `\\d{${len+1}}`;
arrBaseNumber.map((item, index) => {
// 這裡因為有位數限制,'^'和'$'不是必須的,可以去除
strRegExp += `|${strBaseNumber.substring(index,-1) || '^'}[${+item + 1}-9]\\d{${len - index - 1}}$`
});
// 丟給ES進行查詢時,貌似不可使用\d(可用[0-9]替代)、開頭、結尾匹配等字元,上面四行可用下面註釋內容替換
//let strRegExp = `[0-9]{${len+1}}`;
//arrBaseNumber.map((item, index) => {
// strRegExp += `|${strBaseNumber.substring(index,-1) || ''}[${+item + 1}-9][0-9]{${len - index - 1}}`
//});
const regExp = new RegExp(strRegExp);
// 丟給ES進行正則查詢時使用strRegExp結果
console.log(regExp, strRegExp);
return arr.filter(item => {
// 小於等於判斷的話,這裡取反或者自行修改正則
if(regExp.test(item)) return true;
});
};
filterStrNumberByRegExp(386, ['12', '334', '556', '1122', '5546','234','388','387','1234','386','385']); // ["556", "1122", "5546", "388", "387", "1234"]
詳細Elasticsearch列表頁搜尋公共方法實現可以檢視我的這篇筆記。
第十二式:前端太寂寞?如何讓頁面和你說話? —— TTS(Text To Speah)
在專案中需要對ajax請求返回的訊息進行語音播報,str 為需要播報的資訊(適應於錯誤資訊語音提示等場景):
//語音播報
function voiceAnnouncements(str){
// 百度語音合成:或者使用新版地址https://tsn.baidu.com/text2audio
var url = "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&spd=5&text=" + encodeURI(str);
var n = new Audio(url);
n.src = url;
n.play();
};
voiceAnnouncements(`
秋名山上路人稀,常有車手較高低;
如今車道依舊在,不見當年老司機。
司機車技今尚好,前端群裡不寂寥;
向天再借五百年,誓言各行領風騷。
`);
// 嘗試了一些換女聲的方式,但是,都失敗了。。。
voiceAnnouncements(`
哇,程式碼寫的真棒,你可真秀哇!
`);
引數解釋:
- lan:固定值zh。語言選擇,目前只有中英文混合模式,填寫固定值zh
- ie:編碼方式
- spd:語速,取值0-9,預設為5中語速
- text:合成的文字,使用UTF-8編碼。小於512箇中文字或者英文數字。(文字在百度伺服器內轉換為GBK後,長度必須小於1024位元組)
- React Native Text-To-Speech library for Android and iOS
- 用語音控制自己的網站 annyang:A tiny JavaScript Speech Recognition library that lets your users control your site with voice commands.annyang has no dependencies, weighs just 2 KB, and is free to use and modify under the MIT license。
第十三式:失焦事件與點選事件衝突怎麼辦?
場景:
- 下拉框中blur與click衝突;
- 輸入框blur與下方可點選浮沉click衝突:輸入值時下方出現浮層,輸入框失去焦點時,浮層隱藏;點選浮層條目觸發搜尋並隱藏浮層;
- 問題:點選浮層時,由於失焦事件先觸發,浮層隱藏邏輯執行,導致浮層的onClick事件邏輯無法執行
// 點選彈窗條目進行搜尋
handleSearch = (activeSearch) => {
console.log(activeSearch);
this.setState({ visible: false });
}
// 獲得焦點,有值時展示彈窗
onFocus = () => {
if (this.state.keyword) {
this.setState({ visible: true });
}
}
// 輸入且有值時展示彈窗
onChange = (e) => {
this.setState({
keyword: e.target.value,
visible: !!e.target.value
})
}
// 失去焦點隱藏彈窗
onBlur = () => {
if (this.state.keyword) {
this.setState({ visible: false });
}
}
render() {
const { keyword, visible } = this.state;
return (
<div>
<Input
allowClear
addonBefore={<Icon type="user" />}
placeholder="支援ID、名稱、主郵箱、客戶經理、專屬賬戶、客戶ID、GroupID搜尋"
style={ { width: 460 } }
onFocus={this.onFocus}
onChange={this.onChange}
onBlur={this.onBlur}
/>
{
// 展示彈窗(點選條目完成搜尋)
visible && keyword && <div className={styles.SearchSelect}>
{
showOptions.map(item => (
<div
onClick={() => this.handleSearch(item)}
className={styles.item}
key={item.key}
>
<div>
{item.label}:{keyword}
</div>
</div>
))
}
</div>
}
</div>
);
}
解決:
方法一:給失焦事件設定延遲觸發
onBlur = () => { if (this.state.keyword) { setTimeout(() => { this.setState({ visible: false }); }, 300); } }
方法二:使用onMouseDown替代onClick
- mousedown事件:當滑鼠指標移動到元素上方,並按下滑鼠按鍵時,會發生mousedown事件,所以它會先於失焦事件執行。
- mouseup事件:當在元素上放鬆滑鼠按鈕時,會發生mouseup事件。
第十四式:不用加減乘除如何做加法——位運算讓你的程式碼更高效
- JavaScript 位運算子
位運算是基於二進位制的,如何快速獲得二進位制可參考第十式。
不用加減乘除做加法
function add(a,b) { let sum; let add1; while(b!=0) { // 異或 sum = a^b; // 與 左移 add1 = (a&b)<<1; a = sum; b = add1; } return a }; add(1,2); // 3
JS按位運算子的妙用:
使用
&
運算子判斷一個數的奇偶(只需記住0和1與1進行&
運算的結果即可):偶數 & 1 = 0
奇數 & 1 = 1
使用
~~,>>,<<,>>>,|
來取整:~~Math.PI
:3(按位取反再取反)Math.PI>>0
,Math.PI<<0
,Math.PI>>>0
:3(按位左移或者右移0位,>>>
不可用於負數)Math.PI|0
:3,按位異或
使用
<<,>>
來計算乘除:- 整數左移n位相當於乘2的n次方;
- 右移相當於除以2的n次方,再向下取整
- 利用
^
來完成比較兩個數是否相等:!(a ^ b)
- 使用
^
來完成值交換:參考第十五式 使用
&,>>,|
來完成rgb值和16進位制顏色值之間的轉換16進位制顏色值轉RGB:
function hexToRGB(hex){ hex = hex.replace("#","0x"); let r = hex >> 16; let g = hex >> 8 & 0xff; let b = hex & 0xff; return "rgb("+r+","+g+","+b+")"; }; hexToRGB('#cccccc'); // rgb(204,204,204)
RGB轉16進位制顏色值:
function RGBToHex(rgb){ let rgbArr = rgb.split(/[^\d]+/); let color = rgbArr[1]<<16 | rgbArr[2]<<8 | rgbArr[3]; return "#"+color.toString(16); }; RGBToHex('rgb(204,204,204)'); // #cccccc
參考資料:JavaScript 位運算子
第十五式:無聊的腦筋急轉彎,不借助第三個變數交換a,b兩個變數值的N種方法
方法一:加減
a = a + b; b = a - b; a= a - b;
方法二:位運算
a ^= b; b ^= a; a ^= b;
方法三:物件或者陣列
a = {a, b}; b = a.a; a = a.b; // a = [a, b]; // b = a[0]; // a = a[1];
方法四:ES 6 解構賦值
[a, b] = [b, a]
方案五:運算子優先順序
a = [b, b=a][0];
參考資料: 不借助第三個變數交換a,b兩個變數值
第十六式:如何在瀏覽器當前頁面開啟並操作另一個tab頁
if (window.customeWindow) {
window.customeWindow.close()
}
window.customeWindow = window.open()
window.customeWindow.document.write('<p style="color:red">寫點什麼呢?<p>')
window.customeWindow.document.write('<p style="color:#cccccc">想寫什麼就寫什麼。<p>')
window.customeWindow.document.write('再追加點別的。')
window.customeWindow.document.close() // 連續追加輸入結束
window.customeWindow.document.write('哈哈,現在頁面上就只有我了!')
window.customeWindow.document.write('<p style="color:red">不,還有我!<p>')
參考資料:BRAFT EDITOR富文字編輯器預覽
第十七式:產品說要按照中文拼音順序排序?
- 使用
stringObject.localeCompare(target)
方法實現中文按照拼音順序排序
var array = ["上海", "北京", "杭州", "廣東", "深圳", "西安"];
// localeCompare() 方法返回一個數字來指示一個參考字串是否在排序順序前面或之後或與給定字串相同。
array = array.sort((item1, item2) => item1.localeCompare(item2));
// ["北京", "廣東", "杭州", "上海", "深圳", "西安"]
參考資料:String.prototype.localeCompare()
- 一個物件陣列按照另一個陣列排序
sortFunc = (propName, referArr) => (prev, next) =>
referArr.indexOf(prev[propName]) - referArr.indexOf(next[propName])
// 按照年齡age的順序給person排序
const age = [33, 11, 55, 22, 66];
const person = [
{age: 55, weight: 50},
{age: 22, weight: 42},
{age: 11, weight: 15},
{age: 66, weight: 56},
{age: 33, weight: 68}]
person.sort(sortFunc('age', age));
// 結果:
// [
// {"age": 33,"weight": 68},
// {"age": 11,"weight": 15},
// {"age": 55,"weight": 50},
// {"age": 22,"weight": 42},
// {"age": 66,"weight": 56}
// ]
第十八式:這段程式碼為什麼會報錯,說好的分號可以省略呢?
console.log(123)
[12,2].filter(item => item > 3)
// Uncaught TypeError: Cannot read property '2' of undefined
// at <anonymous>:2:1
- 分號推斷:編譯原理裡的分號推斷,作用是在程式設計的時候,讓程式設計師省略掉不必要的分號;
- JavaScript有著自動分號插入的機制(Automatic Semicolon Insertion),簡稱ASI(ASI 只是表示編譯器正確理解了程式設計師的意圖,並沒有真的插入分號);
- 瀏覽器引擎的 Parser(負責將JS 原始碼轉換為 AST)總是優先將換行符前後的符號流當作一條語句解析(帶換行的多行註釋與換行符是等效的);
所以在 Parser 眼裡,以上程式碼是這樣的:
console.log(123)[12,2].filter(item => item > 3)
,console.log(123)
沒有返回值,既undefined
;[12,2]
中的方括號被視為讀取console.log(123)
返回值中的屬性2
,類似於根據下標取陣列中的元素;- 為什麼是取屬性
2
呢,因為12,2
是個逗號表示式,表示式的值是最右邊的“2”,如此以來,上面的報錯資訊就很好理解了。
不能省略的分號:
- for 迴圈頭部的分號
- 作為空語句存在的分號
- 以
[、(、+、-、和/
五個字元開頭的語句之前的分號
資料參考:備胎的自我修養——趣談 JavaScript 中的 ASI (Automatic Semicolon Insertion)