15 April 2013

(原创翻译)JS中的判断为真与相等(Truth, Equalit and JavaScript)--补充

补充一下我的前一篇文章(原创翻译)JS中的判断为真与相等(Truth, Equalit and JavaScript)

文中其他地方都好理解,主要是当 object 与其他类型做 ==+ 等操作时,强制转换类型时调用的 toPrimitive 方法多多少少会让人比较迷惑。

我又翻阅了另一篇资料http://www.adequatelygood.com/Object-to-Primitive-Conversions-in-JavaScript.html

中间详细讲解了 toPrimitive 的运作过程。

我在这里就凭自己的理解总结下,有兴趣的朋友可以去原文中阅读更多内容。

(另外不得不吐槽下,JS真的是一门很神奇的语言,是那么的灵活,那么的吸引人…但同时也是那么的蛋疼。)


### 正文开始 ———-

首先一步步来,JS中的基本类型与引用类型,不是很清楚的同学可以Google下或者翻看我以前的文章

原创翻译–JS中的判断为真与相等(Truth, Equalit and JavaScript)

当我们对一个对象与一个基本类型使用 + 操作符时,它会对对象使用内置的 ToPrimitive 方法。

这个方法会优先调用对象的 valueOf 方法,如果不返回基本类型,再调用对象的 toString 方法,如果还不能获得基本类型,报 TypeError错误

toString方法

所有对象都从Object.prototype中继承了一个toString方法,它返回 [object Object] 字符串,我们可以重写这个方法,包括直接写在每个实例中或者写进它的prototype。

原文中写法如下:

function population(country, pop) {
    return {
        country: country,
        pop: pop,
        
        toString: function () {
            return "[Population " + 
                "\"" + country + "\" " +
                pop +
            "]";
        }
    }
}

toValue方法

同样的,所有对象也从Object.prototype中继承了valueOf方法,它返回这个对象本身。

同样的,我们经常会将它重写成我们希望的结果,返回一个基本类型,好让它能够工作在 + 操作符中。

原文中写法如下:

function population(country, pop) {
    return {
        country: country,
        pop: pop,
        
        toString: function () {
            return "[Population " + 
                "\"" + country + "\" " +
                pop +
            "]";
        },
        
        valueOf: function () {
            return pop;
        }
    };
}

我们看一个例子,它同时重写了foo对象的toString与valueOf方法:

var foo = {
    toString: function () {
        return "foo";
    },
    valueOf: function () {
        return 5;
    }
};

alert(foo + "bar"); // 5bar
alert([foo, "bar"].join("")); // foobar

可以看到,当使用 + 操作符导致对对象强制转换为基本类型时,优先调用的是 valueOf 方法。

值得注意的是,当你重写这两个方法的时候,JS并不会检查返回值的类型,只要它们是一个基本类型。

所以你可以这样写:

var foo = {
    toString: function () {
        return 5;
    },
    valueOf: function () {
        return "foo";
    }
};
alert(foo.toString() + 1); // 6 (bad!)
alert(foo + 1); // "foo1" (no good!)
alert(+foo); // NaN (the worst!)

可以看到,如果将valueOf或者toString重写为返回其他类型的值,非常容易导致错误!

所以,请不要在重写对象的toString方法时返回不是字符串类型的返回值!

总结下两篇文章吧…其实啰里啰嗦的,总结起来很简单:

  • 在if()等条件判断语句中

为true的有:(true,”potato”,36,[1,2,4],{a:16}),其中对象永远为true,包括{},

为false的有: (false,0,”“,null and undefined)。

  • 在判断相等“==”操作符中

除了null和undefined相互相等外,其他大部分类型都是先强制转为了数字(toNumber),再比较。

如果操作符中出现了对象,对对象使用ToPrimitive方法,既优先调用valueOf,不返回基本类型的话再调用toString(除了Date对象是优先调用toString)。

转为基本类型后再做比较。NaN不与任何东西相等,包括自己。

  • 严格判断相等“===”中

null和undefined永远不会===其他类型,NaN不会===任何东西,包括自己。

对对象使用===时,只有两个对象指向同一实例时才能相等,哪怕是一模一样的两个实例都不===。

  • 在使用“+”操作符时

根据《Javascript权威指南》上的说法,当两边都是数字或者都是字符串时,结果显而易见。

对于其他情况,要进行类型转换,+号的转换规则优先考虑字符串连接,如果其中一个值为字符串,那么另外一个转为字符串。如果都不是类字符串(string-like),则会进行算数加法。

如果其中一个为对象,就会首先根据上面所提的toPrimitive方法转为基本类型,转换完后,再按上面的规则进行操作。

一些例子:

"1" + 2// "12"

1+{} // "1[object Object]"

true + true// 2

2 + null // 2

2 + undefined // NaN   ,注意  undefined转换为数字是NaN,而null转为数字是0

Posted in 2013-04-15 20:30