Does a matter whether a value is primitive or boxed
Asked Answered
D

3

15

One can use typeof to determine whether a value is primitive or boxed.

Consider:

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

In combination with Object.prototype.toString we could define the following two functions

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";
};

Are there any use cases for these two functions? (Or similar functions for Number, Boolean, etc).

The concept behind this question came from the following Comment by T.J.Crowder.

Should we ever care whether a value we have is primitive or boxed?

Disincline answered 22/7, 2011 at 16:40 Comment(0)
H
9

I'd say there's virtually no point, you almost never care whether you're dealing with a string primitive or a String object.

There are edge cases. For instance, a String object is an actual object, you can add properties to it. This lets you do things like this:

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

If calling code passes in a string primitive:

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

...arg gets promoted to a String object and gets a property added to it, but that String object isn't used by anything after test returns.

In contrast, if calling code passes in a String object:

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

...then the property is added to that object and the calling code can see it. Consider (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";
}

Output:

[Before] typeof s1.foo = undefined
[After] typeof s1.foo = undefined
[Before] typeof s2.foo = undefined
[After] typeof s2.foo = string

Note that s2.foo is a string, but s1.foo isn't (because s1 was a string primitive, the object created when we promoted it in test has nothing to do with the calling code).

Is there any use case for this? Dunno. I'd say it would be an extremely edgy edge case if so.

Hull answered 22/7, 2011 at 16:50 Comment(2)
The fact that boxed values are "passed by reference" compared to primitives can have some interesting consequences.Disincline
@Raynos, what consequences? Primitives are immutable so there should be no observable difference between pass-by-value and pass-by-reference for primitives. I don't think there is any program you could write that could detect whether all trues are passed as a reference to an immutable object that resides in a single memory location (as in Rhino) and one where it is a tagged union that is copied as in most other interpreters.Medical
M
3

All the toString stuff seems to be an attempt to workaround problems with cross-frame mixing of different builtin String constructors. That is unnecessary for checking whether something is a primitive string -- typeof is sufficient, so there is no use case for is_primitive_string.

I very rarely see arguments passed as String instances so I can't see why I would need to check whether something is a String instance cross-frame instead of just coercing to a String value via ("" + s) or String(s). The only time I've ever used a String value in production code was when I needed an empty string that was truthy in some highly optimized code.

As far as the others go, instances of the Boolean class don't behave as one might expect in conditions.

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) is true, but when you use create an instance of the Boolean class, the ! operator applies to the object value, and object values are always truthy.

I believe EcmaScript 5 strict mode is changing the way this is presented so the last example (false.not()) will behave as one might naively expect when "use strict"; is added to the top of Boolean.prototype.not in a valid ES5 interpreter.

With Numbers, comparisons using < are OK and addition and other operators tend to work as expected. new Number(0) and new Number(NaN) have the same problems as new Boolean(false) around conditions, and of course

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

and === and !== compare by reference for all of String, Number, and Boolean.

Medical answered 22/7, 2011 at 16:42 Comment(2)
Apart from boolean logic and the ! operator. Is there anything else that acts strangly on boxed vs primitive types. And should we ever check/gaurd against this?Disincline
@Raynos, obviously new Number(0) is truthy and var NAN = new Number(NaN); alert(NAN === NAN && !!NAN).Medical
E
-2

I use underscore.js methods to detect the type of the variable. Try to use: isEmpty, isElement, isArray, isArguments, isFunction, isString, isNumber, isBoolean, isDate, isRegExp isNaN, isNull, isUndefined

Described here: http://documentcloud.github.com/underscore/

Evaginate answered 22/7, 2011 at 16:45 Comment(2)
We're not talking about type, were talking about checking whether a value is a primitive or boxed type for a particular type (String, Number, Boolean)Disincline
Pardon me. I misunderstood the question.Evaginate

© 2022 - 2024 — McMap. All rights reserved.