首頁 > web前端 > js教程 > 《JavaScript 闖關記》之基本包裝類型

《JavaScript 闖關記》之基本包裝類型

高洛峰
發布: 2016-10-31 13:23:41
原創
1225 人瀏覽過

為了方便操作基本型別值,JavaScript 也提供了3個特殊的參考型別:Boolean、Number 和 String。實際上,每當讀取一個基本類型值的時候,後台就會建立一個對應的基本包裝類型的對象,讓我們能夠呼叫一些方法來操作這些資料。來看下面的例子。

var s1 = "some text"; 
 
var s2 = s1.substring(2);
登入後複製

這個例子中的變數 s1 包含一個字串,字串當然是基本型別值。而下一行呼叫了 s1 的 substring() 方法,並將傳回的結果保存在了 s2 中。我們知道,基本類型值不是對象,因而從邏輯上講它們不應該有方法(儘管如我們所願,它們確實有方法)。其實,為了讓我們實現這種直覺的操作,後台已經自動完成了一系列的處理。當第二行程式碼存取 s1 時,存取過程處於一種讀取模式,也就是要從記憶體中讀取這個字串的值。而在讀取模式中存取字串時,後台會自動完成下列處理。

建立 String 類型的一個實例;

在實例上呼叫指定的方法;

銷毀這個實例。

可以將上述三個步驟想像成是執行了下列 JavaScript 程式碼。

var s1 = new String("some text"); 
 
var s2 = s1.substring(2); 
 
s1 = null;
登入後複製

經過此番處理,基本的字串值就變得跟物件一樣了。而且,上面這三個步驟也分別適用於 Boolean 和 Number 類型對應的布林值和數字值。

引用類型與基本包裝類型的主要區別就是物件的生存期。使用 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 的實例。

要注意的是,使用 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 對象,可以像下面這樣呼叫 Boolean 建構子並傳入 true 或 false值。

var booleanObject = new Boolean(true);
登入後複製

Boolean 類型的實例重寫了 valueOf() 方法,傳回基本型別值 true 或 false;重寫了 toString() 方法,傳回字串 "true" 和"false"。可是,Boolean 物件在 JavaScript 中的用處不大,因為它經常會造成人們的誤解。其中最常見的問題是在布林表達式中使用 Boolean 對象,例如:

var falseObject = new Boolean(false); 
 
var result = falseObject && true; 
 
console.log(result); // true 
 
var falseValue = false; 
 
result = falseValue && true; 
 
console.log(result); // false
登入後複製

在這個例子中,我們使用 false 值建立了 Boolean 物件。然後,將這個物件與基本類型值 true 構成了邏輯與表達式。在布林運算中,false && true 等於 false。可是,範例中的這行程式碼是對 falseObject 而不是對它的值 false 進行求值。布林表達式中的所有物件都會轉換為 true,因此 falseObject 物件在布林運算式中代表的是 true。結果,true && true 當然就等於true 了。

基本型別與引用型別的布林值還有兩個差異。首先,typeof 運算子會對基本型別傳回 "boolean",而對參考型別傳回 "object"。其次,由於 Boolean 物件是 Boolean 類型的實例,所以使用 instanceof 操作符測試 Boolean 物件會傳回 true,而測試基本類型的布林值則傳回 false。例如:

console.log(typeof falseObject); // object 
 
console.log(typeof falseValue); // boolean 
 
console.log(falseObject instanceof Boolean); // true 
 
console.log(falseValue instanceof Boolean); // false
登入後複製

理解基本類型的布林值與 Boolean 物件之間的差異非常重要,我們的建議是永遠不要使用 Boolean 物件。

Number 型別

Number 是與數字值對應的參考型別。若要建立 Number 對象,可以在呼叫 Number 建構函式時向其中傳遞對應的數值。下面是一個例子。

var numberObject = new Number(10);
登入後複製

與 Boolean 類型一樣,Number 類型也重寫了 valueOf()、toLocaleString() 和 toString() 方法。重寫後的 valueOf() 方法傳回物件表示的基本類型的數值,另外兩個方法則傳回字串形式的數值。可以為 toString() 方法傳遞一個表示基數的參數,告訴它傳回幾進制數值的字串形式,如下面的範例所示。

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"
登入後複製


來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板