看透JavaScript:原理、方法与实践
上QQ阅读APP看书,第一时间看更新

6.3 直接量的包装对象

直接量所对应的对象叫作包装对象,string、number、boolean所对应的包装对象分别是String对象、Number对象和Boolean对象,它们都是function类型的对象。本节我们就来学习这三个对象。

一个对象最重要的就是它所包含的属性,而function对象的属性又分为两大类,一类是它自身的属性,另一类是它创建的object类型实例对象的属性,创建的实例对象的属性又分为实例自己的属性和function的prototype的属性。

学习function类型对象最重要的是学习两个方面的内容:function作为函数的功能和它对应的属性。对于包装类型的对象来说,作为函数使用时的功能都是将传入的参数转换为function所对应的直接量,例如,使用String("abc");可以新建值为abc的字符串类型的直接量等,其实和不使用函数的效果是一样的,所以学习包装类型对象主要是学习它所对应的属性。包装对象的属性和普通对象的属性没有什么区别,也是一共包括三部分:function对象自身拥有的属性、创建的实例对象所拥有的属性和function的prototype属性对象中的属性。下面我们就从这三个方面分别学习这三个包装对象。

本节的内容以ES5.1为主,ES2015中新增的内容会在后面给大家补充。

6.3.1 String对象

String对象是function类型的对象,对应的是字符串类型,可用来创建字符串类型的object对象,例如,new String("abc");就可以创建一个值为abc的字符串对象。最重要的还是它所对应的三种属性。

1. String自身的属性

String类型自身只有两个属性,一个是prototype,另一个是fromCharCode。对于prototype我们就不再解释了,fromCharCode方法的作用是创建由Unicode值所对应的字符组成的字符串,需要一个或多个参数,例如下面的例子。

    var s = String.fromCharCode(97, 98, 99);
    console.log(s);     //abc

在这个例子中,因为97、98、99所对应的Unicode值分别为a、b、c,所以创建出来的字符串s就是abc。

2. String.prototype中的属性

在ES5.1标准中,String的prototype有20个属性,这20个属性在使用String创建出来的object类型对象和字符串直接量中都可以直接使用,分别介绍如下。

▯ constructor:默认指向String对象本身。

▯ toString:因为String的prototype重写了toString方法,所以字符串的toString不会返回[object, Object]或者[object, String]而是返回字符串本身的值。

▯ valueOf:String的prototype也重写了valueOf方法,它会返回字符串本身。

▯ charAt:这个方法用来获取指定位置的字符,序号从0开始,例如下面的例子。

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

这里获取的是s字符串中序号为1的字符,也就是第二个字符,因此是e。

▯ charCodeAt:这个方法和charAt类似,但它获取的是Unicode值,例如下面的例子。

    var s = "hello";
    console.log(s.charCodeAt(1));     //101

在这个例子中,因为e的Unicode值为101,所以这里返回的值为101。

▯ concat:这个方法可以将多个字符串连接在一起组成一个新字符串,例如下面的例子。

    var s = "hello";
    var s1 = s.concat(" ECMAScript", "! ");
    console.log(s1);                  //hello ECMAScript!

在这个例子中,concat方法就将字符串s、“ECMAScript”和“! ”连接在一起。需要注意的是,连接之后s并没有发生变化,只是将连接之后的值返回给新的变量s1。

▯ indexOf:这个方法用来查找指定的字符或者字符串,它有两个参数,第一个参数是要查找的字符或字符串;第二个参数可选,代表查找的起始位置,如果省略第二个参数,那么默认会从第一个字符开始查找。如果indexOf方法查找到指定的字符(串),就会返回查找到的第一个字符在字符串中的序号(从0开始);如果找不到就会返回-1。我们来看下面的例子。

    var s="prototype";
    console.log(s.indexOf("o"));             //2
    console.log(s.indexOf("ot"));            //2
    console.log(s.indexOf("ot", 3));        //4
    console.log(s.indexOf("a"));           //-1

▯ lastIndexOf:这个方法和indexOf的用法一样,不同之处在于:indexOf是从前往后找,而lastIndexOf是从后往前找,并且lastIndexOf的第二个参数position的作用是指定要查找的字符串的结束位置(从0开始计数),例如下面的例子。

    var s="prototype";
    console.log(s.lastIndexOf("o"));        //4
    console.log(s.lastIndexOf("o", 3));     //2,在“prot”字符串中查找

▯ localeCompare:这个方法的作用是使用本地化方式比较字符串,类似于>、<的作用,但是,>、<只能依据Unicode编码来比较字符串的大小,而有些地区的字符顺序和Unicode编码并不一样,这时就需要使用localeCompare方法来比较了。如果当前对象比要比较的字符小,则返回一个小于0的数,如果当前对象比要比较的字符大,则返回一个大于0的数,如果当前对象与要比较的字符相同,则返回0,例如下面的例子。

    console.log("a".localeCompare("b"));            //-1
    console.log("b".localeCompare("a"));            //1
    console.log("a".localeCompare("a"));            //0

▯ match:这个方法用于匹配指定的内容,如果传入的参数为字符串,则会匹配字符串,如果传入的参数是正则表达式,则会返回与正则表达式相匹配的内容,例如下面的例子。

    console.log("hello JavaScript".match("Script"));          //["Script"]
    console.log("hello JavaScript".match("script"));          //null
    console.log("hello JavaScript".match(/script/i));         //["Script"]
    console.log("hello JavaScript".match(/a+/g));             //["a", "a"]
    console.log("hello ECMAScript5.1 and ECMAScript2015".match(/\d+/g));
                                                              //["5", "1", "2015"]

如果直接使用字符串匹配,那么是区分大小写的,如果使用正则表达式,则可以使用i(ignore case)标示不区分大小写。还可以使用g(global)标示查找全部符合条件的内容,例如,上述例子中的最后一行代码查找了所有数字。

▯ replace:这个方法用来将字符串中指定的内容替换为新内容,要替换的内容可以是字符串也可以是正则表达式。默认只会替换第一个符合条件的内容,使用正则表达式可以使用g来替换全部符合条件的内容。我们来看个例子。

    var s = "beneficial";
    console.log(s.replace("e", "E"));               //bEneficial
    console.log(s.replace(/e/, "E"));               //bEneficial
    console.log(s.replace(/e/g, "E"));              //bEnEficial
    console.log(s);                                 //beneficial
    console.log("pwd:12345".replace(/\d/g, "*"));  //pwd:*****

通过这个例子大家就可以明白replace的用法了。需要注意的是,replace并不会修改原来对象的值,而是返回新的对象,例如,这个例子中调用了s.replace("e", "E")之后s的值并没有发生变化。如果需要同时修改s的值,那么可以将返回值再赋值给s,即s =s.replace("e", "E");,这样就可以将替换后的值赋值给s。

▯ search:这个方法的功能类似indexOf方法,不同之处在于:search使用的是正则表达式而不是字符串进行查找,且不能指定起始位置,在正则表达式中也不能使用g标示,返回值和indexOf方法相同,例如下面的例子。

    console.log("hello ECMAScript5.1 and ECMAScript2015".search(/\d/));    //16

▯ slice:这个方法用于截取字符串的一部分,它有两个参数,分别表示要截取的字符串的起始位置和结束位置,如果大于0,则从前面计数,如果小于0,则从后面计数,如果省略第二个参数,则会截取到字符串的末尾,例如下面的例子。

    var s = "hello ECMAScript5.1 and ECMAScript2015";
    console.log(s.slice(6,19));        //ECMAScript5.1
    console.log(s.slice(6, -4));      //ECMAScript5.1 and ECMAScript
    console.log(s.slice(-14, -4));    //ECMAScript
    console.log(s.slice(0, -14));    //hello ECMAScript5.1 and
    console.log(s.slice(-14));       //ECMAScript2015

需要注意的是,slice方法也不会改变原来的字符串,同样会返回一个新的字符串。

▯ substring:这个方法和slice类似,也是截取字符串中的一部分,它的两个参数也分别表示字符串的起始位置和结束位置,所不同的是substring中如果结束位置在起始位置之前,则会自动将其调换之后再截取,当参数小于0时会按0处理,例如下面的例子。

    console.log("www.excelib.com".substring(4, 11));       //excelib
    console.log("www.excelib.com".substring(11, 4));       //excelib
    console.log("www.excelib.com".substring(3, -4));       //www
    console.log("www.excelib.com".substring(3, 0));        //www
    console.log("www.excelib.com".substring(4));           //excelib.com

从这个例子可以看出,substring(4, 11)和substring(11, 4)是一样的,substring(3, -4)和substring(3, 0)也是一样的,如果省略第二个参数,则会截取到字符串末尾。

与substring类似的还有一个substr方法,但substr并不是标准里的方法。substr方法也有两个参数,第一个参数也是起始位置,第二个参数表示要截取的长度。如果第一个参数是负数,则会从字符串的后面向前面计数(同slice方法),例如下面的例子。

    console.log("www.excelib.com".substr(4, 7));           //excelib
    console.log("www.excelib.com".substr(-11, 7));         //excelib
    console.log("www.excelib.com".substr(-11));            //excelib.com

虽然现在的主流浏览器都对substr方法提供了支持,但是,因为它不是标准里的方法,随时都有可能被舍弃,所以应该尽量少使用它。

▯ split:这个方法用于按照指定分隔符将字符串转换为字符串数组。split方法有两个参数,第一个是分隔符,如果不为空则使用它来分隔字符串,如果为空则按字符分隔字符串;第二个参数可选,表示需要返回数组中元素的个数,如果省略则将分隔后的元素全部返回,例如下面的例子。

    var s = "hello ECMAScript5.1 and ECMAScript2015";
    console.log(s.split(" "));                      //["hello", "ECMAScript5.1", "and",
      "ECMAScript2015"]


    s = "是诸法空相不生不灭不垢不净不增不减";
    console.log(s.split("不"));                    //["是诸法空相", "生", "灭",
                                                  //"垢", "净", "增", "减"]


    s = "hello张三丰hello老子hello王阳明";
    console.log(s.split("hello"));                //["", "张三丰", "老子", "王阳明"]


    console.log("www.excelib.com".split("."));   //["www", "excelib", "com"]
    console.log("excelib".split(""));           //["e", "x", "c", "e", "l", "i", "b"]
    console.log("excelib".split("", 5));       //["e", "x", "c", "e", "l"]

从这个例子可以看出,split的分隔符不仅可以是英文字符,还可以是字符串甚至汉字。另外,如果字符串起始位置就是分隔符,那么分隔后数组的第一个元素会是空字符串。

▯ toLowerCase:这个方法的作用是将字符串转换为小写形式,例如下面的例子。

    console.log("Hello Mick".toLowerCase());        //hello mick

▯ toLocaleLowerCase:这个方法的作用是使用本地语言将字符串转换为小写。有些地区有自己的大小写字符对应规则,在那种环境下就会用到此方法。一般情况下它与toLowerCase的作用一样。

▯ toUpperCase:这个方法的作用是将字符串转换为大写形式,例如下面的例子。

    console.log("www.excelib.com".toUpperCase());    //WWW.EXCELIB.COM

▯ toLocaleUpperCase:一般情况下这个方法与toUpperCase相同,只有在具有自己的大小写字符对应规则的地区才会被用到。

▯ trim:这个方法的作用是去掉字符串中头部和尾部的空格,例如下面的例子。

    console.log(" hello张三丰 ".trim());              //"hello张三丰"

注意,trim方法只能去掉头部和尾部的空格,而不能去掉字符串中间的空格,如果想去掉字符串中所有的空格,则可以使用replace和正则表达式来操作,例如下面的例子。

    console.log(" h e     l l o   ".replace(/\s+/g, ""));  //hello

3. String创建的对象实例的属性

String创建的实例对象一共有两个属性,一个是length属性,它代表字符串的长度;另外一个属性类似于数组,属性名为0到length-1,属性值为序号所对应的字符,例如下面的例子。

    var s = "www.excelib.com";
    console.log(s.length);           //15
    console.log(s[7]);               //e

使用第二个属性我们就可以把字符串当作字符数组来使用了。

6.3.2 Number对象

Number对象是function类型的对象,对应的是数字类型,可用来创建数字类型的object对象,例如,new Number (123)就可以创建一个值为123的数字实例对象。最重要的依然是它所对应的三种属性。

1. Number自身的属性

Number共有6个属性,如下所示。

▯ prototype:这个属性我们就不再解释了。

▯ MAX_VALUE:用来表示最大的数,其值约为1.7976931348623157×10308

▯ MIN_VALUE:用来表示最小的数,其值约为5×10-324

▯ NaN:Not a Number的缩写,表示不是数字。

▯ NEGATIVE_INFINITY:表示负无穷大,一般使用-Infinity来表示。

▯ POSITIVE_INFINITY:表示正无穷大,一般使用Infinity来表示。

下面来看个例子。

    console.log(Number("abc"));              //NaN
    console.log(Number.MAX_VALUE);           //1.7976931348623157e+308
    console.log(Number.MIN_VALUE);           //5e-324
    console.log(Number.MAX_VALUE*2);        //Infinity
    console.log(-Number.MAX_VALUE*2);       //-Infinity
    console.log(1/0);                       //Infinity

从这个例子可以看出,将字符串“abc”转换为数字就会产生NaN,最大数乘以2就会产生正无穷大,负最大数乘以2就会出现负无穷大,0做分母也会产生无穷大。

2. Number.prototype的属性

Number的prototype一共有7个属性:constructor、toString、toLocaleString、valueOf、toFixed、toExponential和toPrecision,分别介绍如下。

▯ constructor:这个属性默认指向Number对象本身。

▯ toString:Number的prototype重写了toString方法,重写后的toString方法会返回数字的字符串形式,还可以指定要转换为数字的基数,即指定几进制,默认为十进制。下面来看个例子。

    var n = 11;
    console.log(n.toString());               //11
    console.log(n.toString(2));              //1011
    console.log( (255).toString(16));       //ff
    console.log( (0xff).toString());        //255
    console.log( (5).toString(2));         //101

▯ toLocaleString:这个方法会按照数字的本地表示法来输出,例如下面的例子。

    var n = 2739297;
    console.log(n.toLocaleString());                          //2,739,297
    console.log(n.toLocaleString("zh-Hans-CN-u-nu-hanidec")); //二,七三九,二九七

ES5.1标准中的toLocaleString方法并没有参数,但在第6版(ES2015)中规定可以使用参数指定区域,并且现在大部分主流浏览器也都支持。

▯ valueOf:返回数字直接量。下面来看个例子。

    var n = new Number(34290);
    console.log(typeof n);                   //object
    console.log(typeof n.valueOf());        //number

因为Number创建的实例对象有自动解包的功能,所以这个方法很少使用。

▯ toFixed:这个方法用来指定数字的精度,即保留几位小数。它的参数为要保留小数的位数,如果不指定则按0处理,即没有小数,并且它会按需要自动进行四舍五入。下面来看个例子。

    console.log(837.346.toFixed(2));        //837.35
    console.log(837.346.toFixed());         //837
    console.log(837.346.toFixed(5));        //837.34600
    console.log(-837.346.toFixed(2));       //-837.35
    console.log( (3.17e7).toFixed(2));      //31700000.00

▯ toExponential:此方法的作用是将数字转换为科学计数法来表示,有一个可选参数,表示保留小数的位数,如果省略参数,则将输出尽可能多的数字,下面来看个例子。

    console.log(24803.5.toExponential());           //2.48035e+4
    console.log(24803.5.toExponential(2));          //2.48e+4
    console.log(24803.5.toExponential(1));          //2.5e+4

▯ toPrecision:这个方法用于将数字格式化为指定位数(包括整数和小数)。如果指定的位数小于数字的整数部分,那么将使用科学计数法来表示。下面来看个例子。

    console.log(49320.34702.toPrecision(7));        //49320.35
    console.log(49320.34702.toPrecision(9));        //49320.3470
    console.log(49320.34702.toPrecision(3));        //4.93e+4

3. Number创建的实例对象的属性

Number创建的实例对象没有自己的命名属性。

6.3.3 Boolean对象

Boolean对象是function类型的对象,对应的是布尔类型,可用来创建布尔类型的object实例对象。例如,new Boolean (true)就可以创建一个值为true的布尔类型实例对象。Boolean对象非常简单。

1. Boolean自身的属性

Boolean作为对象时自身只有一个prototype属性。prototype我们已经非常熟悉了,这里就不再重述了。

2. Boolean.prototype的属性

Boolean的prototype一共有三个属性:constructor、toString和valueOf。constructor指向Boolean本身,toString和valueOf都返回实例对象的值,但它们的类型不一样,toString返回string类型,而valueOf返回boolean类型。下面来看个例子。

    var b = new Boolean(true);
    console.log(b.toString());               //true
    console.log(b.valueOf());                //true
    console.log(typeof b.toString());       //string
    console.log(typeof b.valueOf());        //boolean
    console.log(typeof b);                 //object

3. Boolean创建的对象实例的属性

Boolean创建的对象实例自身不包含命名属性。

多知道点

如何在浏览器中查看对象的属性

本节介绍的包装对象中的属性主要是标准中规定的属性,但不同的浏览器除了实现标准中的属性外,还可能会添加自己特有的属性,我们可以使用Object的getOwnPropertyNames方法来获取当前浏览器中对象自身的所有属性。对于function类型的对象,我们需要获取三种类型的属性:function自身的属性、function.prototype包含的属性以及使用function创建的实例对象自身所包含的属性。这三种属性都可以通过Object的getOwnPropertyNames方法来获取。例如,可以通过下面的代码来获取String对象的三种类型的属性。

    console.log(Object.getOwnPropertyNames(String));
    //获取String对象自身的属性
    console.log(Object.getOwnPropertyNames(String.prototype));
    //获取String的prototype的属性
    console.log(Object.getOwnPropertyNames(new String()));
    //获取String创建的对象实例的属性

通过上述方法可以获取当前浏览器中某个具体function对象的相关属性。对于所有function类型的对象都可以使用这个方法,包括Object对象和浏览器中的Window对象。注意Window对象不可以用来创建新的实例对象。