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 \ (文字列リテラル中での改行へのエスケープシーケンス) |
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 (setFullYear 、setHours など) での省略可能な引数の指定 |
100 | 1.1 |
RegExp コンストラクタの以下のプロパティ: input 、 $_ 、 multiline 、 $* 、 lastMatch 、 $& 、 lastParen 、 $+ 、 leftContext 、 $` 、 rightContext 、 $' |
- | 3? |
/pattern/g.toString() == "/pattern/g" (RegExp#toString でフラグを示す文字が連結される) |
312.1 | 1.3 |
RegExp#source 、 RegExp#global 、 RegExp#ignoreCase 、 RegExp#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.prototype は length プロパティを持つ) |
312.1 | 1.3 |
function f(arg) { ... } において、arg と arguments[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; (class 、 enum 、 export 、 extends 、 import 、 super を除く 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)。
参考
コメント
_ hiroaki ― 2006年11月20日 01時04分
_ nanto_vi ― 2006年11月20日 20時12分
なお、実際にどこに問題があるのかは判断が付かないためいくつかパターンを作ってみました。よろしければどんなパターンでクラッシュするのか試していただけると幸いです。
http://nanto.asablo.jp/blog/2006/11/20/962040
_ 仕様書作成係 ― 2008年12月23日 15時11分
大変参考になりました。
これからも、良い情報の発信をしていだければと思います。
ありがとうございました。
※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。
※投稿には管理者が設定した質問に答える必要があります。
トラックバック
このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2006/01/13/209495/tb
このたびコメントをしましたのは、記事に直接は関係がなくて申し訳ないのですが、 Safari でこのページを開くと、 Safari がクラッシュしてしまい、困る事がありましたので、それをお知らせしたいと思ったがためです。
すべてのページは試していませんが、じつはこのページに限らずクラッシュしてしまいます。
OS は Mac OS X v10.3.9 で、クラッシュしてしまう Safari のバージョンは 1.3.2 (v312.6) です。これは Mac OS X v10.3.9 の現時点での最新のものだと思います。
こちらのサイトは Google の検索でよくヒットするのですが、その検索結果から辿るときは、このサイトであることを認知しないまま開いているため、思いがけずクラッシュしてしまうことになってしまいます。これまでも何度か経験してしまっています。
そこで、もし問題がどこかに認められるのであれば、対処していただけないでしょうか。
不躾なお願いでお気を悪くされましたら申し訳ありませんが、こちらのサイトには有益な情報が多く、今後も参照させていただきたいため、どうかよろしくお願いします。