JavaScript 数据类型转换

      JavaScript是一门动态类型语言,变量是没有类型的,可以随时赋予任意值。但数据本身和运算是区别类型的。因此需要进行数据类型转化,有些转换是自动进行的,有些转换则需要强制转化。

1. 强制转换 

      强制转化使用三个构造函数:Number、String和Boolean,手动将各种类型的值转化为数字、字符串或者布尔值。  

1.1 Number函数:强制转化为数值 

      将任意类型的变量转化为数字 

      原始类型值的转换规则

1)数值:转换后还是原来的值

2)布尔值:true转化为1,false转化为0

3)undefined:转化为NaN

4)null:转化为0

5)字符串:转换规则较多,如下所示:

------如果字符串中只包含数字,转化为相应的十进制数值。eg:"1"转化为1,"123"转化为123,而"011"转化为11(忽略前导0);

------如果字符串中包含有效的浮点格式,转化为浮点数。eg:"12.3"转化为12.3(也会忽略前导0);

------如果字符串为空字符"",返回0;

------如果字符串中包含有效的十六进制格式,如"0xf",返回对应的十进制数值(由于忽略前导0,不能识别八进制数值);

------除上述规则外,其它情况下返回NaN。

 eg: console.log(Number("123"));  "123abc"--->NaN、""--->0、"0xf"--->15、undefined--->NaN、null--->0、false--->0;

       console.log(Number(" v12.34 ")); //12.34(Number函数会自动过滤一个字符串前导和后缀中的空格) 

       console.log(Number([])); //空数组转换为0 

      对象的转换规则

1)首先调用对象自身的valueOf方法,如果结果为原始类型的值(数值、字符串或布尔值),再对该值使用Number函数,不再进行下面的步骤;

2)如果valueOf方法返回复合类型的值,则调用对象自身的toString方法,如果结果为原始类型的值,再对该值使用Number函数,不再进行下面的步骤;

3)如果toString方法返回的是复合类型的值,则报错。

    eg:Number({a:1}); //NaN

    上面代码的执行过程如下:

if(typeof {a:1}.valueOf() === "object") {
     Number({a:1}.toString());
else {
     Number({a:1}.valueOf());
}

      上面代码分析如下:首先调用对象的valueOf方法,返回对象本身{a:1},所以toString方法的返回值"[Object Object]"使用Number函数,结果为NaN。

      如果toString方法返回的是复合类型的值,则会报错。

var obj = {
    toString: function() {
        console.log("toString");
        return {};   
    },
    valueOf: function() {
        console.log("toString");
        return {};
    }
}
Number(obj);

      上述代码中,valueOf方法和toString方法返回的都是对象,Number方法报错:"TypeError: Cannot convert object to primitive value"。

Number({valueOf: function() {return 2; }});
Number({toString: function(){ return 3; }});
Number({valueOf: function(){ return 2;}, toString: function() {return 3;}});

      上述代码的执行结果分别是:2,3,3。第一个对象返回valueOf方法的值,第二个对象返回toString方法的值,第三个对象表示valueOf方法先与toString方法执行。

1.2 String函数:强制转化为字符串

      可以将任意类型的值转化为字符串。

      原始类型值的转化规则

1)数值:转化为相应的字符串

2)字符串:转化为原来的值

3)布尔值:true转化为"true",false转化为"false"

4)null:转化为"null"

5)undefined:转化为"undefined" 

      对象的转化规则

1)首先调用对象自身的toString方法,如果返回的是原始类型的值,对该值使用String函数,不再进行下面的步骤;

2)如果toString方法返回的是复合类型的值,再调用对象自身的valueOf方法,如果返回原始类型的值,对该值使用String函数, 不再进行下面的步骤;

3)如果valueOf方法放回的是复合类型的值,报错。

      String函数的这种过程正好与Number函数正好相反。

      eg: String({a:1}); //"[Object Object]" 

      上述代码相当于String({a:1}.toString()); //"[Object Object]"

var obj = {
    toString: function() {
        console.log("toString");
        return {};   
    },
    valueOf: function() {
        console.log("toString");
        return {};
    }
}
String(obj);

      上述代码中toString方法和valueOf方法返回的都不是原始类型的值,那么String方法报错:"TypeError: Cannot convert object to primitive value"。

String({toString: function(){ return 3; }});
String({valueOf: function() {return 2; }});
String({toString: function() {return 3;}, valueOf: function(){ return 2;}});

      上述代码的执行结果分别为:3 2 3。第一个对象返回toString方法,第二对象返回valueOf方法,第三个对象说明toString方法咸鱼valueOf方法执行。

1.3 Boolean函数:强制转化成布尔值

      可以将任意类型的值转化为布尔值。 

      原始类型值得转化规则

      仅有7个值转化为false,这7个值分别是:null、undefined、+0、-0、""、NaN、false,其它值则转化为true;

      对象的转化规则

1)所有对象都转化为true,包括false对应的布尔对象;

2)空数组和空对象也转化为true。 

      eg:Boolean(false); //false      Boolean(new Boolean(false)); //true      Boolean([]); //true;      Boolean({}); //true; 

      对于数组,三种方式的强制类型转换

eg:Number([]); //0   Number([1,2,3]); //NaN   String([]); //""   String([1,2,3]); //"1,2,3"  Boolean([]); //true   Boolean([1,2,3]);// true

 

2. 自动转换

      当遇到以下3种情况,JavaScript会进行自动类型准换。

1)不同类型数据进行互相运算;

2)对非布尔值类型的数据求布尔值;

3)对非数值类型的数据进行一元运算(+,-); 

2.1 自动转化为数值

      当JavaScript遇到预期为数值的地方,会自动将非数值的参数转化为数值,转化规则与强制转化为数值规则一致,即在预期为数值的地方,自动调用Number方法。

      除了"+"运算符有可能将值转化为字符串外,其它运算符将两侧的值自动转化为数值。

eg: ('5' - '2'; //3)  ('5' * '2' //10)   (false - 1; //-1)   (true - 1; //0)   ('1' - 1; //0)  ( '5' * []; //0)   (false / '4'; //0)   ("abc" - 1; //NaN)

      上述例子是二元算术运算符的例子,JavaScript中的两个一元运算符,一元加运算'+'和一元减运算'-'也会将运算因子自动转化为数值。 

eg:(+'abc'; //NaN)  (-'abc'; //NaN)   (+true; //1)   (-false; //0)

2.2 自动转化为字符串

      当JavaScript遇到预期为字符串的地方,会自动将非字符串的数据转化为字符串,转化规则与强制转化为字符串的规则一致。

      自动转化为字符串的情况发生在,'+'运算中,当一个运算因子为字符串,而另一个运算因子为非字符串时,自动将非字符串的数据转化为字符串。

eg:('5' + 123; //"5123")('5' + true; //"5true")('5' + {}; //"5[Object Object]")('5' + []; //"5")('5' + function() {}; //"5function() {}")('5' + undefined; //"5undefined")('5' + null; //"5null")

2.3 自动转化为布尔值

      当JavaScript遇到预期为布尔值得地方,会自动将非布尔值的参数转化为布尔值,转化规则与强制转化为布尔值规则一致。 

      除了6个值,分别是undefined、null、+0、-0、""、NaN是自动转化为false外,其它值自动转化为true。

      if(!undefined && !null &&  !0 && !"" && !NaN) {console.log(true);} //true

      2.4 总结

      由于自动转换有很大的不确定性,因此在预期为数值、字符串、布尔值的地方,全部使用Number()、String()、Boolean()进行显示转换。

3. 加法运算符的类型转换 

      加法运算符(+)需要特别讨论,因为它涉及到两种类型的运算(加法和字符连接),所以不单要考虑数据类型转换,还需确定运算的类型。

3.1 三种情况 

      加法运算符的类型转化,可以分为如下三种情况讨论:

1)运算因子之中存在字符串

      只要有一个运算因子为字符串,那么执行的是字符连接操作,另一个运算因子会自动转化为字符串。

2)两个运算因子都为数值或布尔值

      这种情况下,执行加法运算,布尔值转化为数值。eg: true + 5; //6    true + true; //2

3)运算因子之中存在对象

      运算因子之中存在对象(更准确地说:存在非原始类型的值),首先调用对象的valueOf方法,如果返回的是原始类型的值,则依据上面两条规则进行运算。如果返回的是复合类型的值,则调用toString方法,对返回值运用上面两条规则。

       1 + [1,2]; //"11,2"

       首先调用[1,2].valueOf(),返回数组[1,2]本身,则继续调用[1,2].toString(),返回"1,2",所以最终结果为"11,2"。

       1 + {a:1}; //"1[Object Object]"

       首先调用对象{a:1}的valueOf方法,返回的是对象本身,接着调用对象{a:1}的toString方法,返回字符串"[Object Object]",所以最终结果是"1[Object Object]"

       有趣的是,如果更换上例中前后因子的顺序,结果不一样了。

       {a:1} + 1; //1

       原来此处,js引擎将{a:1}解析为代码块,并不解析为对象。{a:1}代码块没有返回值,因此原式相当于{a:1};+1,结果就是1。为了避免这种情况,要将{a:1}放置在括号()中,这样js引擎不会将他解析为代码块,而是解析为对象处理。({a:1}) + 1; //"[Object Object]1"

       1 + {valueOf: function() {return 2;}};//3 此代码中,valueOf方法返回数值2,因此最终结果为3。

       1 + {valueOf: function() { return {};}};//"1[Object Object]" 此代码中,valueOf方法返回一个空对象,继续调用toString方法,返回"[Object Object]",所以最终结果为"1[Object Object]"。

       1 + {valueOf: function() { return {};}, toString: function() { return 2; }};//3 此代码中,valueOf方法返回空对象,非原始类型值,继续调用toString方法,返回数值2,所以最终结果是3。

       1 + {valueOf: function() { return {};}, toString: function() { return {};}};//报错:"TypeError:can not covert object to primitive value" 此代码中,valueOf返回空对象,继续调用toString方法,返回空对象,非原始类型值,因此报错。

3.2 四个特殊表达式

1)空数组 + 空数组   eg: [] + []; //""  此代码中,首先对空数组调用valueOf方法,返回数组本身,因此再对空数组调用toString方法,返回空字符串,因此最终结果是空字符串""。

2)空数组 + 空对象   eg: [] + {}; //"[Object Object]" 此代码中,等同于空字符串""与"[Object Object]"相加,因此最终结果为"[Object Object]"

3)空对象 + 空数组   eg: {} + []; //0  此代码中,{}被解析为空代码块,因此原表达式等同于+ [],此时(+)表示一元加运算,对空数组求正值,将[]强制转化成数值,首先调用valueOf方法,返回数组本身,因此继续调用toString方法,返回"",再执行Number(""),最终结果为0。+[] 代码执行过程为:Number([]); Number([].toString()); Number(""); 如果不将{}解析为代码块,则有:({a:1}) + []; //"[Object Object]"。

4)空对象 + 空对象   eg: {} + {}; // 此代码中,第一个{}被解析为代码块,因此原表达式等同于+ {},执行一元加运算,将{}强制转化为数值,首先调用valueOf方法,返回对象本身,因此继续调用toString方法,返回"[Object Object]",再执行Number("[Object Object]"),最终结果为NaN。+ {}代码执行过程为:Number({}); Number({}.toString()); Number("[Object Object]");

      {} + {}; 此代码中,如果第一个{}不被解析为代码块,最终结果为:"[Object Object][Object Object]"。情况如有:({}) + {};       ({} + {});      console.log({} + {});      var a = {} + {}; a;

      需要指出的是对于情况3)和4),Node.js的运行结果不同于浏览器环境。

      {} + []; // "[Object Object]"

      {} + {}; //"[Object Object][Object Object]"   

      可以看到Node.js并未将第一个{}解析为代码块,原因是Node.js的命令行环境,内部执行机制大致如下所示:

      eval.call(this,"(function() { return {} + {}; } }).call(this);");

      Node.js把命令行输入都放在eval中执行,因此不会把起首的大括号理解为空代码块加以忽略。


引用:http://javascript.ruanyifeng.com/grammar/conversion.html#toc5 

时间:2014-10-15

地点:合肥科大图书馆 

原文地址:https://www.cnblogs.com/sun-mile-rain/p/4026912.html