Safari の JavaScript の不備2006年01月13日 02時36分

ぱっと見はいいんだけど細かいところでちょこちょこ穴があるような気がする Safari (というより Apple WebKit) ですが、どのような穴がいつふさがれてきたのかまとめてみました。最初は JavaScript 関係だけ調べるつもりだったのですが Safari 2.0.3 で setSelectionRange がサポートされたというのを聞いたのでそっちのほうも少し。ただし、手元に Mac 環境がないので実際に Safari で試したわけではありません。すべてソースと変更履歴から推測しただけなのでそのつもりで。

下の表で「WebKit」というのはその機能が実装された WebKit の (正式公開) バージョン、「Safari」というのはそのときの Safari のバージョンをあらわしています。「-」はその機能が現時点 (Safari 2.0.3) でも未実装ということです。「-」はその機能が未実装、またはその機能を実装した WebKit が正式に公開されていないことを意味します。「?」が付いているのは推測です。

機能 WebKit Safari
Object#toLocaleString - 3?
Object#hasOwnProperty 416.11 2.0.2
Object#isPrototypeOf - 3?
Object#propertyIsEnumerable - 3?
({ name: value, }) (オブジェクト初期化子での終端へのコンマの付与) - 3?
ゲッタ / セッタ - 3?
string[n] (文字列に対する添字でのアクセス) 100 1.1
for (var i in string) (for-in 文での文字列の添字 (0, 1, 2, ...) の列挙) - 3?
String#indexOf.length == 1 312.1 1.3
String#lastIndexOf.length == 1 312.1 1.3
String#localeCompare - -
String#match(noMatch) == null (match メソッドでマッチしなかったときは、空の配列ではなく null が返る) 100 1.1
String#replace(/.{n,m}/, replacement) (量指定子 {n,m} を含む正規表現の replace メソッドでの使用) 412.7 2.0.1
String#replace(regexp, func) (第 2 引数への関数の指定) 417.9 2.0.3
String#replace(regexp, "$`$&$'") (置換文字列での $` (先行部分)、$& (一致部分)、$' (後続部分) のサポート) - 3?
String#slice(negative) (第 1 引数への負数の指定) 124 1.2
String#split(/.../i) (split メソッドでの ignoreCase プロパティの反映) - 3?
String#toLocaleLowerCase 312.1 1.3
String#toLocaleUpperCase 312.1 1.3
"String \
literal"
(文字列リテラル中での改行へのエスケープシーケンス)
125.5.5 1.2.4
(-0.5).toString(2) == "-0.1" (負数または小数に対する 10 以外を基数とした toString メソッドの適用) - 3?
Number#toFixed 312.1 1.3
(0.5).toFixed(0) == "1" (Number#toFixed では大きい数に丸められる) - 3?
(0.5).toFixed(1) == "0.5" (Number#toFixed では整数部が 0 の場合文字列 "0" が使われる) - 3?
Number#toExponential 312.1 1.3
(0.5).toFixed() == "1" (Number#toFixed および Number#toExponential の引数なしでの呼び出し結果は、0 を引数とした呼び出し結果と等しい) - 3?
Number#toPrecision 312.1 1.3
var a = [42]; a["0.0"] == undefined; (配列に対する操作に関して、プロパティ名は数値ではなく文字列として扱われる) 100 1.1
var a = [42]; delete a["0.0"]; a[0] == 42; (配列に対する delete に関して、プロパティ名は数値ではなく文字列として扱われる) - 3?
try { array.length = 1.5 } catch (e) {} (Array#length への小数または負数の設定は RangeError 例外を投げる) - 3?
[1, 2, 3].toString("-") == "1,2,3" (Array#toString は引数を無視する) - 3?
var a = []; a.push(a); a.toStirng(); (循環参照を含む配列の文字列への変換でエラーにならない) - 3?
Array#toLocaleString は各要素に対して toLocaleString メソッドを呼び出す - 3?
[1, 2, 3].join(undefined) == "1,2,3" (join メソッドの引数が undefined であるときは、区切り文字として "," が使用される) 312.1 1.3
Array#sort で比較関数の返り値の絶対値が 1 未満のとき 0 に丸めない 124 1.2
Array#forEach - 3?
Array#map - 3?
Array#filter - 3?
Array#some - 3?
Array#every - 3?
Array#indexOf - 3?
Array#lastIndexOf - 3?
new Date(year, outOfRange)Date#setMonth(outOfRange) (Date オブジェクトでの月への負数、13 以上の数の指定) - 3?
new Date(year, month, outOfInt8)Date#setHours(outOfInt8, outOfInt8) (Date オブジェクトでの日、時、分への -129 以下、128 以上の数の指定) - 3?
Date#setXXX (setFullYearsetHours など) での省略可能な引数の指定 100 1.1
RegExp コンストラクタの以下のプロパティ: input$_multiline$*lastMatch$&lastParen$+leftContext$`rightContext$' - 3?
/pattern/g.toString() == "/pattern/g" (RegExp#toString でフラグを示す文字が連結される) 312.1 1.3
RegExp#sourceRegExp#globalRegExp#ignoreCaseRegExp#multiline は読み取り専用 312.1 1.3
decodeURI 100 1.1
decodeURIComponent 100 1.1
encodeURI 100 1.1
encodeURIComponent 100 1.1
(function ident() { ... }) (名前つき関数式) - 3?
func.prototype.constructor (関数宣言により作られた Function オブジェクト f に関して、f.prototype.constructor == f である) 124 1.2
(function () { ... }).prototype.constructor (関数式により作られた Function オブジェクト f に関して、f.prototype.constructor == f である) - 3?
Function#constructor = value (関数オブジェクト f に関して、f.prototype.constructor は書き込み可能) - -
Function.prototype.length == 0 (Function.prototypelength プロパティを持つ) 312.1 1.3
function f(arg) { ... } において、argarguments[0] が値を共有する - 3?
delete arguments.length (arguments オブジェクトの length プロパティは削除可能) - 3?
function f() { arguments = 42; arguments == 42; } (arguments プロパティは書き込み可能) - 3?
function f() {} delete f; typeof f == "function"; (関数宣言は DontDelete 属性を付与する) - 3?
eval("var x = 42;"); delete x; typeof x == "undefined"; (eval 内での変数宣言・関数宣言は DontDelete 属性を付与しない) - 3?
const x = 42; (定数宣言、ReadOnly 属性を付与する) - 3?
for (var i in objectWithShadowedProperty) (for-in 文ではプロトタイプチェーンにより隠されたプロパティ (同名のプロパティ) を列挙しない) - 3?
eval("throw 42;") (eval 内での throw 文の使用) 416.11 2.0.2
var \u0078 = 42; (識別子中での Unicode エスケープシーケンスの使用) - 3?
var char = 42; (classenumexportextendsimportsuper を除く ECMA-262 3rd での予約語の識別子としての使用) - 3?
ビット積、ビット排他的論理和、ビット和演算子 (&^|) の優先順位はこの順に高い 124 1.2
setTimeout(func, delay [, args...]) (第 1 引数への関数の指定) 124 1.2
setInterval(func, delay [, args...]) (第 1 引数への関数の指定) 124 1.2
Element#clientLeft - 3?
Element#clientTop - 3?
Element#scrollIntoView 416.11 2.0.2
HTMLElement#contentEditable 124 1.2
HTMLInputElement#selectionStart 417.9 2.0.3
HTMLInputElement#selectionEnd 417.9 2.0.3
HTMLInputElement#setSelectionRange 417.9 2.0.3
HTMLTextAreaElement#selectionStart 417.9 2.0.3
HTMLTextAreaElement#selectionEnd 417.9 2.0.3
HTMLTextAreaElement#setSelectionRange 417.9 2.0.3
HTMLTextAreaElement#scrollLeft の設定 - 3?
HTMLTextAreaElement#scrollTop の設定 - 3?
HTMLTextAreaElement#scrollWidth での正常な値の取得 - 3?
HTMLTextAreaElement#scrollHeight での正常な値の取得 - 3?
HTMLOptionsCollection#add - 3?
Event#target が常に要素を返す 416.11 2.0.2
CSSCharsetRule - 3?
DOM 3 XPath - 3?
XMLHttpRequest 124 1.2
XMLSerializer 124 1.2
DOMParser 412.7 2.0.1
XSLTProcessor - 3?
  • DOMParserを追加。
  • いろいろと追加・修正。
  • Date オブジェクトの月の指定について修正。
  • JavaScript 関係をいろいろと追加、一部修正。

ざっと見た感じ 1.2 になってようやく使い物になるようになってきたというところでしょうか。String#replace のようにこちら側で修正できるものもありますが。もしそのために UA 文字列からブラウザ判別を行うのなら「Safari」ではなく「AppleWebKit」から調べていったほうがいいでしょう。

それにしても Safari のビルド番号と WebCore 、JavaScriptCore 、WebKit のバージョンがそれぞれ微妙に違うのは非常にややこしくて参ります (Safari 2.0.3 の場合ビルド番号が 417.8 、WebCore のバージョンが 417.17 、JavaScriptCore のバージョンが 417.8 、WebKit のバージョンが 417.9)。

参考