首页 > 代码库 > 原生函数和强制类型装换

原生函数和强制类型装换

  var a = new String("abc");
  console.log(typeof a); //是Object,而不是string

使用new String("abc"); 创建的是字符串 abc 的封装对象,而不是基本类型值"abc"。

封装对象

由于基本类型值没有.length和.toString()这样的属性和方法,需要通过封装对象才能访问,此时JavaScript会自动为基本类型值包装一个封装对象,如:

 var a = "abc";
 console.log(a.length);

注意:如果需要频繁的用到.length这样的属性和方法,那么从一开始就创建一个封装对象也许更方便,但需要注意的是,事实并非如此,因为浏览器已经为这些常用的方法做了性能优化。直接使用封装对象来提前优化,反而会降低执行效率。

 var a = new Boolean(false);
        if (!a) {
            console.log("aaaa"); //永远不会执行到这里
        }

为false创建一个封装对象,然而该对象的真是是truthy,所有总是返回返回true,如果想要自行封装基本类型值,可以使用object函数。

拆封

如果想要得到封装对象中的基本类型值,可以使用valueOf函数:

 var a = new String("abc");
 console.log(a.valueOf());//abc

日期Date

创建日期对象必须使用new Date() Date可以带参数用来指定日期和时间,而不带参数的话,则使用当前的日期和时间。

强制类型装换

强制类型转换分为隐式转换和显示转换,这两种转换的区别是根据你对程序的理解来定义的。例如:

 var a = 42;
 var b = a + "";//隐式转换
 var c = String(a);//显示转换

如果你对a+""理解的话,那么这行代码对你来说也是显式转换。

ToString方法

基本类型字符串化规则为:null转换为"null",undefined转换为"undefined" true转换为"true",数字的字符串化则最需通用规则。

对普通对象来说,除非自行定义,否则tostring返回内部属性Class的值,如:[object Object]

 

JSON字符串化

JSON.stringify在对象中遇到undefined、function、和symbol时会自动将其忽略。

replacer

我们可以向JSON.stringify方法传递一个可选参数replacer,可以是数组或者函数,用来指定对象序列号过程中那些属性应该被处理或排除,如果replacer是一个数组,那么它必须是一个字符串数组

其中包含序列化要处理的对象的属性名称,除此之外的属性将被忽略。

如果replacer是一个函数,它会对对象本身调用一次,然后对对象的每个属性各调用一次,每次传递两个参数,建和值,如果要忽略某个键就返回undefined,否则返回指定的值。

例如:

  console.log(JSON.stringify(a, ["b", "c"])); //{"b":42,"c":"42"}
        console.log(JSON.stringify(a, function (k, v) { //{"b":42,"d":[1,2,3]}
            if (k != "c") {
                return v;
            }
        }));

tonumber

tonumber方法,其中true转换为1,false转为0,undefined转换为NaN,null转换为0

在转换时,先检查是否有valueOf方法,如果有并且返回基本类型值,就使用该值进行强制类型转换,如果没有就使用tostring的返回值来进行强制类型转换。如果valueOf和tostring均不返回基本类型值,会产生typeerror错误。(使用object.crete(null)创建的对象属性为null,并且没有这两个方法)

  var a = {
            valueOf: function () {
                return "43";
            }
        }
        var b = {
            toString: function () {
                return "43";
            }
        }
        var c = [4, 3];
        c.toString = function () {
            return this.join("");
        }

        console.log(Number(a)); //43
        console.log(Number(b)); //43
        console.log(Number(c)); //43
        console.log(Number("")); //0
        console.log(Number([])); //0
        console.log(Number(["abc"])); //NaN

ToBoolean

我们常误以为数值1和0分别等同于true和false,在有些语言中可能是这样,但在JavaScript中布尔值和数字是不一样的,虽然我们可以将1强制转换为true,0转换为false,反之亦然,但它们并不是一回事。

下面这些是可以转换为false的值:

undefined

null

false

+0、-0、NaN

""

我们可以理解为除了以上转换为假值外,其他的都是真值

例如:

        var a = "false";
        var b = "0";
        var c = "‘‘"
        var d = [];
        var e = {};
        var f = function () { };
        console.log(Boolean(a)); //true
        console.log(Boolean(b));//true
        console.log(Boolean(c));//true
        console.log(Boolean(d));//true
        console.log(Boolean(e));//true
        console.log(Boolean(f));//true

真值可以无限长,所以无法一一列举,这里只能以假值列表作为参考。

显式强制转换

+运算符

 var a = "3.14";
 console.log(+a);//3.14

直接转换为数值类型而非数字假发运算,也不是字符串拼接。

var a = "3.14";
var d = 5 + +a;
console.log(+d);//8.14

这里需要注意两个+号的问题,如果中间不添加空格的话,那么可能会被按照 ++自增来运算。

var d = new Date();
console.log(+d); //1482134335608
var d = +new Date();
console.log(d); //1482134335609

日期直接转为毫秒数。

不过这样的写法可能在可读性上不是太好,并不推荐使用强制转换来得到毫秒数,所以可以有以下这几种写法,得到毫秒数:

        var timestamp = new Date().getTime();
        console.log(timestamp);//1482134515222
        var timestamp = (new Date()).getTime();
        console.log(timestamp);//1482134515222
        var timestamp = (new Date).getTime();
        console.log(timestamp);//1482134515222
        var timestamp = Date.now();
        console.log(timestamp);//1482134515222 推荐这种写法  

~运算符

~表示字位操作 “非”的相关枪支转换。

~x大致等同于-(x+1)。例如:

console.log(~42);//-43

这里需要注意的是,在-(x+1)中,唯一能够得到0的x值是-1,这个-1是一个稍为值,也就是被赋予了特殊含义的值。程序中一般用大于等于0的值来表示成功,-1表示失败。例如indexOf方法。

那么这种>=0或者==-1的写法不是很好,这种代码被称为“抽象渗漏”,意思是在代码中暴露了底层的实现细节。这里是指用-1作为失败时返回的值,这些细节应该被屏蔽掉。

例如:

     var a = "hello";
        if (~a.indexOf("lo")) {
            console.log("true");
        }
        if (!~a.indexOf("li")) {
            console.log("false");
        }

使用这种方法会比>=0更简洁。

~~字位截除

     console.log(Math.floor(-49.6)); //-50
        console.log(~~-49.6); //-49 优先级更高
        console.log(-49.6|0); //-49 优先级低

解析数字字符串

Number()和parseInt()方法的区别:

     var a = "42";
        var b = "42px";
        console.log(Number(a)); //42
        console.log(parseInt(a));//42
        console.log(Number(b)); //NaN
        console.log(parseInt(b));//42

可以看到parseint方法是执行到非不可转换为数字的字符时,停止转换,然后返回前面的值,而number方法则是要求整体必须都可以进行转换,才返回,否则返回NaN。

parseint方法的执行过程:

var a = {
            toString: function () {
                return "48";
            }
        }
        console.log(parseInt(a));//48

可以看到,parseInt方法先将参数转换为字符串再进行解析。

!!布尔值转换

        var a = 0;
        console.log(!!a);//false

隐式强制转换

+运算符

当有+运算符左右两端存在字符串时,那么会进行字符串连接,否则会进行加法运算。这是一般的情况下,但还有一种情况:

     var a = [1, 2];
        var b = [3, 4];
        console.log(a + b);//1,23,4

a和b都不是字符串,这里为何会进行拼接呢,这是因为在进行转换时,会先调用数组的valueOf方法,然而数组的valueOf操作无法得到简单的基本类型,所以转而调用tostring方法,这样返回两个字符串后,进行拼接。

a+""和String之间有一个席位的差别需要注意,a+""会调用valueof方法,然后通过tostring转为字符串,而String方法,会直接调用tostring方法。

  var a = {
            valueOf: function () { return 42 },
            toString: function () { return 4}
        }
        console.log(a + "");//42
        console.log(String(a));//4

||和&&

在JavaScript中,它们被称为选择器运算符更恰当。因为和其他语言不同,它们返回的并不是布尔值。

它们返回的是两个操作数中的一个(且仅一个),例如:

     var a = 42;
        var b = "abc";
        var c = null;
        console.log(a || b); //42
        console.log(a && b); //42
        console.log(c || b); //42
        console.log(c && b); //null

可以看出,它们返回的是两个操作数中的一个,而非布尔值。

可以换一个角度来理解:

a||b 可以理解为:

a?a:b

a&&b可以理解为:

a?b:a;

下面是一个常见的用法:

a=a||hello;

还有一种方法并不常见,一般在压缩工具中常见,即:

var a=43;
a&&foo();

这种被称为短路,也就是只有a在被定义时,才执行foo方法。

当它们在if中使用时,其实是由if对最后的结果进行了强制类型转换。如:

 var a = 42;
        var b = "abc";
        if (a && b) {
            console.log(true); //true
        }

也相当于:

  var a = 42;
        var b = "abc";
        if (!!a && !!b) {
            console.log(true); //true
        }

==和===运算符

我们常见的对它们区别的解释为:==是检查值是否相等,而===是检查值和类型是否相等。

这种解释还不够准确和全面,正确的解释为:==允许在相等比较重进行强制类型转换,而===不允许。

根据第一种解释,我们可以列举为===似乎做的事比==更多,因为它还要检查类型,而第二种则正好相反。

==在比较两个不同类的值会发生隐式强制转换,会将其中之一或两者转换为相同的类型,再进行比较。

例如:

     var a = 42;
        var b = "42";
        console.log(a == b); //true
        console.log(a === b);//false

那么到底是将字符串转换为数值,还是将数值转换为字符串呢,具体的转换规则如下:

如果type(x)是数字,type(y)是字符串,则返回x==ToNumber(y)的结果

如果type(x)是字符串,type(y)是数字,那么则返回ToNumber(x)==y的结果

==最容易出错的一个地方是true和false与其他类型直接的比较:

     var a = "42";
        var b = true;
        console.log(a == b); //false

我们都知道"42"是一个真值,那么为什么==的结果不是true呢,看一下规范:

如果type(x)是布尔类型,则返回ToNumber(x)==y的结果

如果type(y)是布尔类型,则返回x==ToNumber(y)的结果

所以在这里,根据规范,先将true转换为1进行比较,变成"42"==1,然而类型仍然不同,在进行转换,"42"被转换为42变成42==1,所以为false。

建议无论何时都不要使用 ==true 或者 ==false

在==中 null和undefined是相当的,也就是说只判断 ==null就可以把undefined的情况也判断进去了。

最特殊的情况:

 var i = 2;
        Number.prototype.valueOf = function () {
            return i++;
        }
        var a = new Number(42);
        if (a == 2 && a == 3) {
            console.log(true);//true
        }

可以看到,判断条件为true,所以最好不要修改内置原型。

所以==会在不经意间就进行了隐式转换,特别需要注意的两点:

如果两边的值中有true或者false,千万不要使用==

如果两边的值有[]、""或者0尽量不要使用==

这时应该使用ongoing===来避免这些不经意间的错误。

抽象关系比较

一般来说比较对象时,会将对象转换为字符串,然后对字母进行比较,比较常见的是"123">"13"为false。但是有几种特殊的情况,如:

        var a = { b: 42 };
        var b = { b: 43 };
        console.log(a < b); //false
        console.log(a == b);//false
        console.log(a > b);//false
        console.log(a <= b);//true
        console.log(a >= b);//true

这里需要注意的是,为什么a<b和a==b都为false,那么a<=b为什么为true?

因为根据规范,a<=b被处理为a>b然后将结果反转。因为a>b 的结果为false,那么反转的结果为true。

实际上JavaScript在处理>=时,是按照不大于的意思进行处理的,即(!(a<b))的结果。

 

原生函数和强制类型装换