1.3.1 String类型的定义与调用

在JavaScript中,有3种定义字符串的方式,分别是字符串字面量,直接调用String()函数与new String()构造函数。

1. 字符串字面量

字符串字面量就是直接通过单引号或者双引号定义字符串的方式。

需要注意的是,在JavaScript中,单引号和双引号是等价的,都可以用来定义字符串,只不过使用单引号开头的字符串就要使用单引号结尾,使用双引号开头的字符串就要使用双引号结尾。


var str = 'hello JavaScript';  // 正确写法
var str2 = "hello html";       // 正确写法
var str = 'hello css";         // 错误写法,首尾符号不一样

2. 直接调用String()函数

直接调用String()函数,会将传入的任何类型的值转换成字符串类型,在转换时遵循的规则如下。

① 如果是Number类型的值,则直接转换成对应的字符串。


String(123);     // '123'
String(123.12);  // '123.12'

② 如果是Boolean类型的值,则直接转换成'true'或者'false'。


String(true);  // 'true'
String(false); // 'false'

③ 如果值为null,则返回字符串'null';


String(null);  // 'null'

④ 如果值为undefined,则返回字符串'undefined';


String(undefined); // 'undefined'

⑤ 如果值为字符串,则直接返回字符串本身;


String('this is a string');  // 'this is a string'

⑥ 如果值为引用类型,则会先调用toString()函数获取返回值,将返回值按照上述步骤①~⑤判断能否转换字符串类型,如果都不满足,则会调用对象的valueOf()函数获取返回值,并将返回值重新按照步骤①~⑤判断能否转换成字符串类型,如果也不满足,则会抛出类型转换的异常。

以下是通过toString()函数将对象正确转换成String类型的示例。


var obj = {
   age: 21,
   valueOf: function () {
       return this.age;
   },
   toString: function () {
       return 'good';
   }
};

String(obj);  // 'good'

以下是通过valueOf()函数将对象正确转换成String类型的示例。


var obj = {
   age: '21',
   valueOf: function () {
       return this.age;
   },
   toString: function () {
       return [];
   }
};

String(obj);  // '21'

如果toString()函数和valueOf()函数返回的都是对象类型而无法转换成原生类型时,则会抛出类型转换的异常。


var obj = {
   age: '21',
   valueOf: function () {
       return [];
   },
   toString: function () {
       return [];
   }
};
String(obj);  // 抛出异常TypeError: Cannot convert object to primitive value

3. new String()构造函数

new String()构造函数使用new运算符生成String类型的实例,对于传入的参数同样采用和上述String()函数一样的类型转换策略,最后的返回值是一个String类型对象的实例。


new String('hello JavaScript'); // String {"hello JavaScript"}

4. 三者在作比较时的区别

使用第一种字符串字面量方式和第二种直接调用String()函数的方式得到的字符串都是基本字符串,而通过第三种方式,new运算符生成的字符串是字符串对象。

基本字符串在作比较时,只需要比较字符串的值即可;而在比较字符串对象时,比较的是对象所在的地址。

我们看看以下用来测试相等的实例。


var str = 'hello';
var str2 = String(str);
var str3 = String('hello');
var str4 = new String(str);
var str5 = new String(str);
var str6 = new String('hello');

str === str2;   // true
str2 === str3;  // true
str3 === str4;  // false
str4 === str5;  // false
str5 === str6;  // false

首先对于str、str2和str3,因为都是基本字符串,只需要比较字符串的值即可,三者字符串值都为'hello',所以三者是互相严格相等的。


str === str2;   // true 
str2 === str3;  // true

其次,对于str4、str5和str6,因为是使用new运算符生成的String类型的实例,所以在比较时需要判断变量是否指向同一个对象,即内存地址是否相同,很明显str4、str5、str6都是在内存中新生成的地址,彼此各不相同。


str4 !== str5;  // true
str5 !== str6;  // true
str4 !== str6;  // true

同样,对于基本字符串和字符串对象的比较,在判断严格相等时,也会返回“false”。


str === str4;  // false
str2 === str4; // false

5. 函数的调用

在String对象的原型链上有一系列的函数,例如indexOf()函数、substring()函数、slice()函数等,通过String对象的实例可以调用这些函数做字符串的处理。

但是我们发现,采用字面量方式定义的字符串没有通过new运算符生成String对象的实例也能够直接调用原型链上的函数。


'hello'.indexOf('e');  // 1
'hello'.substring(1);  // 'ello'
'hello'.slice(1);      // 'ello'

这是为什么呢?

实际上基本字符串本身是没有字符串对象的函数,而在基本字符串调用字符串对象才有的函数时,JavaScript会自动将基本字符串转换为字符串对象,形成一种包装类型,这样基本字符串就可以正常调用字符串对象的方法了。

基本字符串和字符串对象在经过eval()函数处理时,会产生不同的结果。

eval()函数会将基本字符串作为源代码处理,如果涉及表达式会直接进行运算,返回运算后的结果;而字符串对象则会被看作对象处理,返回对象本身。


var s1 = '2 + 2';               // 创建一个字符串字面量
var s2 = new String('2 + 2');   // 创建一个对象字符串
console.log(eval(s1));          // 4
console.log(eval(s2));          // String {"2 + 2"}

通过实例可以看出,在使用eval()函数处理字符串字面量时,进行了2 + 2 = 4的算术运算,并返回“4”;而使用eval()函数处理对象字符串时,会将'2 + 2'看成是一个对象,而不会进行运算,直接输出字符串本身。