I'm looking at what the Boolean object works. It can't be used as reference value transfer. Because the value can't be changed. What's the use of using the new operator? The problem can be solved by creating a new boolean object myself. But that doesn't answer the question, why doesn't there be a set function in the boolean object? Creating a boolean object means nothing to me. Because you can't do anything. Is there a solution other than creating a new object? and what does the boolean object do?
let bool=true;
let boolObj=new Boolean(true);
let ref=boolObj;
//set booObj false
console.log(ref.valueOf());//expected out:false
Is there a solution other than creating a new object?
If the problem is that you want an object with mutable boolean state, then yes, that's the solution:
const boolObj = {flag: true, valueOf() { return this.flag; }};
boolObj.flag = false;
console.log(boolObj.valueOf());
Note that that uses boolObj.flag = false;, not boolObj = false;. The latter would store false in boolObj, not modify the state of the object.
What's the use of using the new operator?
There is almost never any reason to create a Boolean object. Boolean objects, like all the primitive wrapper objects, are immutable. Their primary purpose is to provide a specification mechanism by which "methods" can be called on primitives:
const b = false;
console.log(b.toString()); // "false"
b is a primitive, so how can it have a method? The answer is: It can't. But when the JavaScript engine sees b.toString, it "promotes" the primitive to the equivalent object and then gets the method from that equivalent object. (Theoretically; obviously, engines optimize that object creation away when they can.) This doesn't mean much with the built-in methods, but you can add methods. Before ES5's strict mode, that meant that there had to be an object representation of the primitive:
Object.defineProperty(Boolean.prototype, "example", {
value() {
console.log("typeof this = " + typeof this);
return !this;
},
configurable: true,
writable: true
});
false.example();
That was necessary because before ES5's strict mode, this always had to be of type "object" (null or a reference to an object), it couldn't be a primitive. As of ES5's strict mode, this is allowed to be a primitive:
"use strict";
Object.defineProperty(Boolean.prototype, "example", {
value() {
console.log("typeof this = " + typeof this);
return !this;
},
configurable: true,
writable: true
});
false.example();
I said "almost never" above because there is one use case for Boolean objects, but it's not a very good one: Tristate flags. You can use null for the indeterminate state, new Boolean(true) for the true state, and new Boolean(false) for the false state. Again, though, it's not a great use case. :-)
Related
I am wondering why the Boolean coercion fails in this case:
!!(new Boolean(false)) === true
Although:
(new Boolean(false).valueOf()) === false
Mozilla says:
Booleans are returned as-is.
I am wondering what "as-is" means in the context of a coercion. I thought "coercion" means "convert anything to a primitive boolean". How is it possible that something which is meant to be false gets coerced to true?
BTW: Consequently this fails too:
Boolean(new Boolean(false)) === true
It seems to me, that the Boolean class itself is an error. Maybe I have to use the following code:
if (arg instanceof Boolean)
throw new Error("Stop doing nonsense")
Or maybe this:
function coerce_boolean_correctly (arg) {
if (arg instanceof Boolean)
return coerce_boolean_correctly(arg.valueOf())
return !!arg
}
There is a huge difference between a Boolean object and a Boolean primitive. The MDN page you referenced, actually warns about this at the very outset:
Do not confuse the primitive Boolean values true and false with the true and false values of the Boolean object.
Any object, including a Boolean object whose value is false, evaluates to true when passed to a conditional statement.
And this (what I marked in bold) is exactly what happens in your code: it creates a new Boolean(false) and that will coerce to true -- in other words it is a truthy value. In your code you have explicitly converted it to a boolean primitive, by applying ! to it twice. In either case (implicit coercion or explicit conversion) new Boolean(false) is truthy. Fact is that all objects are considered truthy (when coerced to boolean, they evaluate to true).
The article continues with:
Do not use the Boolean() constructor with new to convert a non-boolean value to a boolean value — use Boolean as a function or a double NOT instead.
This suggests that your code should be modified to drop the use of new, and call Boolean as a plain function, not as constructor:
!!(Boolean(false)) === false
When Boolean is called as plain function, it returns a primitive boolean (false or true). But anything that is called as constructor, even Boolean, will return an object. And objects are truthy.
When in the context of coercion MDN states "Booleans are returned as-is." they refer to boolean primitives. (Boolean) objects are covered by the last bullet point in the same list: "All objects become true". The latter in includes Boolean objects.
A Boolean object has a valueOf method which returns a boolean primitive, and so it returns what you would intuitively expect.
The MDN article rightly says:
Warning: You should rarely find yourself using Boolean as a constructor.
Don't use new Boolean, but Boolean.
If for some reason you have a Boolean object, really ask yourself why you have that object in the first place. Tackle the code that created that object and use a primitive boolean from the outset.
Objects are truthy
Some are surprised in a similar way that the following holds:
if (!!new String("")) console.log("An empty string is truthy!?");
if (!!new Number(0)) console.log("The number 0 is truthy!?");
if (!!new Number(NaN)) console.log("NaN is truthy!?");
if (!!new Object(null)) console.log("null is truthy!?");
if (!!new Object(undefined)) console.log("Undefined is truthy!?");
It is the same principle: objects are truthy. Always.* No matter what their constructor is named. No matter what value was passed to that constructor.
* document.all is an abhorrent, but accepted, violation of this rule.
See also
What's the point of the Boolean object?
What is the purpose of new Boolean() in Javascript?
I've heard similar questions, but not the answer that I wanted;
I do not count const because:
1).
it doesn't actually make it immutable, it only makes the reference immutable
2).
it messes with the scope, and I want it to work outside the block, too
3).
not all browsers support it yet
{
const hello = ["hello", "world"];
hello.push("!!!");
console.log(hello);//outputs "hello", "world", "!!!"
}
//and it doesn't, and shouldn't, work here
console.log(hello);
Just use Object.freeze
const immutableArray = Object.freeze([1,2,4])
You can use Object.freeze for this (obviously only on objects).
const hello = Object.freeze(["hello", "world"]);
// hello.push("!!!");
// will throw "TypeError: can't define array index property past the end of an array with non-writable length"
// hello.length = 0;
// will fail silently
// hello.reverse();
// will throw "TypeError: 0 is read-only"
// hello[0] = "peter";
// will fail silently
From MDN:
The Object.freeze() method freezes an object. A frozen object can no longer be changed; freezing an object prevents new properties from being added to it, existing properties from being removed, prevents changing the enumerability, configurability, or writability of existing properties, and prevents the values of existing properties from being changed. In addition, freezing an object also prevents its prototype from being changed. freeze() returns the same object that was passed in.
However, there is no keyword to define a completely immutable variable without using Object.freeze or Object.seal on the variable's value.
For a less restrictive approach Javascript also has Object.seal().
The way to do it without const is to use Object.defineProperty, and like I wanted, it behaves like var in terms of scope:
{
Object.defineProperty(typeof global === "object" ? global : window, "PI", {
value: Object.seal(3.141593),
enumerable: true,
writable: false,
configurable: false
});
}
console.log(PI); // 3.141593
The only problem is that it that it doesn't throw an error outside of strict mode.
Why does comparing an explicitly called String constructor to an implicit string evaluate true, but adding the new keyword makes it evaluate false on deep equals, but true again on shallow equals?
> "hello"===String("hello")
true
> "hello"==new String("hello")
true
> "hello"===new String("hello")
false
Edit: after further testing, this appears to happen with all types that have implicit constructors.
Edit 2: to clarify, this is not a question of == vs. ===, but one of implicit vs. explicit constructors.
When you use the new keyword you are creating an object. If you were to check the typeof new String('hello') you will see that it is of type object. Checking the type of 'hello' on its own will yield string.
As you may know, using a strict equals operator (===) will check for both value and type, so it will return false due to the types not matching.
The reason the expression without the new keyword returns true is because calling upon the String global object is not the same as calling the constructor using new:
String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings.
As such, the type of the return value will be string and not object, so the strict check will return true.
The difference between == and === is that === requires the type to be the same, while == does not.
So this is telling you that both "hello" and String("hello") are of the same type (which is string); but new String("hello"), while it has the same value, is a different type (which is object)
I'm curious about whether there is any way to fake out Array.isArray() with a user-defined object.
From the book JavaScript Patterns:
Array.isArray([]); // true
// trying to fool the check
// with an array-like object
Array.isArray({
length: 1,
"0": 1,
slice: function () {}
}); // false
That object clearly fails, but is there any other way to do it? This is sheer curiosity, and not because I think that you could ever screw with .isArray() in regular client code (though it would obviously be fantastic to know if you could!).
Only if you set the internal [[Class]] property to "Array", which is not possible afaik. From the specification:
The isArray function takes one argument arg, and returns the Boolean value true if the argument is an object whose class internal property is "Array"; otherwise it returns false.
Or you go the other way round: Create a normal array and explicitly set every array method to undefined.
Array.isArray = function () { return true; }
And if you want to be naughty
Array.isArray.toString = function () {
return 'function () { [native code] }';
};
What is the use of:
var flag = new Boolean(false);
compared to:
var flag = false;
When would you actually use new Boolean?
The global function Boolean() can be used for type casting when called without new, eg
var foo = Boolean(bar); // equivalent to `var foo = !!bar`
When called with new, a wrapper object will be created additionally, which means that you can assign arbitrary properties to the object:
var foo = new Boolean(bar); // equivalent to `var foo = Object(Boolean(bar));`
foo.baz = 'quux';
alert(foo.baz);
This is not possible with primitive values as primitives can't hold properties:
var foo = true;
foo.baz = 'quux';
alert(foo.baz); // `foo.baz` is `undefined`
Assigning a property to a primitive doesn't produce an error because of auto-boxing, ie
foo.baz = 'quux';
will be interpreted as
// create and immediately discard a wrapper object:
(new Boolean(foo)).baz = 'quux';
To get the primitive value back, you'll have to invoke the valueOf() method. This is needed if you want to actually use the wrapped value, because objects always evaluate to true in boolean contexts - even if the wrapped value is false.
I've never come across a useful application of being able to assign properties to booleans, but boxing might be useful in cases where a reference to a primitive value is needed.
While others mentioned the theory, let me talk about the practical part:
Because Boolean objects (as objects in general) are always truthy, it is considered bad practice to use them. In many years of JS programming, I have never used them, and I can't remember seeing Booleans in other peoples' code either. Not even once.
Using primitive values will avoid confusion and will make your code a little bit shorter.
If you ever need a bool wrapped in an object, you might as well use an Object object like so:
foo = { value: false };
Also, calling the Boolean() constructor as a function (as in foo = Boolean(bar)) has the same effect as explicit typecasting using !!, and the latter is generally preferred over the former.
Before the above question first the Boolean function, Boolean ()
Boolean(10 > 4) // return true
Boolean(4 > 9) // return false
Next: everything with real value return true. E.g
100
-4
4.4
"hello"
"false" // note even the string value false return true.
everthing without real value return false E.g
NaN
var x = 10 / "H"; // Boolean(x); return false.
undefined
""
0
-0
false
null
Now the Boolean object is an object wrapper for a boolean value. The value passed as the first parameter is converted to a boolean value, if necessary. If value is omitted or is 0, -0, null, false, NaN, undefined, or the empty string (""), the object has an initial value of false. All other values, including any object or the string "false", create an object with an initial value of true.
This allows very powerful tricks.
Interesting question:
You use new Boolean to create a boolean object. There can be many scenarios but I have discussed below one scenario.
Suppose you want a comparison in your code where you want to match string value and its datatype and it has to bool (true/false) then you will use new boolean instead of assigning simple false value.
var flag = false;
var flag2 = new Boolean (false);
alert(typeof flag); //boolean object
alert(typeof flag2); //simple object
if (flag === flag2){
alert("Value and datatype match");
}
else{
alert("Value and datatype do not match");
}