I'm working on an open source project and stumbled over bitwise operators. I do understand the principles and also that javascript evaluates non-zero integer values to true (correct me here if I'm wrong; found this statement in an accepted answer in another post).
The code in question is as follows:
function example(){
var args = arguments;
var j = 1 & args[0];
.
.
.
}
var test = {keyA: 1, keyB: 2};
example(test);
My Question is: What's the value of j
?
What is the binary equivalent of an object?
As far as i understand it, j = 0
if the last bit in test
is 0 and j = 1
if the last bit in test
is 1.
Please help me out here guys. I spend the last hour searching any nearly related post here and most topics are about numbers, one or two were about booleans and that's that.
Edit: As the code example given above doesn't seem to be as clear as i thought, here the full function as i found it (and working):
function Extend() {
var args = arguments;
var target;
var options;
var propName;
var propValue;
var deep = 1 & args[0];
var i = 1 + deep;
target = args[i - 1] || {};
for (; i < args.length; i++) {
// Only deal with non-null/undefined values
if (options = args[i]) {
// Extend the base object
for (propName in options) {
propValue = options[propName];
if (propValue !== undefined) {
propValue = options[propName];
target[propName] = (deep && IsPlainObject(target[propName])) ? Extend(deep, {}, propValue) : propValue;
}
}
}
}
// Return the modified object
return target;
}
var _someVariable = Extend({keyA: 1, keyB: 2, keyC: 10}, _someOtherVariable);
deep
has to have some meaning as it determines whether to enter the FOR-loop ... or not.
The intent of the deep
variable is to detect the difference between these two calls:
Extend({keyA: {a: 1}, keyB: 2}, {keyC: 3, keyA: {b: 2}});
and
Extend(true, {keyA: {a: 1}, keyB: 2}, {keyC: 3, keyA: {b: 2}});
The first variant will return this:
{keyA: {b: 2}, keyB: 2, keyC: 3}
The second is intended to return this:
{keyA: {a: 1, b: 2}, keyB: 2, keyC: 3}
So the function in fact allows for a first, optional argument, that will make the function apply the extension recursively so you get a deep instead of a shallow extended object.
You can also see this intent by analysing the recursive call, where the first argument is deep
, the second is the object to extend, and the third the object to extend with.
The following line also shows this:
var i = 1 + deep;
As i
is point where the loop will start from, you can see that if deep
is set to 1 instead of 0, the loop will start processing from argument #2 onwards, which makes sense, as argument #0 was recognised as being the optional argument, and the next one is the target object.
Note that the function accepts a variable number of additional arguments which it will use to extend the target object with. It is over these arguments that the i
variable loops.
As a side note: because of a bug, the second version returns the same as the first. To fix the bug, replace
target[propName] = (deep && IsPlainObject(target[propName]))
? Extend(deep, {}, propValue) : propValue;
with:
target[propName] = (deep && IsPlainObject(target[propName]))
? Extend(deep, target[propName], propValue) : propValue;
Now, coming to the essence:
var deep = 1 & args[0];
The use of the bitwise operator must have been an idea to have efficiency rule over clarity. The intent was to set deep
to 1 if the first argument represented the optional argument, which should be a boolean indicating whether the extending should happen shallow or deep. As objects will make this expression evaluate to 0, it seemed like a nice trick.
But there is an issue with this. If one would like to do this:
Extend(["3"], {myattr: 2});
One would expect to get back an array-like object with an additional custom property myattr
:
{0: "3", length: 1, myattr: 2}
However, the current Extend
function will misinterpret the first argument as an instruction to perform a deep extension. This is because
1 & ["3"]
will evaluate to 1 & 3
, which evaluates to 1. And so the result will be the second argument without any extension happening:
{myattr: 2}
Conclusion: it is better to avoid such cryptic use of bitwise operators, and do something like this:
var deep = args.length > 0 && typeof args[0] === 'boolean' && args[0];
In common language: let deep
be true (1) when there is at least one argument and that argument is a boolean and its value is true. In all other cases let it be false (0).
Note that one cannot pass false
as the first argument, thinking that it will make the function perform a shallow extension. In this case,
that argument will be taken as the object to extend, which will fail. So the optional first argument, if provided, must be a boolean with value true.
This is true both for the original Extend
function and the corrected one.
Finally, it would be good to add comments to this function to clarify the use of the first optional argument.