誰說 JavaScript 很簡單了?

鬍子大哈發表於2017-04-11

本文作者:Aurélien Hervé

編譯:鬍子大哈

翻譯原文:huziketang.com/blog/posts/…

英文連線:Who said javascript was easy ?

轉載請註明出處,保留原文連結以及作者資訊

本文介紹了 JavaScript 初學者應該知道的一些技巧和陷阱。如果你是老司機,就當做回顧了,哪裡有寫的不好的地方歡迎指出。

1. 你是否嘗試過對一個數字陣列進行排序呢?

JavaScript 中的 sort() 預設是按字母排序的。所以比如你這樣 [1,2,5,10].sort(),會輸出 [1,10,2,5]

誰說 JavaScript 很簡單了?

正確的排序可以使用 [1,2,5,10].sort((a, b) => a — b)

是不是很簡單,這個知識點是告訴你第一種方式排序是有問題的。

2. new Date() 很好用

誰說 JavaScript 很簡單了?

new Date() 可以接收:

  • 無引數:返回當前時間;
  • 1 個引數 x:返回 1970 年 1 月 1 日 + x 毫秒時間。Unix 的小夥伴知道為什麼是這樣;
  • new Date(1,1,1) 返回 1901 年 2 月 1 日:第一個 “1” 代表 1900 年以後的第 1 年;第二個 “1” 代表這一年的第 2 個月(並不是像你想象的那樣從 1 月開始);第三個 “1” 代表這個月的第 1 天(這個確實是如你想象的那樣從 1 開始)。
  • new Date(2017,1,1) :這並不是表示 1900 + 2017了,它就是表示 2017 年 1 月 1 日。

誰說 JavaScript 很簡單了?

3. 替換,實際上並沒有替換

對於原始串不被替換掉,我是雙手贊同的,我不喜歡一個函式的輸入總是在變化。另外你應該知道 replace 只會替換第一個匹配上的字元。

    let s = "bob"
    const replaced = s.replace('b', 'l')
    replaced === "lob" // 只替換第一個匹配上的
    s === "bob" // 原始串始終沒變複製程式碼

如果你想替換所有的,那就是用正則符 /g

    "bob".replace(/b/g, 'l') === 'lol' // 替換所有串複製程式碼

4. 小心使用比較

    // These are ok
    'abc' === 'abc' // true
    1 === 1         // true

    // These are not
    [1,2,3] === [1,2,3] // false
    {a: 1} === {a: 1}   // false
    {} === {}           // false複製程式碼

原因:[1,2,3] 和 [1,2,3] 是兩個陣列,它們只是恰巧值相等罷了,他們的引用是不同的,所以不能用簡單的 === 來比較。

5. 陣列不是原始型別

    typeof {} === 'object'  // true
    typeof 'a' === 'string' // true
    typeof 1 === number     // true
    // But....
    typeof [] === 'object'  // true複製程式碼

想知道你的變數是不是陣列,仍然可以使用 Array.isArray(myVar)

6. 閉包

這是很出名的一道 JavaScript 面試題:

    const Greeters = []
    for (var i = 0 ; i < 10 ; i++) {
      Greeters.push(function () { return console.log(i) })
    }

    Greeters[0]() // 10
    Greeters[1]() // 10
    Greeters[2]() // 10複製程式碼

你預期的是輸出:0,1,2...嗎?你知道這是為什麼嗎?你知道怎麼 fix 嗎?

我來提兩種可能的解決方案來解決這個問題:

  • 第一種:使用 let,不用 var。Duang!解決了~

letvar 的區別是作用域。var 的作用域是最近的函式塊。而 let 的作用域是最近的封閉塊。如果兩個都是在塊外的,那兩個都是全域性的。最近的封閉塊,要比最近的函式塊範圍小。這裡是原始碼

  • 第二種:使用 bind
    Greeters.push(console.log.bind(null, i))複製程式碼

還有很多方法可以解決這一問題,這裡列出了我個人的兩種最優選擇。

7. 聊一聊 bind

你覺得下面的程式碼會輸出什麼?

    class Foo {
      constructor (name) {
        this.name = name
      }

      greet () {
        console.log('hello, this is ', this.name)
      }

      someThingAsync () {
        return Promise.resolve()
      }

      asyncGreet () {
        this.someThingAsync()
        .then(this.greet)
      }
    }

    new Foo('dog').asyncGreet()複製程式碼

給你點提示,你認為是否會丟擲異常呢?Cannot read property 'name' of undefined

原因:greet 沒有在恰當的上下文中執行。依舊,有很多種方法解決這個問題。

  • 第一種:我個人比較喜歡如下解決方法。
    asyncGreet () {
      this.someThingAsync()
      .then(this.greet.bind(this))
    }複製程式碼

這種方式可以保證 greet 是在類已經例項化以後被呼叫。

  • 第二種:如果你想確保 greet 始終可以正確呼叫,可以繫結到建構函式中。
    class Foo {
      constructor (name) {
        this.name = name
        this.greet = this.greet.bind(this)
      }
    }複製程式碼
  • 第三種:你還應該知道箭頭函式(=>)可以保護上下文,也可以解決這個問題。
    asyncGreet () {
      this.someThingAsync()
      .then(() => {
        this.greet()
      })
    }複製程式碼

雖然我覺得最後一種解決方案這個例子中很不優雅……

誰說 JavaScript 很簡單了?

結束語

恭喜!到現在你知道了 JavaScript 中的一些坑,和一點技巧。JavaScript 中還有很多知識等待著你去學習,不過起碼在這幾個問題上,你不會再犯錯誤了。Cheers! \o/

如果你認為文章中還需要注意什麼,或者新增什麼,請讓我知道


我最近正在寫一本《React.js 小書》,對 React.js 感興趣的童鞋,歡迎指點

相關文章