25 道神奇的 javascript 示例,全答對算我輸!!!

「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!前端

前言

JavaScript 是一種很好的語言。它有一個簡單的語法,龐大的生態系統,以及最重要,最偉大的社區。同時,咱們都知道,JavaScript 是一個很是有趣又充滿戲法的語言。他們中的有些能夠迅速將咱們的平常工做變成地獄,有些可讓咱們大聲笑起來。web

背景

這些示例的主要目的是收集一些瘋狂的例子,並解釋它們如何工做,若是可能的話。只是由於學習之前不瞭解的東西頗有趣。若是您是初學者,您能夠閱讀此文章來深刻了解 JavaScript。我但願這個文章會激勵你花更多的時間閱讀規範。若是您是專業開發人員,您能夠將這些示例視爲您公司新手訪問問題和測驗的重要資源。同時,這些例子在準備面試時會很方便。不管如何,讀讀看。也許你會爲本身找到新的東西。面試

例子

1. true + false

Number(true); // -> 1
Number(false); // -> 0
1 + 0; // -> 1
複製代碼

💡 說明:

布爾值被轉換爲它們的數字表示後端

2. true 是 false

!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> true
複製代碼

💡 說明:

考慮一下這一步:數組

true == "true"; // -> true
false == "false"; // -> false

// 'false' 不是空字符串,因此它的值是 true
!!"false"; // -> true
!!"true"; // -> true
複製代碼

3. baNaNa

"b" + "a" + +"a" + "a"; // baNaNa
複製代碼

用 JavaScript 寫的老派笑話:瀏覽器

"foo" + +"bar"; // -> 'fooNaN'
複製代碼

💡 說明:

這個表達式能夠轉化成 'foo' + (+'bar'),但沒法將'bar'強制轉化成數值。markdown

4. NaN 不是一個 NaN

NaN === NaN; // -> false
複製代碼

💡 說明:

規範嚴格定義了這種行爲背後的邏輯:app

  1. 若是 Type(x) 不一樣於 Type(y), return false.
  2. 若是 Type(x) 數值, 而後
    1. 若是 xNaN, return false.
    2. 若是 yNaN, return false.
    3. … … …

遵循 IEEE 的「NaN」的定義:函數

有四種可能的相互排斥的關係:小於,等於,大於和無序。 當至少一個操做數是 NaN 時,即是最後一種狀況。每一個 NaN 都要比較無窮無盡的一切,包括本身。oop

5. 它是 fail

你不會相信,但...

(![] + [])[+[]] +
  (![] + [])[+!+[]] +
  ([![]] + [][[]])[+!+[] + [+[]]] +
  (![] + [])[!+[] + !+[]];
// -> 'fail'
複製代碼

💡 說明:

將大量的符號分解成片斷,咱們注意到,如下表達式常常出現:

![] + []; // -> 'false'
![]; // -> false
複製代碼

因此咱們嘗試將[]false加起來。 可是經過一些內部函數調用(binary + Operator - >ToPrimitive - >[[DefaultValue] ]),咱們最終將右邊的操做數轉換爲一個字符串:

![] + [].toString(); // 'false'
複製代碼

將字符串做爲數組,咱們能夠經過[0]來訪問它的第一個字符:

"false"[0]; // -> 'f'
複製代碼

如今,其他的是明顯的,能夠本身弄清楚!

6. []true, 但它不等於 true

!![]       // -> true
[] == true // -> false
複製代碼

💡 說明:

數組是一個true,可是它不等於true

7. null 是 false, 但又不等於 false

儘管 nullfalse,但它不等於 false

!!null; // -> false
null == false; // -> false
複製代碼

同時,其餘的一些等於 false 的值,如 0'' 等於 false

0 == false; // -> true
"" == false; // -> true
複製代碼

💡 說明:

跟前面的例子相同。這是一個相應的連接:

8. document.all 是一個 object,但又同時是 undefined

⚠️ 這是瀏覽器 API 的一部分,對於 Node.js 環境無效 ⚠️

儘管 document.all 是一個 array-like object 而且經過它能夠訪問頁面中的 DOM 節點,但在經過 typeof 的檢測結果是 undefined

document.all instanceof Object; // -> true
typeof document.all; // -> 'undefined'
複製代碼

同時,document.all 不等於 undefined

document.all === undefined; // -> false
document.all === null; // -> false
複製代碼

可是同時:

document.all == null; // -> true
複製代碼

💡 說明:

document.all 曾經是訪問頁面 DOM 節點的一種方式,特別是在早期版本的 IE 瀏覽器中。它從未成爲標準,但被普遍使用在早期的 JS 代碼中。當標準演變出新的 API 時(例如 document.getElementById)這個 API 調用就被廢棄了,標準委員會必須決定如何處理它。由於它被普遍使用嗯他們決定保留這個 API 但引入一個有意的對 JavaScript 的標準的違反。 其與 undefined 使用嚴格相等比較得出 false 而使用抽象相等比較 得出 true 是由於這個有意的對標準的違反明確地容許了這一點。

9. 最小值大於零

Number.MIN_VALUE 是最小的數字,大於零:

Number.MIN_VALUE > 0; // -> true
複製代碼

💡 說明:

Number.MIN_VALUE5e-324 ,便可以在浮點精度內表示的最小正數,便可以達到零。 它定義了浮點數的最高精度。

如今,總體最小的值是 Number.NEGATIVE_INFINITY ,儘管這在嚴格意義上並非真正的數字。

10. 函數不是函數

⚠️ V8 v5.5 或更低版本中出現的 Bug(Node.js <= 7) ⚠️

大家全部人都知道的關於討厭的 undefined 不是 function ,可是這個呢?

// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]

new Foo() instanceof null;
// > TypeError: function is not a function
// > at … … …
複製代碼

💡 說明:

這不是規範的一部分。這只是一個錯誤,如今它已被修復,因此未來不會有這個問題。

11. 數組相加

若是您嘗試兩個數組相加呢?

[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'
複製代碼

💡 說明:

會發生合併。一步一步地,它是這樣的:

[1, 2, 3] +
  [4, 5, 6][
    // joining
    (1, 2, 3)
  ].join() +
  [4, 5, 6].join();
// concatenation
"1,2,3" + "4,5,6";
// ->
("1,2,34,5,6");
複製代碼

12. 數組中的逗號

您已經建立了一個包含 4 個空元素的數組。儘管如此,你仍是會獲得一個有三個元素的,由於後面的逗號:

let a = [, , ,];
a.length; // -> 3
a.toString(); // -> ',,'
複製代碼

💡 說明:

尾逗號 (有時也稱爲「最後逗號」) 在向 JavaScript 代碼中添加新元素、參數或屬性時有用。若是您想添加一個新屬性,您能夠簡單地添加一個新行,而不用修改之前的最後一行,若是該行已經使用了後面的逗號。這使得版本控制比較清潔和編輯代碼可能不太麻煩。

13. 數組相等是一個怪物

數組進行相等比較是一個怪物,看下面的例子:

[] == ''   // -> true
[] == 0    // -> true
[''] == '' // -> true
[0] == 0   // -> true
[0] == ''  // -> false
[''] == 0  // -> true

[null] == ''      // true
[null] == 0       // true
[undefined] == '' // true
[undefined] == 0  // true

[[]] == 0  // true
[[]] == '' // true

[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0  // true

[[[[[[ null ]]]]]] == 0  // true
[[[[[[ null ]]]]]] == '' // true

[[[[[[ undefined ]]]]]] == 0  // true
[[[[[[ undefined ]]]]]] == '' // true
複製代碼

💡 說明:

你應該很是當心留意上面的例子! 7.2.13 Abstract Equality Comparison 規範描述了這些行爲。

14. undefinedNumber

若是咱們不把任何參數傳遞到 Number 構造函數中,咱們將獲得 0undefined 是一個賦值形參,沒有實際的參數,因此您可能指望 NaNundefined 做爲參數的值。然而,當咱們經過 undefined ,咱們將獲得 NaN

Number(); // -> 0
Number(undefined); // -> NaN
複製代碼

💡 說明:

根據規範:

  1. 若是沒有參數傳遞給這個函數,讓 n+0 ;
  2. 不然,讓 n 調用 ToNumber(value)
  3. 若是值爲 undefined,那麼 ToNumber(undefined) 應該返回 NaN.

15. parseInt 是一個壞蛋

parseInt 它以的怪異而出名。

parseInt("f*ck"); // -> NaN
parseInt("f*ck", 16); // -> 15
複製代碼

**💡 說明:

** 這是由於 parseInt 會持續經過解析直到它解析到一個不識別的字符,'f*ck' 中的 f 是 16 進制下的 15

解析 Infinity 到整數也頗有意思…

//
parseInt("Infinity", 10); // -> NaN
// ...
parseInt("Infinity", 18); // -> NaN...
parseInt("Infinity", 19); // -> 18
// ...
parseInt("Infinity", 23); // -> 18...
parseInt("Infinity", 24); // -> 151176378
// ...
parseInt("Infinity", 29); // -> 385849803
parseInt("Infinity", 30); // -> 13693557269
// ...
parseInt("Infinity", 34); // -> 28872273981
parseInt("Infinity", 35); // -> 1201203301724
parseInt("Infinity", 36); // -> 1461559270678...
parseInt("Infinity", 37); // -> NaN
複製代碼

也要當心解析 null

parseInt(null, 24); // -> 23
複製代碼

💡 說明:

它將 null 轉換成字符串 'null' ,並嘗試轉換它。 對於基數 0 到 23,沒有能夠轉換的數字,所以返回 NaN。 在 24,「n」 ,第 14 個字母被添加到數字系統。 在 31,「u」 ,添加第 21 個字母,能夠解碼整個字符串。 在 37 處,再也不有能夠生成的有效數字集,並返回 NaN

不要忘記八進制:

parseInt("06"); // 6
parseInt("08"); // 8 若是支持 ECMAScript 5
parseInt("08"); // 0 若是不支持 ECMAScript 5
複製代碼

💡 說明:

這是由於 parseInt 可以接受兩個參數,若是沒有提供第二個參數,而且第一個參數以 0 開始,它將把第一個參數當作八進制數解析。

parseInt 老是把輸入轉爲字符串:

parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2
Number({ toString: () => 2, valueOf: () => 1 }); // -> 1
複製代碼

解析浮點數的時候要注意

parseInt(0.000001); // -> 0
parseInt(0.0000001); // -> 1
parseInt(1 / 1999999); // -> 5
複製代碼

💡 說明: ParseInt 接受字符串參數並返回一個指定基數下的證書。ParseInt 也去除第一個字符串中非數字字符(字符集由基數決定)後的內容。0.000001 被轉換爲 "0.000001"parseInt 返回 0。當 0.0000001 被轉換爲字符串時它被處理爲 "1e-7" 所以 parseInt 返回 11/1999999 被轉換爲 5.00000250000125e-7parseInt 返回 5

16. truefalse 數學運算

咱們作一些數學計算:

true +
  true(
    // -> 2
    true + true
  ) *
    (true + true) -
  true; // -> 3
複製代碼

嗯… 🤔

💡 說明:

咱們能夠用 Number 構造函數強制轉化成數值。 很明顯,true 將被強制轉換爲 1

Number(true); // -> 1
複製代碼

一元加運算符嘗試將其值轉換成數字。 它能夠轉換整數和浮點的字符串表示,以及非字符串值 truefalsenull 。 若是它不能解析特定的值,它將轉化爲 NaN 。 這意味着咱們能夠更容易地強制將 true 換成 1

+true; // -> 1
複製代碼

當你執行加法或乘法時,ToNumber方法調用。 根據規範,該方法返回:

若是 參數 is true , 返回 1 。 若是 參數false 返回 +0

這就是爲何咱們能夠進行進行布爾值相加並獲得正確的結果

相應部分:

17. HTML 註釋在 JavaScript 中有效

你會留下深入的印象,<!-- (這是 HTML 註釋)是一個有效的 JavaScript 註釋。

// 有效註釋
<!-- 也是有效的註釋
複製代碼

💡 說明:

感動嗎? 相似 HTML 的註釋旨在容許不理解標籤的瀏覽器優雅地降級。這些瀏覽器,例如 Netscape 1.x 已經再也不流行。所以,在腳本標記中添加 HTML 註釋是沒有意義的。

因爲 Node.js 基於 V8 引擎,Node.js 運行時也支持相似 HTML 的註釋。

18. NaN 不是一個數值

儘管 NaN 類型是 'number' ,可是 NaN 不是數字的實例:

typeof NaN; // -> 'number'
NaN instanceof Number; // -> false
複製代碼

💡 說明:

typeofinstanceof 運算符的工做原理:

19. []null 是對象

typeof []; // -> 'object'
typeof null; // -> 'object'

// 然而
null instanceof Object; // false
複製代碼

💡 說明:

typeof 運算符的行爲在本節的規範中定義:

根據規範,typeof 操做符返回一個字符串 。對於沒有 [[Call]] 實現的 null、普通對象、標準特異對象和非標準特異對象,它返回字符串 "object「

可是,您可使用 toString 方法檢查對象的類型。

Object.prototype.toString.call([]);
// -> '[object Array]'

Object.prototype.toString.call(new Date());
// -> '[object Date]'

Object.prototype.toString.call(null);
// -> '[object Null]'
複製代碼

20. 神奇的數字增加

999999999999999; // -> 999999999999999
9999999999999999; // -> 10000000000000000
複製代碼

💡 說明:

這是由 IEEE 754-2008 二進制浮點運算標準引發的。

21. 0.1 + 0.2 精度計算

來自 JavaScript 的知名笑話。0.10.2 相加是存在精度錯誤的

0.1 +
  0.2(
    // -> 0.30000000000000004
    0.1 + 0.2
  ) ===
  0.3; // -> false
複製代碼

💡 說明:

浮點計算壞了:

程序中的常量 0.20.3 也將近似爲真實值。最接近 0.2double 大於有理數 0.2 ,但最接近 0.3double 小於有理數 0.30.10.2 的總和大於有理數 0.3,所以不符合您的代碼中的常數判斷。

這個問題是衆所周知的,甚至有一個網站叫 0.30000000000000004.com

22. 擴展數字的方法

您能夠添加本身的方法來包裝對象,如 NumberString

Number.prototype.isOne = function() {
  return Number(this) === 1;
};

(1.0).isOne(); // -> true
(1).isOne(); // -> true
(2.0)
  .isOne()(
    // -> false
    7
  )
  .isOne(); // -> false
複製代碼

💡 說明:

顯然,您能夠像 JavaScript 中的任何其餘對象同樣擴展 Number 對象。可是,不建議擴展不屬於規範的行爲定義。如下是 Number 屬性的列表:

23. 三個數字的比較

1 < 2 < 3; // -> true
3 > 2 > 1; // -> false
複製代碼

💡 說明:

爲何會這樣呢?其實問題在於表達式的第一部分。如下是它的工做原理:

1 < 2 < 3; // 1 < 2 -> true
true < 3; // true -> 1
1 < 3; // -> true

3 > 2 > 1; // 3 > 2 -> true
true > 1; // true -> 1
1 > 1; // -> false
複製代碼

咱們能夠用 大於或等於運算符(>=

3 > 2 >= 1; // true
複製代碼

24. 有趣的數學

一般 JavaScript 中的算術運算的結果多是很是難以預料的。 考慮這些例子:

3  - 1  // -> 2
 3  + 1  // -> 4
'3' - 1  // -> 2
'3' + 1  // -> '31'

'' + '' // -> ''
[] + [] // -> ''
{} + [] // -> 0
[] + {} // -> '[object Object]'
{} + {} // -> '[object Object][object Object]'

'222' - -'111' // -> 333

[4] * [4]       // -> 16
[] * []         // -> 0
[4, 4] * [4, 4] // NaN
複製代碼

💡 說明:

前四個例子發生了什麼?這是一個小表,以瞭解 JavaScript 中的添加:

Number  + Number  -> addition
Boolean + Number  -> addition
Boolean + Boolean -> addition
Number  + String  -> concatenation
String  + Boolean -> concatenation
String  + String  -> concatenation
複製代碼

剩下的例子呢?在相加以前,[]{} 隱式調用 ToPrimitiveToString 方法。

25. 字符串不是 String 的實例

"str"; // -> 'str'
typeof "str"; // -> 'string'
"str" instanceof String; // -> false
複製代碼

💡 說明:

String 構造函數返回一個字符串:

typeof String("str"); // -> 'string'
String("str"); // -> 'str'
String("str") == "str"; // -> true
複製代碼

咱們來試試一個 new

new String("str") == "str"; // -> true
typeof new String("str"); // -> 'object'
複製代碼

對象?那是什麼?

new String("str"); // -> [String: 'str']
複製代碼

注: 部份內容參考自 jsisweird

相關文章
相關標籤/搜索