第23章 標準的なグローバル変数
目次
書籍を購入する
(広告、ブロックしないでください。)

第23章 標準的なグローバル変数

この章は、ECMAScript仕様で標準化されたグローバル変数のリファレンスです。ウェブブラウザにはさらに多くのグローバル変数があり、MDNにリストされています。すべてのグローバル変数は、グローバルオブジェクト(ブラウザではwindowグローバルオブジェクトを参照)の(独自のまたは継承された)プロパティです。

エラーコンストラクタ

これらのコンストラクタの詳細については、エラーコンストラクタを参照してください。

  • Error
  • EvalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

非コンストラクタ関数

いくつかのグローバル関数はコンストラクタではありません。このセクションにリストされています。

テキストのエンコードとデコード

以下の関数は、URIの様々なエンコードとデコード方法を処理します。

encodeURI(uri)

uri内の特殊文字をパーセントエンコードします。特殊文字は、以下の文字を除くすべてのUnicode文字です。

URI文字

; , / ? : @ & = + $ #

エンコードされない

a-z A-Z 0-9 - _ . ! ~ * ' ( )

例えば

> encodeURI('http://example.com/Für Elise/')
'http://example.com/F%C3%BCr%20Elise/'
encodeURIComponent(uriComponent)

uriComponent内のすべての文字をパーセントエンコードします。ただし、以下を除きます。

エンコードされない

a-z A-Z 0-9 - _ . ! ~ * ' ( )

encodeURIとは対照的に、URLやファイル名で重要な文字もエンコードされます。したがって、この関数を使用して、任意のテキストを合法的なファイル名またはURLパスセグメントに変換できます。例えば

> encodeURIComponent('http://example.com/Für Elise/')
'http%3A%2F%2Fexample.com%2FF%C3%BCr%20Elise%2F'
decodeURI(encodedURI)

encodeURIによって生成されたパーセントエンコードされたURIをデコードします。

> decodeURI('http://example.com/F%C3%BCr%20Elise/')
'http://example.com/Für Elise/'

encodeURIはURI文字をエンコードせず、decodeURIは正しくエンコードされていてもURI文字をデコードしません。

> decodeURI('%2F')
'%2F'
> decodeURIComponent('%2F')
'/'
decodeURIComponent(encodedURIComponent)

encodeURIComponentによって生成されたパーセントエンコードされたURIコンポーネントをデコードします。decodeURIとは対照的に、すべてのパーセントエンコードされた文字がデコードされます。

> decodeURIComponent('http%3A%2F%2Fexample.com%2FF%C3%BCr%20Elise%2F')
'http://example.com/Für Elise/'

以下は非推奨です。

  • escape(str)str をパーセントエンコードします。非ASCII文字を適切に処理しないため、非推奨です。encodeURIComponent() を代わりに使用してください。
  • unescape(str)str をパーセントデコードします。非ASCII文字を適切に処理しないため、非推奨です。decodeURIComponent() を代わりに使用してください。

数値の分類と解析

以下のメソッドは、数値の分類と解析に役立ちます。

eval()とnew Function()によるJavaScriptコードの動的な評価

このセクションでは、JavaScriptでコードを動的に評価する方法を調べます。

eval()を使ったコードの実行

関数呼び出し:

eval(str)

str内のJavaScriptコードを実行します。例えば

> var a = 12;
> eval('a + 5')
17

eval()はステートメントコンテキストで解析することに注意してください(式と文を参照)。

> eval('{ foo: 123 }')  // code block
123
> eval('({ foo: 123 })')  // object literal
{ foo: 123 }

厳格モードでeval()を使用する

eval()では、厳格モードを使用する必要があります(厳格モードを参照)。ゆるいモードでは、評価されたコードは周囲のスコープにローカル変数を作成できます。

function sloppyFunc() {
    eval('var foo = 123');  // added to the scope of sloppyFunc
    console.log(foo);  // 123
}

厳格モードではそれは起こりません。

function strictFunc() {
    'use strict';
    eval('var foo = 123');
    console.log(foo);  // ReferenceError: foo is not defined
}

しかし、厳格モードでも、評価されたコードは周囲のスコープの変数への読み書きアクセス権を持っています。このようなアクセスを防止するには、eval()を間接的に呼び出す必要があります。

間接的なeval()はグローバルスコープで評価されます。

eval()を呼び出すには2つの方法があります。

  • 直接。名前が「eval」である関数を直接呼び出すことによって。
  • 間接的に。他の方法(call()を介して、windowのメソッドとして、別の名前で保存してそこで呼び出すなど)。

既に見たように、直接的なeval()は現在のスコープでコードを実行します。

var x = 'global';

function directEval() {
    'use strict';
    var x = 'local';

    console.log(eval('x')); // local
}

逆に、間接的なeval()はグローバルスコープで実行します。

var x = 'global';

function indirectEval() {
    'use strict';
    var x = 'local';

    // Don’t call eval directly
    console.log(eval.call(null, 'x')); // global
    console.log(window.eval('x')); // global
    console.log((1, eval)('x')); // global (1)

    // Change the name of eval
    var xeval = eval;
    console.log(xeval('x')); // global

    // Turn eval into a method
    var obj = { eval: eval };
    console.log(obj.eval('x')); // global
}

(1)の説明:変数を参照すると、最初の結果は、いわゆる参照、2つの主要なフィールドを持つデータ構造になります。

  • base環境を指し、変数の値が格納されているデータ構造です。
  • referencedNameは変数の名前です。

eval()関数の呼び出し中、関数呼び出し演算子(括弧)はevalへの参照に遭遇し、呼び出す関数の名前を決定できます。したがって、このような関数呼び出しは直接的なeval()をトリガーします。しかし、演算子に参照を渡さないことで、間接的なeval()を強制することができます。それは、演算子を適用する前に参照の値を取得することで実現されます。(1)行のコンマ演算子がそれを行います。この演算子は最初のオペランドを評価し、2番目のオペランドの評価結果を返します。評価は常に値を生成するため、参照は解決され、関数名は失われます。

間接的に評価されたコードは常にゆるいです。これは、コードが現在の周囲とは独立して評価されるという結果です。

function strictFunc() {
    'use strict';

    var code = '(function () { return this }())';
    var result = eval.call(null, code);
    console.log(result !== undefined); // true, sloppy mode
}

new Function()を使ったコードの実行

コンストラクタFunction()は次のシグネチャを持っています。

new Function(param1, ..., paramN, funcBody)

これは、ゼロ個以上のパラメータの名前がparam1parem2などであり、本体がfuncBodyである関数を作成します。つまり、作成された関数は次のようになります。

function («param1», ..., «paramN») {
    «funcBody»
}

パラメータの合計を返す関数fを作成するためにnew Function()を使用しましょう。

> var f = new Function('x', 'y', 'return x+y');
> f(3, 4)
7

間接的なeval()と同様に、new Function()はスコープがグローバルである関数を生成します。[18]

var x = 'global';

function strictFunc() {
    'use strict';
    var x = 'local';

    var f = new Function('return x');
    console.log(f()); // global
}

このような関数は、デフォルトでゆるい関数でもあります。

function strictFunc() {
    'use strict';

    var sl = new Function('return this');
    console.log(sl() !== undefined); // true, sloppy mode

    var st = new Function('"use strict"; return this');
    console.log(st() === undefined); // true, strict mode
}

ベストプラクティス

eval()new Function()は避けるべきです。コードを動的に評価することは遅く、潜在的なセキュリティリスクです。また、静的解析を使用するほとんどのツール(IDEなど)がコードを考慮することを妨げます。

多くの場合、より良い代替案があります。例えば、Brendan Eichは最近、ツイートで、名前が変数propNameに格納されているプロパティにアクセスしたいプログラマーによって使用されるアンチパターンについて述べました。

var value = eval('obj.'+propName);

その考えは理にかなっています。ドット演算子は固定された静的に提供されるプロパティキーのみをサポートします。この場合、プロパティキーは実行時にのみ知られるため、その演算子を使用するにはeval()が必要です。幸いなことに、JavaScriptにはブラケット演算子もあり、動的なプロパティキーを受け入れます。したがって、以下は前述のコードのより良いバージョンです。

var value = obj[propName];

また、JSONデータの解析にeval()またはnew Function()を使用しないでください。それは安全ではありません。ECMAScript 5のJSONに対する組み込みサポート(第22章を参照)に依存するか、ライブラリを使用してください。

正当なユースケース

eval()new Function()には、高度ではありますが、正当なユースケースがいくつかあります。関数を含む構成データ(JSONでは許可されていません)、テンプレートライブラリ、インタープリタ、コマンドライン、モジュールシステムなどです。

結論

これは、JavaScriptでのコードの動的な評価に関する比較的ハイレベルな概要でした。さらに詳しく調べたい場合は、kangaxによる記事“Global eval. What are the options?”をご覧ください。

コンソールAPI

ほとんどのJavaScriptエンジンには、ログ出力とデバッグのためのメソッドを持つグローバルオブジェクトconsoleが存在します。このオブジェクトは言語自体の一部ではありませんが、事実上の標準となっています。consoleメソッドの主な目的はデバッグであるため、開発中は頻繁に使用されますが、デプロイされたコードではほとんど使用されません。

このセクションでは、コンソールAPIの概要を示します。Chrome 32、Firebug 1.12、Firefox 25、Internet Explorer 11、Node.js 0.10.22、Safari 7.0時点の現状を記述しています。

コンソールAPIのエンジン間での標準化レベル

コンソールAPIの実装は大きく異なり、常に変化しています。権威のあるドキュメントが必要な場合は、2つの選択肢があります。まず、APIの標準のような概要を参照できます。

  • Firebugが最初にコンソールAPIを実装し、そのwikiにあるドキュメントは、現在最も標準に近いものです。
  • さらに、Brian KardellとPaul IrishはAPIの仕様に取り組んでおり、より一貫性のある動作につながるはずです。

第二に、様々なエンジンのドキュメントを参照できます。

警告

Internet Explorer 9にはバグがあります。このブラウザでは、開発者ツールが少なくとも一度開かれていた場合のみconsoleオブジェクトが存在します。つまり、consoleを参照してツールが事前に開かれていない場合、ReferenceErrorが発生します。回避策として、consoleが存在するかどうかをチェックし、存在しない場合はダミーの実装を作成できます。

シンプルなログ出力

コンソールAPIには、次のログ出力メソッドが含まれています。

console.clear()
コンソールをクリアします。
console.debug(object1, object2?, ...)
このメソッドと同じ動作をするconsole.log()を使用することをお勧めします。
console.error(object1, object2?, ...)
パラメータをコンソールに出力します。ブラウザでは、ログ出力された内容は「エラー」アイコンでマークされている場合があり、スタックトレースやコードへのリンクが含まれる場合があります。
console.exception(errorObject, object1?, ...]) [Firebug限定]
object1などをログ出力し、インタラクティブなスタックトレースを表示します。
console.info(object1?, object2?, ...)
パラメータをコンソールに出力します。ブラウザでは、ログ出力された内容は「情報」アイコンでマークされている場合があり、スタックトレースやコードへのリンクが含まれる場合があります。
console.log(object1?, object2?, ...)

パラメータをコンソールに出力します。最初の引数がprintfスタイルのフォーマット文字列である場合、それを使用して残りのパラメータを出力します。例(Node.js REPL)

> console.log('%s', { foo: 'bar' })
[object Object]
> console.log('%j', { foo: 'bar' })
{"foo":"bar"}

唯一確実にクロスプラットフォームで動作するフォーマットディレクティブは%sです。Node.jsはJSONとしてデータをフォーマットするための%jをサポートしていますが、ブラウザはコンソールに何かインタラクティブなものをログ出力するディレクティブをサポートする傾向があります。

console.trace()
スタックトレースを出力します(多くのブラウザではインタラクティブです)。
console.warn(object1?, object2?, ...)
パラメータをコンソールに出力します。ブラウザでは、ログ出力された内容は「警告」アイコンでマークされている場合があり、スタックトレースやコードへのリンクが含まれる場合があります。

様々なプラットフォームでのサポートは次の表に示されています。

ChromeFirebugFirefoxIENode.jsSafari

clear

debug

error

exception

info

log

trace

warn

exceptionは、単一のプラットフォームでのみサポートされているため、斜体で表記されています。

確認とカウント

コンソールAPIには、次の確認とカウントのメソッドが含まれています。

console.assert(expr, obj?)
exprfalseの場合、objをコンソールに出力し、例外をスローします。trueの場合は何も行いません。
console.count(label?)
このステートメントを含む行が、このラベルで何回実行されたかをカウントします。

様々なプラットフォームでのサポートは次の表に示されています。

ChromeFirebugFirefoxIENode.jsSafari

assert

count

フォーマットされたログ出力

コンソールAPIには、フォーマットされたログ出力のための次のメソッドが含まれています。

console.dir(object)
オブジェクトの表現をコンソールに出力します。ブラウザでは、その表現をインタラクティブに調べることができます。
console.dirxml(object)
HTMLまたはXML要素のXMLソースツリーを出力します。
console.group(object1?, object2?, ...)
オブジェクトをコンソールに出力し、将来ログ出力されるすべてのコンテンツを含むネストされたブロックを開きます。console.groupEnd()を呼び出すことでブロックを閉じます。ブロックは最初は展開されていますが、折りたたむことができます。
console.groupCollapsed(object1?, object2?, ...)
console.group()と同様に動作しますが、ブロックは最初は折りたたまれています。
console.groupEnd()
console.group()またはconsole.groupCollapsed()で開かれたグループを閉じます。
console.table(data, columns?)

配列をテーブルとして出力します。要素ごとに1行です。オプションのパラメータcolumnsは、どのプロパティ/配列インデックスが列に表示されるかを指定します。このパラメータが指定されていない場合、すべてのプロパティキーがテーブル列として使用されます。欠落しているプロパティと配列要素は、列にundefinedとして表示されます。

var persons = [
    { firstName: 'Jane', lastName: 'Bond' },
    { firstName: 'Lars', lastName: 'Croft', age: 72 }
];
// Equivalent:
console.table(persons);
console.table(persons, ['firstName', 'lastName', 'age']);

結果のテーブルは以下のようになります。

(インデックス)firstNamelastNameage

0

“Jane”

“Bond”

undefined

1

“Lars”

“Croft”

72

様々なプラットフォームでのサポートは次の表に示されています。

ChromeFirebugFirefoxIENode.jsSafari

dir

dirxml

group

groupCollapsed

groupEnd

table

プロファイリングとタイミング

コンソールAPIはプロファイリングとタイミングのための次のメソッドを含みます。

console.markTimeline(label) [Safari限定]
console.timeStampと同じです。
console.profile(title?)
プロファイリングをオンにします。オプションのtitleはプロファイルレポートに使用されます。
console.profileEnd()
プロファイリングを停止し、プロファイルレポートを出力します。
console.time(label)
ラベルがlabelのタイマーを開始します。
console.timeEnd(label)
ラベルがlabelのタイマーを停止し、開始以降経過した時間を表示します。
console.timeStamp(label?)
指定されたlabelでタイムスタンプを出力します。コンソールまたはタイムラインに出力される場合があります。

様々なプラットフォームでのサポートは次の表に示されています。

ChromeFirebugFirefoxIENode.jsSafari

markTimeline

profile

(開発者ツール)

profileEnd

(開発者ツール)

time

timeEnd

timeStamp

markTimelineは、単一のプラットフォームでのみサポートされているため、斜体で表記されています。 (開発者ツール)という指定は、メソッドを機能させるために開発者ツールを開く必要があることを意味します。[19]

名前空間と特殊な値

次のグローバル変数は、関数の名前空間として機能します。詳細については(括弧内に示されている資料を参照してください:

JSON
JSON API機能(22章
Math
Math API機能(21章
Object
メタプログラミング機能(オブジェクト操作のチートシート

次のグローバル変数は、特殊な値を含んでいます。それらについては、(括弧内に示されている資料を参照してください:

undefined

存在しないことを示す値(undefinedとnull

> ({}.foo) === undefined
true
NaN

「数値ではない」ことを示す値(NaN

> 1 / 'abc'
NaN
Infinity

数値無限大∞を示す値(Infinity

> 1 / 0
Infinity



[18] Mariusz Nowak (@medikoo)は、Functionによって評価されるコードは、どこでもデフォルトでsloppyであると教えてくれました。

[19] このセクションへの貢献をしてくださったMatthias Reuter (@gweax)とPhilipp Kyeck (@pkyeck)に感謝します。

次:24. UnicodeとJavaScript