ホームページ > 記事 > ウェブフロントエンド > 「JavaScript Breakthrough」の単一の組み込みオブジェクト
ECMA-262 では、組み込みオブジェクトを「ホスト環境に依存しない JavaScript 実装によって提供されるオブジェクト。これらのオブジェクトは、JavaScript プログラムが実行される前にすでに存在します。」と定義しています。これが意味するのは、組み込みオブジェクトはすでにインスタンス化されているため、開発者が明示的にインスタンス化する必要がないということです。 Object、Array、String などの組み込みオブジェクトのほとんどをすでに紹介しました。 ECMA-262 では、Global と Math という 2 つのシングルトン組み込みオブジェクトも定義されています。
グローバルオブジェクト
グローバルオブジェクトは、どの角度から見ても存在しないため、JavaScript で最も特殊なオブジェクトと言えます。 Global オブジェクトは、ある意味で究極の「キャッチオール オブジェクト」として定義されます。言い換えれば、他のオブジェクトに属さないプロパティとメソッドは、最終的にはそのオブジェクトのプロパティとメソッドになります。グローバル スコープで定義されたすべてのプロパティと関数は、Global オブジェクトのプロパティです。 isNaN()、isFinite()、parseInt()、parseFloat() など、本書の前半で紹介した関数は、実際にはすべて Global オブジェクトのメソッドです。これに加えて、Global オブジェクトには他のメソッドもいくつか含まれています。
URI エンコード メソッド
Global オブジェクトの encodeURI() メソッドと encodeURIComponent() メソッドは、ブラウザに送信するために URI (Uniform Resource Identifier、Universal Resource Identifier) をエンコードできます。スペースなどの特定の文字は、有効な URI に含めることはできません。これら 2 つの URI エンコード方法では、ブラウザが受け入れて理解できるように、すべての無効な文字を特別な UTF-8 エンコードに置き換えます。
このうち、encodeURI() は主に URI 全体に使用され、encodeURIComponent() は主に URI 内の特定のセグメントをエンコードするために使用されます。これらの主な違いは、encodeURI() はコロン、スラッシュ、疑問符、ハッシュ マークなど、それ自体が URI である特殊文字をエンコードしないのに対し、encodeURIComponent() は検出した非標準文字をエンコードすることです。次の例を考えてみましょう。
var uri = "http://shijiajie.com/illegal value.htm#start"; console.log(encodeURI(uri)); // "http://shijiajie.com/illegal%20value.htm#start" console.log(encodeURIComponent(uri)); // "http%3A%2F%2Fshijiajie.com%2Fillegal%20value.htm%23start"
encodeURI() を使用してエンコードした結果、スペースを除くすべての文字はそのままで、スペースのみが %20 に置き換えられます。 encodeURIComponent() メソッドは、英数字以外の文字をすべて対応するエンコーディングに置き換えます。これが、URI 全体に対して encodeURI() を使用できるのに、既存の URI に追加された文字列に対してのみ encodeURIComponent() を使用できる理由です。
一般に、encodeURI() よりも encodeURIComponent() メソッドを頻繁に使用します。これは、実際には、基になる URI よりもクエリ文字列パラメータをエンコードする方が一般的であるためです。
encodeURI() メソッドと encodeURIComponent() メソッドに対応する 2 つのメソッドは、それぞれ decodeURI() と decodeURIComponent() です。このうち、decodeURI()はencodeURI()で置き換えられた文字のみをデコードできます。たとえば、%20 はスペースに置き換えられますが、%23 は何も行われません。これは、%23 はシャープ記号 # を表し、encodeURI() を使用して置き換えられないためです。同様に、decodeURIComponent() は、encodeURIComponent() を使用してエンコードされたすべての文字をデコードできます。つまり、任意の特殊文字のエンコードをデコードできます。次の例を考えてみましょう:
var uri = "http%3A%2F%2Fshijiajie.com%2Fillegal%20value.htm%23start"; console.log(decodeURI(uri)); // http%3A%2F%2Fshijiajie.com%2Fillegal value.htm%23start console.log(decodeURIComponent(uri)); // http://shijiajie.com/illegal value.htm#start
ここで、変数 uri には、encodeURIComponent() によってエンコードされた文字列が含まれています。 decodeURI() の最初の呼び出しの出力では、%20 のみがスペースに置き換えられます。 decodeURIComponent() の 2 回目の呼び出しの出力結果では、すべての特殊文字のエンコーディングが元の文字に置き換えられ、エスケープされていない文字列が取得されます (ただし、この文字列は有効な URI ではありません)。
eval() メソッド
eval() メソッドは完全な JavaScript パーサーのようなもので、パラメータを 1 つだけ受け入れ、実行される JavaScript 文字列を受け取ります。以下の例を見てください:
eval("console.log('hi')");
このコード行は、次のコード行と同等です:
console.log("hi");
パーサーは、コード内で eval() メソッドが呼び出されていることを検出すると、渡されたパラメーターを実際の JavaScript ステートメントとして扱います。解析して、実行結果を元の位置に挿入します。 eval() 経由で実行されるコードは、呼び出しを含む実行環境の一部とみなされ、実行されるコードはその実行環境と同じスコープ チェーンを持ちます。これは、eval() 経由で実行されるコードが、それを含む環境で定義された変数を参照できることを意味します。例:
var msg = "console.log(msg)"; // "hello world"
変数 msg は eval() が呼び出される環境の外で定義されていることがわかりますが、そこで呼び出される console.log() は引き続き「hello world」を表示できます。これは、上記のコードの 2 行目が最終的に実際のコード行に置き換えられたためです。同様に、 eval() 呼び出しで関数を定義し、その関数を呼び出しの外部コードで参照することもできます。
eval("function SayHi() { console.log('hi'); }" ); SayHi(); // "こんにちは"
明らかに、関数 SayHi() は eval() 内で定義されています。ただし、 eval() の呼び出しは最終的に関数を定義する実際のコードに置き換えられるため、sayHi() は次の行で呼び出すことができます。変数についても同様です:
eval("var msg = 'hello world';"); console.log(msg); // "hello world"
在 eval() 中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中;它们只在 eval() 执行的时候创建。
严格模式下,在外部访问不到 eval() 中创建的任何变量或函数,因此前面两个例子都会导致错误。同样,在严格模式下,为 eval赋值也会导致错误:
"use strict"; eval = "hi"; // causes error
能够解释代码字符串的能力非常强大,但也非常危险。因此在使用 eval() 时必须极为谨慎,特别是在用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入)。
Global 对象的属性
Global 对象还包含一些属性,其中一部分属性已经在本书前面介绍过了。例如,特殊的值 undefined、NaN 以及 Infinity 都是Global 对象的属性。此外,所有原生引用类型的构造函数,像 Object 和 Function,也都是 Global 对象的属性。下表列出了Global 对象的所有属性。
ECMAScript 5 明确禁止给 undefined、NaN 和 Infinity 赋值,这样做即使在非严格模式下也会导致错误。
window 对象
JavaScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。来看下面的例子。
var color = "red"; function sayColor(){ console.log(window.color); } window.sayColor(); // "red"
JavaScript 中的 window 对象除了扮演规定的 Global 对象的角色外,还承担了很多别的任务。
Math 对象
JavaScript 还为保存数学公式和信息提供了一个公共位置,即 Math 对象。与我们在 JavaScript 直接编写的计算功能相比,Math对象提供的计算功能执行起来要快得多。Math 对象中还提供了辅助完成这些计算的属性和方法。
Math 对象的属性
Math 对象包含的属性大都是数学计算中可能会用到的一些特殊值。下表列出了这些属性。
属性说明
min() 和 max() 方法
Math 对象还包含许多方法,用于辅助完成简单和复杂的数学计算。其中,min() 和 max() 方法用于确定一组数值中的最小值和最大值。这两个方法都可以接收任意多个数值参数,如下面的例子所示。
var max = Math.max(3, 54, 32, 16); console.log(max); // 54 var min = Math.min(3, 54, 32, 16); console.log(min); // 3
要找到数组中的最大或最小值,可以像下面这样使用 apply() 方法。
var values = [1, 2, 3, 4, 5, 6, 7, 8]; var max = Math.max.apply(Math, values); console.log(max); // 8
这个技巧的关键是把 Math 对象作为 apply() 的第一个参数,从而正确地设置 this 值。然后,可以将任何数组作为第二个参数。
舍入方法
下面来介绍将小数值舍入为整数的几个方法:Math.ceil()、Math.floor() 和 Math.round()。这三个方法分别遵循下列舍入规则:
Math.ceil() 执行向上舍入,即它总是将数值向上舍入为最接近的整数;
Math.floor() 执行向下舍入,即它总是将数值向下舍入为最接近的整数;
Math.round() 执行标准舍入,即它总是将数值四舍五入为最接近的整数。
下面是使用这些方法的示例:
console.log(Math.ceil(25.9)); // 26 console.log(Math.ceil(25.5)); // 26 console.log(Math.ceil(25.1)); // 26 console.log(Math.round(25.9)); // 26 console.log(Math.round(25.5)); // 26 console.log(Math.round(25.1)); // 25 console.log(Math.floor(25.9)); // 25 console.log(Math.floor(25.5)); // 25 console.log(Math.floor(25.1)); // 25
random() 方法
Math.random() 方法返回介于0和1之间一个随机数,不包括0和1。对于某些站点来说,这个方法非常实用,因为可以利用它来随机显示一些名人名言和新闻事件。套用下面的公式,就可以利用 Math.random() 从某个整数范围内随机选择一个值。
值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)
公式中用到了 Math.floor() 方法,这是因为 Math.random() 总返回一个小数值。而用这个小数值乘以一个整数,然后再加上一个整数,最终结果仍然还是一个小数。举例来说,如果你想选择一个1到10之间的数值,可以像下面这样编写代码:
var num = Math.floor(Math.random() * 10 + 1);
总共有10个可能的值(1到10),而第一个可能的值是1。而如果想要选择一个介于2到10之间的值,就应该将上面的代码改成这样:
var num = Math.floor(Math.random() * 9 + 2);
从2数到10要数9个数,因此可能值的总数就是9,而第一个可能的值就是2。多数情况下,其实都可以通过一个函数来计算可能值的总数和第一个可能的值,例如:
function selectFrom(lowerValue, upperValue) { var choices = upperValue - lowerValue + 1; return Math.floor(Math.random() * choices + lowerValue); } var num = selectFrom(2, 10); console.log(num); // 介于2和10之间(包括2和10)的一个数值
函数 selectFrom() 接受两个参数:应该返回的最小值和最大值。而用最大值减最小值再加1得到了可能值的总数,然后它又把这些数值套用到了前面的公式中。这样,通过调用 selectFrom(2,10) 就可以得到一个介于2和10之间(包括2和10)的数值了。利用这个函数,可以方便地从数组中随机取出一项,例如:
var colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"]; var color = colors[selectFrom(0, colors.length-1)]; console.log(color); // 可能是数组中包含的任何一个字符串
其他方法
Math 对象中还包含其他一些与完成各种简单或复杂计算有关的方法,但详细讨论其中每一个方法的细节及适用情形超出了本书的范围。下面我们就给出一个表格,其中列出了这些没有介绍到的 Math 对象的方法。
虽然 ECMA-262 规定了这些方法,但不同实现可能会对这些方法采用不同的算法。毕竟,计算某个值的正弦、余弦和正切的方式多种多样。也正因为如此,这些方法在不同的实现中可能会有不同的精度。
关卡
// 如何高效产生m个n范围内的不重复随机数(m<=n) var getRandomNumber = function(n, m){ // 待实现方法体 } console.log(getRandomNumber(20, 3)); // 8,4,19