JS 强制类型转化

  在Js中, 强制类型转化分为两种情况: 一种是引用类型转化基本类型, 如数组转化成数字;一种是两种不同基本类型之间的转化,如字符串转化为数字。你不能将基本类型转化成引用类型,比如,不可能把数字转化为数组。 基本类型之间的转化相对容易,引用类型转化为基本类型则要复杂的多,转化又分为两种情况,转化为字符号或转化为数值

  当引用类型转化为字符串的时候,JS会先调用引用类型的toString 方法,看它能不能返回基本类型,如果能,则使用该基本类型,如果不能,则再调用引用类型的valueOf()方法,如果valueOf方法能返回基本数据类型则使用该基本数据类型 ,如果不能,就会抛出TypeError错误。你可能还记得 字符串 "[object object]"

var arr = [5, 5]; 
console.log(arr + '');  // "5, 5"  调用数组的toString()方法

  当引用类型转化为数值的时候,JS则先调用引用类型的valueOf()方法,同样是看它能不能返回基本类型,如果能就用,如果不能,则继续调用toString()方法,如果toString()方法也不能返回基本类型,则抛出TypeError错误。

console.log(+ new Date());  // 时间毫米数1513860979488, 数值类型

  知道了转化规则,我们就可以利用它来转化我们自定义的对象,但有的时候,转化并没有我们上面看到的那么简单,看下面的代码

var Money = function(val, sym) {
    this.currentSysmbol = sym;
    this.cents = val;
}
var dollar = new Money(100, '$');

console.log(+dollar);  // NaN
console.log("Total" + dollar);  // '[object, object]'

  得到的结果并没有什么意义,我们来给它提供toString, valueOf()方法

Money.prototype.toString = function() {
    return this.currencySymbol + (this.cents / 100).toFixed(2);
}
Money.prototype.valueOf = function() {
    return this.cents
}
console.log(+dollar);  // number 100
console.log("" + dollar);   // string 100

  这时你会发现一个问题:  " " +dollar, 按理说,它应该调用toString 方法,返回$1.00, 但它却返回了100, 证明它这里调用的是valueOf()方法,而不是toStirng()方法,这和我们的规则出现了不一致。 但是当我们把dollar 用数组包起来的时候,它返回调用的是toString()方法

console.log("" + [dollar])  // $1.00

  这时要看一下js的标准,

  

  转化成基本类型的抽象方法ToPrimitive 接受一个必选的参数(input argument) ,就是一个引用类型object和一个可选的参数(PreferredType), 然后把引用类型转化成基本类型。如果一个引用类型能够返回不止一个基本类型时,它就可能需要PreferredType 来决定返回哪一个。这个方法是怎么把引用类型转化成基本类型的?

   

  转化成基本类型,就是标准中所说的return a default value, 需要调用引用类型内置的方法,然后给它传递可选的参数(hint PreferredType). 如果再深入挖掘标准, 你会发现所说的方法,就是toString()和valueOf(), 返回字符串或数值, 到底是返回哪种基本类型取决于传入的可选参数hint PreferredType. 如果hint参数没有传递的话,它默认返回数值number。请下面的标准

  

  这也就解释了("" + dollar)为什么没有调用我们定义的toString()方法,而是调用valueOf()方法了, 因为我们没有提供hint PreferredType参数,默认返回了数值。基本操作符并不能直接用来计算引用类型,它只能计算基本类型, 当计算引用类型的时候,它进行强制类型转化,而这种转化到底转化成哪种基本类型,是js自己做决定。那我们能不能提供一个hint Preferred 参数,把决定权拿回到我们手里。很不幸,js并不没有提供这样的机制,也就是说,我们不能提供一个hint 参数给我们自己定义的对象

  但是当我们把dollar用数组括起来,它却调用了我们自定义的toString()方法. "" + [dollar] 返回 $1.00, 这又怎么解释呢? 这涉及了Js的内置对象。

  

    对于内置对象的来说, 它必须返回一个基本类型。我们把dollar用数组包括起来,但由于涉及到加号操作,所以[dollar] 要转化成基本类型,根据规则,它调用的valueOf()方法,但是数组的valueOf方法返回数组本身,不是基本类型。所以再调用数组的toString()方法,数组的toString()方法就是数组的每一项都调用toString()方法,然后再拼接起来,形成一个大的字符串,所以这里调用的是dollar 对象的toString()方法。

  我们可以模拟一个ToPrimitive的方法 

var ToPrimitive = function(obj) {
    var funct, functions, val, _i, _length;

    functions = ['valueOf', 'toString'];

    if(typeof obj === 'object') {
        // js 对Date日期对象作了区别处理, 先调用toString, 再调用toValue
        if (obj instanceof Date) {
            functions = ['toString', "valueOf"];
        }
        for(_i = 0, _length=functions.length; _i < _length; _i++){
            funct = functions[_i];
            // valueof 或 toString 方法都是函数时才调用
            if (typeof obj[funct] === 'function') {
                val = obj[funct]();
                if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
                    return val;
                }
            }
        }
        // 如果两个方法都不能转化成基本数据类型,则抛出错误
        throw new Error('DefualtValue is ambuiguos')
    }
    return obj;  // 如果传递进来的参数不是引用类型,则返回
}

  现在我们已经明白了js的类型转化,再做一个复杂的例子加深一下印象, 下面这行代码输出多少

console.log(++[[]][+[]]+[+[]])

  1, 先计算数组内的元素+[], 前面的加号肯定是要数组转化成基本类型,数组转化成基本类型,只能调用它的toString()方法,空数组转化为空字符串'', +'' 表示对空字符串转化为数字,它这里调用的是Number()函数,所为转为化0, 现在变成了

console.log(++[[]][0]+[0])

  2, 加号左边[[]][0], 表示它取的是[[]] 里面的第0个位置的元素,[[]] 表示它是一个二维数组,数组里面包括数组,所以它的第0个位置上的元素还是数组,变成了

console.log(++[]+[0])

  3 , 左边是++[], 还是对数组进行基本类型转化,上面说了 [] 转化为空字符串,++‘’ 则变成了数字1,

console.log(1+[0])

  4, 右侧还是要做类型转化, 数组[0] 调用toString,则变成了字符串‘0’ 

console.log(1+'0')

  5, 最终的结果就是字符串‘10’

posted @ 2017-12-21 22:11  SamWeb  阅读(4058)  评论(0编辑  收藏  举报