Skip to main content

Command Palette

Search for a command to run...

JavaScript vs CoffeeScript 1: arguments

Published
2 min read

Update: 嗯… 當 2018 ES6 普及後, coffeescript 變成要花費心思去移掉的東西了…


當 JavaScript 的函式宣告時,其括號中定義的參數並不是限制,而是方便存取引數物件的變數名稱捷徑。在呼叫時傳入但沒有宣告的引數, 我們可以用 arguments[i] 來操作該參數。

無論一個 JavaScript 的函式如何宣告,呼叫該函式時,都可以傳入任意數量的引數。

如同底下的範例,我們先宣告一個有兩個參數的函式 f,接著依序用 1 到 3 個引數來呼叫這個函式。

var f = function(a, b){
    var args = Array.prototype.slice.call(arguments);
    console.log("arguments: " +  args.join(","));

    for(var i = 0; i < arguments.length; i++){
        console.log(i.toString() + ": " + arguments[i]);
    }
}

f("ruby");
//"ruby"
//"0: ruby"
f("ruby", "coffeescript");
/*
"ruby","coffeescript"
"0: ruby"
"1: coffeescript"
*/
f("ruby", "coffeescript", "lisp");
/*
"ruby","coffeescript", "lisp"
"0: ruby"
"1: coffeescript"
"2: lisp"
*/

雖然 arguments 的行為看起來很像陣列,但其實它是一個特殊的物件。缺乏了許多陣列會有的行為,物件型別也不屬於 Array。如果要像陣列一樣使用 join() 來組裝字串,必需要像上例第二行一樣,用 Array.prototype.splice.call(arguments) 先轉成一個值陣列。

在 CoffeeScript 中,則習慣用 Splats 來宣告一個可變的參數陣列。

在 CoffeeScript 裡,除了直接操作 arguments 之外,更偏好使用類似 Ruby 的 splats,宣告一個可取得任意數量引數值的變數,用陣列的方式來進行處理。

f = (a, b, c...) ->
  console.log "arguments: #{a}, #{b}, #{c}".replace(/, $/,"")
  for arg, i in arguments
    console.log "#{i}: #{arg}"

f "ruby"
#"ruby"
#"0: ruby"
f "ruby", "coffeescript"
###
"ruby","coffeescript"
"0: ruby"
"1: coffeescript"
###
f "ruby", "coffeescript", "lisp"
###
"ruby","coffeescript", "lisp"
"0: ruby"
"1: coffeescript"
"2: lisp"
###

在第一行宣告 c... 參數之後,呼叫此函式時,多餘的引數會被存成一個真正的陣列,在函式中就可以用 c 操作此陣列。由於 CoffeeScript 內建了許多方便的陣列操作方式,像是第三行中的 for...in ,同時取得陣列索引及值。若是引數少於三個,則 c 會是一個空陣列。

編譯出來的 JavaScript 長成這個樣子:

var f,
  __slice = [].slice;

f = function() {
  var a, arg, b, c, i, _i, _len, _results;
  a = arguments[0],
  b = arguments[1],
  c = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
  console.log(("arguments: " + a + ", " + b + ", " + c).replace(/, $/, ""));
  _results = [];
  for (i = _i = 0, _len = arguments.length; _i < _len; i = ++_i) {
    arg = arguments[i];
    _results.push(console.log("" + i + ": " + arg));
  }
  return _results;
};

f("ruby");
f("ruby", "coffeescript");
f("ruby", "coffeescript", "lisp");

在第 6 行中,可以看到 CoffeeScript 處理 splats 陣列的方式。第 9 行的迴圈宣告中,CoffeeScript 用 i__i 宣告了索引值及迴圈次數兩個異名同值變數。而第 13 行的部份顯示 CoffeeScript 的總是會以最後一個運算式的結果做為函式回傳值,若是迴圈運算,則會回傳一個陣列。

More from this blog

聊聊 Elixir 中的 type

最近有幾位朋友分別來問 Elixir 的 type 的問題,想說中文世界好像沒有比較完整的東西,就把知道的東西整理出來。 (目前) Elixir 的 type 能做什麼? tl;dr: 最主要是文件,然後在某種程度下防止錯誤。 我覺得這應該是在研究 Elixir 的 type 時最需要知道的事情了。不像 Haskell 及 F# 這種以型別著稱的 ML 系語言,Elixir / Erlang 本質上是個動態語言,所有與型別有關的標註都會被編譯器忽略。而 Erlang 內建的型別檢查工具 dia...

Oct 18, 20222 min read

Steam 上的程式教學類遊戲

農曆年期間比較有空,玩了一些之前買的遊戲。這次特別試了幾個標榜讓不會寫程式的人學寫程式的遊戲。分享一下試玩的心得。 1. 7 Billion Humans 考慮到劇情的話我最喜歡的是 7 Billion Humans。它用拖拉語法的方式下指令,一開始還蠻好上手的,但是因為只有 goto 那樣的結構,而操作的時候又是一次對所有的 worker 下指令,所以常常要想一下執行後每個人運作的順序。但是介面有正體中文,以「想要體驗一下寫程式大概是怎麼一回事」來說還蠻適合的。 2. while Tru...

Feb 24, 20201 min read

Let's (re)start from here.

最近的時間大半都花在這上面了。 算算應該是第五次弄部落格系統。算一下扣除上古時期用現成的之外,每個系統平均各寫六篇文章,也都撐不過兩年。前幾個分別用了 Refinery CMS -> jekyll -> middleman -> jekyll。想來架系統的總時數應該超過寫文章的時間 XD 而這次用上了 Gatsby + tailwindcss,除了恢復一下 GraphQL 的手感之外,這次還挑戰了不套別人做的版型,自己把類似上一個部落格的 style 刻出來。想說來分享一下這些技術的感想: G...

Jan 11, 20201 min read

Mostly Functional

31 posts

/.(ex|jsx?|rb|hs|rs|py)/, A father, a bookworm, a pluviophile. Co-organizer of http://Elixir.tw. Learning Satir, coaching & mediation.