【问题标题】:Does a matter whether a value is primitive or boxed值是原始值还是盒装值是否重要
【发布时间】:2011-07-22 16:40:43
【问题描述】:

可以使用typeof 来确定一个值是原始值还是装箱值。

考虑:

typeof "foo"; // "string"
typeof new String("foo"); // "object"

结合Object.prototype.toString我们可以定义以下两个函数

var toString = Object.prototype.toString;

var is_primitive_string = function(s) {
  return toString.call(s) === "[object String]" && typeof s === "string";
};

var is_boxed_string = function(s) {
  return toString.call(s) === "[object String]" && typeof s === "object";
};

这两个功能是否有任何用例? (或NumberBoolean 等的类似功能)。

这个问题背后的概念来自以下Comment by T.J.Crowder

我们是否应该关心我们拥有的值是原始值还是盒装值?

【问题讨论】:

    标签: javascript boxing typeof


    【解决方案1】:

    我想说这几乎没有意义,您几乎从不关心您是在处理 string 原语还是 String 对象。

    存在边缘情况。例如,String 对象是一个实际对象,您可以为其添加属性。这使您可以执行以下操作:

    function test(arg) {
        arg.foo = "bar";
    }
    

    如果调用代码传入string原语

    var s1 = "str";
    test(s1);
    

    ...arg 被提升为String 对象并获得添加到其中的属性,但String 对象在test 返回后不会被任何东西使用。

    相比之下,如果调用代码传入Stringobject

    var s2 = new String("str");
    test(s2);
    

    ...然后该属性被添加到该对象并且调用代码可以看到它。考虑(live copy):

    var s1, s2;
    
    s1 = "str";
    
    display("[Before] typeof s1.foo = " + typeof s1.foo);
    test(s1);
    display("[After] typeof s1.foo = " + typeof s1.foo);
    
    s2 = new String("str");
    
    display("[Before] typeof s2.foo = " + typeof s2.foo);
    test(s2);
    display("[After] typeof s2.foo = " + typeof s2.foo);
    
    function test(arg) {
      arg.foo = "bar";
    }
    

    输出:

    [之前] typeof s1.foo = undefined
    [之后] typeof s1.foo = undefined
    [之前] typeof s2.foo = undefined
    [之后] typeof s2.foo = string

    注意s2.foo 是一个字符串,但s1.foo 不是(因为s1 是一个字符串primitive,当我们在test 中提升它时创建的对象与使用调用代码)。

    这有什么用例吗?不知道。如果是这样,我会说这将是一个非常前卫的边缘案例。

    【讨论】:

    • 与原语相比,装箱值“通过引用传递”这一事实可能会产生一些有趣的结果。
    • @Raynos,有什么后果?基元是不可变的,因此基元的按值传递和按引用传递之间应该没有可观察到的差异。我不认为您可以编写任何程序来检测是否所有 trues 都作为对驻留在单个内存位置(如在 Rhino 中)的不可变对象的引用传递,并且它是一个标记联合就像在大多数其他解释器中一样复制。
    【解决方案2】:

    所有toString 的东西似乎都是为了解决不同内置String 构造函数的跨框架混合问题。这对于检查某个东西是否是原始字符串是不必要的——typeof 就足够了,所以is_primitive_string 没有用例。

    我很少看到作为 String 实例传递的参数,所以我不明白为什么我需要检查某些东西是否是 String 实例跨帧而不是仅仅通过 @987654329 强制为 String 值@ 或 String(s)。我唯一一次在生产代码中使用String 值是当我需要一个在某些highly optimized code 中为真的空字符串时。

    就其他情况而言,Boolean 类的实例在条件下的行为与预期不同。

    if (new Boolean(false)) {
      alert("WTF!");
    } else {
      alert("OK");
    }
    
    Boolean.prototype.not = function () { return !this; };
    
    if (new Boolean(false).not()) {
      alert("OK");
    } else {
      alert("Really, WTF!");
    }
    
    if (false.not()) {  // Autoboxing
      alert("OK");
    } else {
      alert("Cmon, WTF!");
    }
    

    !(false)true,但是当你使用创建Boolean 类的实例时,! 运算符适用于对象值,并且对象值始终为真。

    我相信 EcmaScript 5 严格模式正在改变 this 的呈现方式,因此最后一个示例 (false.not()) 将表现得像人们天真地期望的那样,当 "use strict"; 被添加到 Boolean.prototype.not ES5 解释器。

    使用Numbers,使用< 进行比较是可以的,加法和其他运算符往往会按预期工作。 new Number(0)new Number(NaN)new Boolean(false) 在条件方面存在相同的问题,当然

    alert(NaN === NaN);  // false
    var NAN = new Number(NaN);
    alert(NAN === NAN);  // true
    

    ===!== 通过引用比较所有StringNumberBoolean

    【讨论】:

    • 除了布尔逻辑和! 运算符。还有什么其他东西对盒装和原始类型产生了奇怪的作用。我们是否应该对此进行检查/防范?
    • @Raynos,显然new Number(0) 是真实的,var NAN = new Number(NaN); alert(NAN === NAN && !!NAN)
    【解决方案3】:

    我使用 underscore.js 方法来检测变量的类型。尝试使用: isEmpty, isElement, isArray, isArguments, isFunction, isString, isNumber, isBoolean, isDate, isRegExp isNaN, isNull, isUndefined

    这里描述: http://documentcloud.github.com/underscore/

    【讨论】:

    • 我们不是在讨论类型,而是在讨论检查一个值是原始类型还是特定类型(字符串、数字、布尔值)的盒装类型
    • 对不起。我误解了这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多