ホームページ  >  記事  >  ウェブフロントエンド  >  「JavaScript Adventure」の基本パッケージタイプ

「JavaScript Adventure」の基本パッケージタイプ

高洛峰
高洛峰オリジナル
2016-10-31 13:23:411172ブラウズ

基本的な型の値の操作を容易にするために、JavaScript は 3 つの特別な参照型 (Boolean、Number、String) も提供します。実際、基本型の値が読み取られるたびに、対応する基本ラッパー型オブジェクトがバックグラウンドで作成され、このデータを操作するいくつかのメソッドを呼び出すことができるようになります。次の例を考えてみましょう。

var s1 = "some text"; 
 
var s2 = s1.substring(2);

この例の変数 s1 には文字列が含まれていますが、これはもちろんプリミティブ型の値です。次の行では、s1 の substring() メソッドを呼び出し、返された結果を s2 に保存します。プリミティブ型の値はオブジェクトではないことがわかっているため、論理的にはメソッドを持つべきではありません (ただし、私たちが望むように、プリミティブ型の値にはメソッドがあります)。実はこの直感的な操作を実現するために、一連の処理がバックグラウンドで自動的に行われています。コードの 2 行目が s1 にアクセスするとき、アクセス プロセスは読み取りモードになります。つまり、この文字列の値がメモリから読み取られます。読み取りモードで文字列にアクセスすると、以下の処理がバックグラウンドで自動的に完了します。

String 型のインスタンスを作成します。

インスタンス上で指定されたメソッドを呼び出します。

このインスタンスを破棄します。

上記の 3 つのステップは、次の JavaScript コードを実行するものと考えることができます。

var s1 = new String("some text"); 
 
var s2 = s1.substring(2); 
 
s1 = null;

この処理により、基本的な文字列値はオブジェクトと同じになります。さらに、上記の 3 つの手順は、それぞれ Boolean 型と Number 型に対応する Boolean 値と数値にも適用されます。

参照型と基本パッケージ化型の主な違いは、オブジェクトの有効期間です。 new 演算子を使用して作成された参照型のインスタンスは、実行フローが現在のスコープを離れるまでメモリ内に残ります。自動的に作成される基本的なパッケージング タイプのオブジェクトは、コード行が実行される時点でのみ存在し、その後すぐに破棄されます。これは、実行時にプリミティブ型の値にプロパティやメソッドを追加できないことを意味します。次の例を考えてみましょう:

var s1 = "some text"; 
 
s1.color = "red"; 
 
console.log(s1.color); // undefined

もちろん、Boolean、Number、String を明示的に呼び出して、基本的なラッパー型のオブジェクトを作成することもできます。ただし、「基本型」の値を扱うのか、「参照型」の値を扱うのかが混乱しやすいため、これは絶対に必要な場合にのみ実行してください。基本ラッパー型のインスタンスに対して typeof を呼び出すと、「object」が返され、基本ラッパー型のすべてのオブジェクトがブール値 true に変換されます。

Object コンストラクターも、ファクトリ メソッドと同様に、渡された値の型に基づいて、対応する基本ラッパー型のインスタンスを返します。例:

var obj = new Object("some text"); 
 
console.log(obj instanceof String); // true

文字列を Object コンストラクターに渡すと String のインスタンスが作成され、数値パラメータを渡すと Number のインスタンスが取得され、Boolean パラメータを渡すと Boolean のインスタンスが取得されます。

new を使用して基本的なパッケージ化タイプのコンストラクターを呼び出すことは、同じ名前の変換関数を直接呼び出すこととは異なることに注意してください。 例:

var value = "25"; 
 
var number = Number(value); // 转型函数 
 
console.log(typeof number); // "number" 
 
var obj = new Number(value); // 构造函数 
 
console.log(typeof obj); // "object"

基本的なラッパー型のオブジェクトを明示的に作成することはお勧めしませんが、基本的な型の値を操作する機能は依然として非常に重要です。それぞれの基本的なラッパー タイプは、対応する値を操作するための便利なメソッドを提供します。

Boolean型

Boolean型は、Boolean値に対応する参照型です。ブール型オブジェクトを作成するには、次のようにブール型コンストラクターを呼び出し、true または false の値を渡します。

var booleanObject = new Boolean(true);

ブール型のインスタンスは、valueOf() メソッドをオーバーライドして基本型の値 true または false を返し、toString() メソッドをオーバーライドして文字列 "true" と "false" を返します。ただし、ブール型オブジェクトは誤解を招くことが多いため、JavaScript ではあまり役に立ちません。最も一般的な問題の 1 つは、次のようなブール式でブール オブジェクトを使用することです。

var falseObject = new Boolean(false); 
 
var result = falseObject && true; 
 
console.log(result); // true 
 
var falseValue = false; 
 
result = falseValue && true; 
 
console.log(result); // false

この例では、false 値を持つブール オブジェクトを作成します。次に、このオブジェクトを基本型の値 true と組み合わせて、論理 AND 式を形成します。ブール演算では、false && true は false と等しくなります。ただし、例のこのコード行は、値 false ではなく falseObject を評価します。ブール式内のすべてのオブジェクトは true に変換されるため、ブール式では falseObject オブジェクトは true を表します。結果として、true && true は当然 true と等しくなります。

基本型と参照型のブール値には2つの違いがあります。まず、typeof 演算子はプリミティブ型の場合は「boolean」を返し、参照型の場合は「object」を返します。次に、ブール オブジェクトはブール型のインスタンスであるため、instanceof 演算子を使用してブール オブジェクトをテストすると true が返されますが、プリミティブ型のブール値をテストすると false が返されます。例:

console.log(typeof falseObject); // object 
 
console.log(typeof falseValue); // boolean 
 
console.log(falseObject instanceof Boolean); // true 
 
console.log(falseValue instanceof Boolean); // false

プリミティブ ブール値とブール オブジェクトの違いを理解することが重要です。私たちのアドバイスは、ブール オブジェクトは決して使用しないことです。

数値型

数値は数値に対応する参照型です。 Number オブジェクトを作成するには、呼び出し時に Number コンストラクターに適切な数値を渡します。以下に例を示します。

var numberObject = new Number(10);

Boolean 型と同様に、Number 型も valueOf()、toLocaleString()、toString() メソッドをオーバーライドします。オーバーライドされた valueOf() メソッドは、オブジェクトによって表される基本型の値を返し、他の 2 つのメソッドは文字列の形式で値を返します。次の例に示すように、基本パラメーターを toString() メソッドに渡して、10 進数値の文字列表現を返すように指示できます。

var num = 10; 
 
console.log(num.toString()); // "10" 
 
console.log(num.toString(2)); // "1010" 
 
console.log(num.toString(8)); // "12" 
 
console.log(num.toString(10)); // "10" 
 
console.log(num.toString(16)); // "a"

除了继承的方法之外,Number 类型还提供了一些用于将数值格式化为字符串的方法。其中,toFixed() 方法会按照指定的小数位返回数值的字符串表示,例如:

var num = 10; 
 
console.log(num.toFixed(2)); // "10.00"

这里给 toFixed() 方法传入了数值 2,意思是显示几位小数。于是,这个方法返回了 "10.00",即以 0 填补了必要的小数位。如果数值本身包含的小数位比指定的还多,那么接近指定的最大小数位的值就会舍入,如下面的例子所示。

var num = 10.005; 
 
console.log(num.toFixed(2)); // "10.01"

能够自动舍入的特性,使得 toFixed() 方法很适合处理货币值。

但需要注意的是,不同浏览器给这个方法设定的舍入规则可能会有所不同。

在给 toFixed() 传入0的情况下,IE8 及之前版本不能正确舍入范围在{(-0.94,-0.5],[0.5,0.94)}之间的值。对于这个范围内的值,IE8 会返回0,而不是-1或1;其他浏览器都能返回正确的值。IE9 修复了这个问题。

toFixed() 方法可以表示带有0到20个小数位的数值。但这只是标准实现的范围,有些浏览器也可能支持更多位数。

另外可用于格式化数值的方法是 toExponential(),该方法返回以指数表示法(也称 e 表示法)表示的数值的字符串形式。与toFixed() 一样,toExponential() 也接收一个参数,而且该参数同样也是指定输出结果中的小数位数。看下面的例子。

var num = 10; 
 
console.log(num.toExponential(1)); // "1.0e+1"

以上代码输出了 "1.0e+1";不过,这么小的数值一般不必使用 e 表示法。如果你想得到表示某个数值的最合适的格式,就应该使用toPrecision() 方法。

对于一个数值来说,toPrecision() 方法可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式;具体规则是看哪种格式最合适。这个方法接收一个参数,即表示数值的所有数字的位数(不包括指数部分)。请看下面的例子。

var num = 99; 
 
console.log(num.toPrecision(1)); // "1e+2" 
 
console.log(num.toPrecision(2)); // "99" 
 
console.log(num.toPrecision(3)); // "99.0"

以上代码首先完成的任务是以一位数来表示 99,结果是 "1e+2",即 100。因为一位数无法准确地表示 99,因此 toPrecision()就将它向上舍入为 100,这样就可以使用一位数来表示它了。而接下来的用两位数表示 99,当然还是 "99"。最后,在想以三位数表示 99 时,toPrecision() 方法返回了 "99.0"。实际上,toPrecision() 会根据要处理的数值决定到底是调用 toFixed() 还是调用 toExponential()。而这三个方法都可以通过向上或向下舍入,做到以最准确的形式来表示带有正确小数位的值。

toPrecision() 方法可以表现1到21位小数。但这只是标准实现的范围,有些浏览器也可能支持更多位数。

与 Boolean 对象类似,Number 对象也以后台方式为数值提供了重要的功能。但与此同时,我们仍然不建议直接实例化 Number 类型,而原因与显式创建 Boolean 对象一样。具体来讲,就是在使用 typeof 和 instanceof 操作符测试基本类型数值与引用类型数值时,得到的结果完全不同,如下面的例子所示。

var numberObject = new Number(10); 
 
var numberValue = 10; 
 
console.log(typeof numberObject); // "object" 
 
console.log(typeof numberValue); // "number" 
 
console.log(numberObject instanceof Number); // true 
 
console.log(numberValue instanceof Number); // false

String 类型

String 类型是字符串的对象包装类型,可以像下面这样使用 String 构造函数来创建。

var stringObject = new String("hello world");

String 对象的方法也可以在所有基本的字符串值中访问到。其中,继承的 valueOf()、toLocaleString() 和 toString() 方法,都返回对象所表示的基本字符串值。

String 类型的每个实例都有一个 length 属性,表示字符串中包含多个字符。来看下面的例子。

var stringValue = "hello world"; 
 
console.log(stringValue.length); // 11

应该注意的是,即使字符串中包含双字节字符(不是占一个字节的 ASCII 字符),每个字符也仍然算一个字符。例如:

var stringValue = "大家好"; 
 
console.log(stringValue.length); // 3

String 类型提供了很多方法,用于辅助完成对 JavaScript 中字符串的解析和操作。

字符方法

两个用于访问字符串中特定字符的方法是:charAt() 和 charCodeAt()。这两个方法都接收一个参数,即基于0的字符位置。其中,charAt() 方法以单字符字符串的形式返回给定位置的那个字符(JavaScript 中没有字符类型)。例如:

var stringValue = "hello world"; 
 
console.log(stringValue.charAt(1)); // "e"

如果你想得到的不是字符而是字符编码,那么就要像下面这样使用 charCodeAt() 了。例如:

var stringValue = "hello world"; 
 
console.log(stringValue.charCodeAt(1)); // 101,101是小写字母"e"的字符编码

ECMAScript 5 还定义了另一个访问个别字符的方法。在支持浏览器中,可以使用方括号加数字索引来访问字符串中的特定字符,如下面的例子所示。

var stringValue = "hello world"; 
 
console.log(stringValue[1]); // "e"

字符串操作方法

下面介绍与操作字符串有关的几个方法。第一个就是 concat(),用于将一或多个字符串拼接起来,返回拼接得到的新字符串。先来看一个例子。

var stringValue = "hello "; 
 
var result = stringValue.concat("world"); 
 
console.log(result); // "hello world" 
 
console.log(stringValue); // "hello"

实际上,concat() 方法可以接受任意多个参数,也就是说可以通过它拼接任意多个字符串。再看一个例子:

var stringValue = "hello "; 
 
var result = stringValue.concat("world", "!"); 
 
console.log(result); // "hello world!" 
 
console.log(stringValue); // "hello"

虽然 concat() 是专门用来拼接字符串的方法,但实践中使用更多的还是加号操作符 + 。而且,使用加号操作符 + 在大多数情况下都比使用 concat()方法要简便易行(特别是在拼接多个字符串的情况下)。

JavaScript 还提供了三个基于子字符串创建新字符串的方法:slice()、substr() 和 substring()。这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说,slice() 和 substring() 的第二个参数指定的是子字符串最后一个字符后面的位置。而 substr() 的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。与 concat() 方法一样,slice()、substr() 和 substring()也不会修改字符串本身的值,它们只是返回一个基本类型的字符串值,对原始字符串没有任何影响。请看下面的例子。

var stringValue = "hello world"; 
 
console.log(stringValue.slice(3)); // "lo world" 
 
console.log(stringValue.substring(3)); // "lo world" 
 
console.log(stringValue.substr(3)); // "lo world" 
 
console.log(stringValue.slice(3, 7)); // "lo w" 
 
console.log(stringValue.substring(3,7)); // "lo w" 
 
console.log(stringValue.substr(3, 7)); // "lo worl"

在传递给这些方法的参数是负值的情况下,它们的行为就不尽相同了。其中,slice() 方法会将传入的负值与字符串的长度相加,substr() 方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0。最后,substring() 方法会把所有负值参数都转换为0。下面来看例子。

var stringValue = "hello world"; 
 
console.log(stringValue.slice(-3)); // "rld" 
 
console.log(stringValue.substring(-3)); // "hello world" 
 
console.log(stringValue.substr(-3)); // "rld" 
 
console.log(stringValue.slice(3, -4)); // "lo w" 
 
console.log(stringValue.substring(3, -4)); // "hel" 
 
console.log(stringValue.substr(3, -4)); //""(空字符串)

字符串位置方法

有两个可以从字符串中查找子字符串的方法:indexOf() 和 lastIndexOf()。这两个方法都是从一个字符串中搜索给定的子字符串,然后返子字符串的位置(如果没有找到该子字符串,则返回-1)。这两个方法的区别在于:indexOf() 方法从字符串的开头向后搜索子字符串,而 lastIndexOf() 方法是从字符串的末尾向前搜索子字符串。还是来看一个例子吧。

var stringValue = "hello world"; 
 
console.log(stringValue.indexOf("o")); // 4 
 
console.log(stringValue.lastIndexOf("o")); // 7

这两个方法都可以接收可选的第二个参数,表示从字符串中的哪个位置开始搜索。换句话说,indexOf()会从该参数指定的位置向后搜索,忽略该位置之前的所有字符;而lastIndexOf()则会从指定的位置向前搜索,忽略该位置之后的所有字符。看下面的例子。

var stringValue = "hello world"; 
 
console.log(stringValue.indexOf("o", 6)); // 7 
 
console.log(stringValue.lastIndexOf("o", 6)); // 4

在使用第二个参数的情况下,可以通过循环调用 indexOf() 或 lastIndexOf() 来找到所有匹配的子字符串,如下面的例子所示:

var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; 
var positions = new Array(); 
var pos = stringValue.indexOf("e"); 
 
while(pos > -1){ 
    positions.push(pos); 
    pos = stringValue.indexOf("e", pos + 1); 
} 
console.log(positions);    // "3,24,32,35,52"

trim() 方法

ECMAScript 5 为所有字符串定义了 trim() 方法。这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。例如:

var stringValue = "   hello world   "; 
var trimmedStringValue = stringValue.trim(); 
console.log(stringValue);            // "   hello world   " 
console.log(trimmedStringValue);     // "hello world"

字符串大小写转换方法

JavaScript 中涉及字符串大小写转换的方法有4个:toLowerCase()、toLocaleLowerCase()、toUpperCase() 和toLocaleUpperCase()。其中,toLowerCase() 和 toUpperCase() 是两个经典的方法,借鉴自 java.lang.String 中的同名方法。而 toLocaleLowerCase() 和 toLocaleUpperCase() 方法则是针对特定地区的实现。对有些地区来说,针对地区的方法与其通用方法得到的结果相同,但少数语言(如土耳其语)会为 Unicode 大小写转换应用特殊的规则,这时候就必须使用针对地区的方法来保证实现正确的转换。以下是几个例子。

var stringValue = "hello world"; 
console.log(stringValue.toLocaleUpperCase());  // "HELLO WORLD" 
console.log(stringValue.toUpperCase());        // "HELLO WORLD" 
console.log(stringValue.toLocaleLowerCase());  // "hello world" 
console.log(stringValue.toLowerCase());        // "hello world"

一般来说,在不知道自己的代码将在哪种语言环境中运行的情况下,还是使用针对地区的方法更稳妥一些。

字符串的模式匹配方法

String 类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是 match(),在字符串上调用这个方法,本质上与调用RegExp 的 exec() 方法相同。match() 方法只接受一个参数,要么是一个正则表达式,要么是一个 RegExp 对象。来看下面的例子。

var text = "cat, bat, sat, fat";  
var pattern = /.at/; 
 
// 与pattern.exec(text)相同 
var matches = text.match(pattern); 
console.log(matches.index);               // 0 
console.log(matches[0]);                  // "cat" 
console.log(pattern.lastIndex);           // 0

另一个用于查找模式的方法是 search()。这个方法的唯一参数与 match() 方法的参数相同:由字符串或 RegExp 对象指定的一个正则表达式。search() 方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回-1。而且,search() 方法始终是从字符串开头向后查找模式。看下面的例子。

var text = "cat, bat, sat, fat"; 
 
var pos = text.search(/at/); 
 
console.log(pos); // 1,即"at"第一次出现的位置

为了简化替换子字符串的操作,JavaScript 提供了 replace() 方法。这个方法接受两个参数:第一个参数可以是一个 RegExp 对象或者一个字符串(这个字符串不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,唯一的办法就是提供一个正则表达式,而且要指定全局 g 标志,如下所示。

var text = "cat, bat, sat, fat"; 
 
var result = text.replace("at", "ond"); 
 
console.log(result); // "cond, bat, sat, fat" 
 
result = text.replace(/at/g, "ond"); 
 
console.log(result); // "cond, bond, sond, fond"

最后一个与模式匹配有关的方法是 split(),这个方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个 RegExp 对象(这个方法不会将字符串看成正则表达式)。split() 方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。请看下面的例子。

var colorText = "red,blue,green,yellow"; 
 
var colors1 = colorText.split(","); // ["red", "blue", "green", "yellow"] 
 
var colors2 = colorText.split(",", 2); // ["red", "blue"] 
 
localeCompare() 方法

这个方法比较两个字符串,并返回下列值中的一个:

如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1,具体的值要视实现而定);

如果字符串等于字符串参数,则返回0;

如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是1,具体的值同样要视实现而定)。

下面是几个例子。

var stringValue = "yellow"; 
 
console.log(stringValue.localeCompare("brick")); // 1 
 
console.log(stringValue.localeCompare("yellow")); // 0 
 
console.log(stringValue.localeCompare("zoo")); // -1

这个例子比较了字符串 "yellow" 和另外几个值:"brick"、"yellow" 和 "zoo"。因为 "brick" 在字母表中排在 "yellow" 之前,所以 localeCompare() 返回了1;而 "yellow" 等于 "yellow",所以 localeCompare() 返回了0;最后,"zoo" 在字母表中排在 "yellow" 后面,所以 localeCompare() 返回了-1。再强调一次,因为 localeCompare() 返回的数值取决于实现,所以最好是像下面例子所示的这样使用这个方法。

function determineOrder(value) { 
    var result = stringValue.localeCompare(value); 
    if (result < 0){ 
        console.log("The string &#39;yellow&#39; comes before the string &#39;" + value + "&#39;."); 
    } else if (result > 0) { 
        console.log("The string &#39;yellow&#39; comes after the string &#39;" + value + "&#39;."); 
    } else { 
        console.log("The string &#39;yellow&#39; is equal to the string &#39;" + value + "&#39;."); 
    } 
} 
 
determineOrder("brick"); 
determineOrder("yellow"); 
determineOrder("zoo");

使用这种结构,就可以确保自己的代码在任何实现中都可以正确地运行了。

localeCompare() 方法比较与众不同的地方,就是实现所支持的地区(国家和语言)决定了这个方法的行为。比如,美国以英语作为 JavaScript 实现的标准语言,因此 localeCompare() 就是区分大小写的,于是大写字母在字母表中排在小写字母前头就成为了一项决定性的比较规则。不过,在其他地区恐怕就不是这种情况了。

fromCharCode() 方法

另外,String 构造函数本身还有一个静态方法:fromCharCode()。这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串。从本质上来看,这个方法与实例方法 charCodeAt() 执行的是相反的操作。来看一个例子:

console.log(String.fromCharCode(104, 101, 108, 108, 111)); // "hello" 
 
var s = &#39;hello&#39;; 
for(let i=0;i<s.length;i++){ 
  console.log(`${s[i]}----${s[i].charCodeAt()}`); 
} 
/* 
"h----104" 
"e----101" 
"l----108" 
"l----108" 
"o----111" 
*/

在这里,我们给 fromCharCode() 传递的是字符串 "hello" 中每个字母的字符编码。

关卡

// 挑战一 
var falseObject = new Object(false); 
console.log(typeof falseObject);             // ??? 
console.log(falseObject instanceof Object);  // ??? 
console.log(falseObject instanceof Boolean); // ??? 
// 挑战二 
var numberObject = new Object(100); 
console.log(typeof numberObject);             // ??? 
console.log(numberObject instanceof Object);  // ??? 
console.log(numberObject instanceof Number);  // ??? 
// 挑战三 
var stringObject = new Object("abcde"); 
console.log(typeof stringObject);             // ??? 
console.log(stringObject instanceof Object);  // ??? 
console.log(stringObject instanceof String);  // ??? 
// 挑战四,翻转一个字符串 
// 提示:可以使用数组的 reverse() 方法 
var reverse = function(str) { 
    // 待实现方法体 
} 
console.log(reverse("hello"));  // "olleh"


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。