Kanasan.JS JavaScript 第 5 版読書会 #32008年02月27日 04時47分

Kanasan.JSJavaScript 第 5 版読書会 #3 (当日のチャットログ参加者のブログ記事一覧) に行ってきました。同じ会場で先立って vim 勉強会があったのですが、私は先日 Meadow に乗り換えたばかりなのでそちらのほうはパスしました。

配列のプロパティ

JavaScript ではすべてのプロパティ名が文字列として扱われます。配列 a に対して a[-1.23] = true としても、a["-1.23"] = true と同じに扱われ、"-1.23" という名前のプロパティができるだけでエラーにはなりません。ただし、配列に関しては非負整数とみなされる名前のプロパティが特別扱い (length プロパティにも影響を及ぼしうる) されます。

Array#join と Array#toString

配列に対して、join メソッドを引数なしで呼び出すのと toString メソッドを呼び出すのとでは同じ結果が返ってきます。ただし、join メソッドは配列のようなオブジェクト (配列ではないが length プロパティを持つオブジェクト) に対しても適用できるのに対し、配列の toString メソッドは配列にしか適用できないという違いがあります。

var arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 };
Array.prototype.join.call(arrayLike);
// => a,b,c
Array.prototype.toString.call(arrayLike);
// => TypeError: Array.prototype.toString called on incompatible Object

そのため、実装によっては toString メソッドのほうが高速化できるかもしれません。ちなみに Firefox などで使われている SpiderMonkey の場合は、join メソッドも toString メソッドも内部的に同じ関数 (array_join_sub 関数) を利用しています。

Array#push、Array#unshift の返り値

push メソッド、unshift メソッドが要素追加後の配列の要素数を返すのはどうしてかという話題。これらのメソッドは Perl 由来なので返り値も Perl に従ったのでしょうが、なぜ Perl がそうなっているのは不明です。個人的には配列自身を返してくれたほうが何かと使いやすいと思うのですが。

Array#slice と Array#splice

slice メソッドと splice メソッドの字面が似ていて間違えやすいという声がありました。Perl や Ruby の場合は slice に相当する構文が別途存在する (@array[1..3]) ので splice があっても混乱しないんだとか。ECMAScript 4 でもスライス構文 (array[1:3]) が定義される予定です。

ちなみに ECMAScript では splice の第 2 引数は必須ですが、これを省略すると IE と Firefox とで動作が違ってくるそうです。

配列の各要素の比較

配列の各要素が等しいかを調べようと思って array1 == array2 と等値演算子を使ってもうまくいきません。配列はオブジェクトであり、等値演算子をオブジェクト同士の比較に用いると両者が同一のオブジェクトかどうかを返すからです。

解決策として、配列の要素がすべて文字列だとわかっていれば、join メソッドの結果を比べるといった方法があります。区切り文字を NUL 文字 ("\x00") にすれば文字がかぶることも減るでしょう。または、Prototype.js を使っていれば、 toJSON メソッドを使って JSON 形式に変換した文字列を比べるという方法もあるそうです。

関数宣言の構文

関数宣言の最後にセミコロンはいりません。これは入れ子になった関数宣言に対しても同じです。JavaScript にはセミコロンの自動補完があるので、本来セミコロンがいるのかどうか迷ってしまいますね。

function f() {
  function g() {
    ...
  }; // ← このセミコロンは不要 (この場合空文と解釈される)。
  ...
} // ← ここもセミコロンは不要。

関数式、関数リテラル、無名関数

個人的には、関数式と関数リテラルは構文の名前 (同一のものを関数式と呼ぶこともあれば関数リテラルと呼ぶこともある) で、無名関数とは識別子を指定していない関数式 (関数リテラル)、または Function コンストラクタによって作られる関数オブジェクトのことであると理解しています。(私も場合によっては意図的に混同させて使いますが。)

関数式 (関数リテラル) を使って名前つきの関数オブジェクトを作ることもできますが、JScript では関数式に指定した名前が外部に公開されたり、古い JavaScriptCore (Safari 2 以前) ではそもそも名前付き関数式に対応していなかったりするので、Web 上での使用は避けたほうがいいかもしれません。

var f = function g() { ... }; // 名前付き関数式
g(); // JScript ではエラーにならない。

負の無限大

Number.NEGATIVE_INFINITY-Infinity-1 / 0 はすべて同じ値 (負の無限大) を指します。

関数のオーバーロード

JavaScript では関数のオーバーロードはできないので、同様の機能を実現しようとしたら自分で引数をチェックとして処理を振り分ける必要があります。オーバーロードを実現するためのライブラリも多数公開されているようです (Introducing overload.jsJavaScript Method Overloading など)。

PascalCase

変数などの命名規則について。UpperCamelCase のことを PascalCase ともいうそうです。JavaScript 組み込みのメソッド名は基本的に lowerCamelCase なので、私はそれに合わせているのですが、アンダースコア区切りのほうがスコープが狭い気がするのでローカル変数はアンダースコア区切りという人もいました。

Function#length

関数オブジェクトの length プロパティ (仮引数の数を表す) は読み取り専用なので値を上書きできません。ただし、JavaScript では読み取り専用のプロパティに値を設定しようとしてもエラーは出ません。SpiderMonkey の strict モードでは警告が出ます。

function f(a, b, c) {}
f.length = 42;
// => strict warning: f.length is read-only
// => 42
// 代入式の返り値は右辺の値。
f.length;
// => 3

読み取り専用のプロパティを新しく作ることはできません。SpiderMonkey 1.7 (Firefox 2) までは Object.prototype.eval と const 文を組み合わせることにより実現可能でしたが、SpiderMonkey 1.8 (Firefox 3) で Object.prototype.eval は削除されます。

var o = {};
o.eval("const x = 42;");
o.x = 12;
o.x; // => 42 (o.x は読み取り専用になる)

カリー化と部分適用

個人的な理解では、一部の引数の値を固定するのが部分適用 (n 引数関数と m 個の値を受け取り、n - m 引数関数を返す)、ある関数を部分適用可能な関数に変換する操作がカリー化です。