Please help me out to understand this example.
function fun(a){
this.length = 1;
this.splice = [].splice;
this[0] = a;
return this;
};
Now, when I execute this function the result is an array.
f = new fun('hi');// result will be : ["hi"]
Why is that?
If I remove this.length=1 OR this.splice = [].splice, the result will be different.
f = new fun('hi'); // result will be : fun {0: "a", splice: function}
Why is that?
I also see this technique used in jQuery. Please describe to me how this is working programmatically.
There is nothing programmatical about it. It's just the way a browser/js engine chooses to show you some variable in the console.
The console is generally for developers as they are the ones who open those things. So the browser does a little sniffing to show the developer what this object he's/she's dealing with is. If it looks like an array, it probably should be printed like an array.
Like the following list shows, there are some differences between the browsers. Namely, IE and node do nothing. My interpretation is that printing in node should yield the complete view and not just the sniffed view. The length seems to satisfy Opera 12 to show it as an array.
Why browsers use index, length and splice is an entirely different story. It's probably the smallest set of properties that denote an array with high probability. Look at it, what else would it be, if not an array?
Chrome 36, Firefox 30 and Opera 18 behave the same way:
fun as-is is ["hi"]
fun without length is fun { 0: 'hi', splice: function}
fun without splice is fun { 0: 'hi', length: 1}
Opera 12 shows:
fun as-is is Object ["hi"]
fun without length is Object
fun without splice is Object ["hi"]
IE 9 and node v0.8 did no sniffing at all (here IE output, node output very similar):
fun as-is is {0 : "hi", length : 1, splice : function splice() { [native code] }}
fun without length is {0 : "hi", splice : function splice() { [native code] }}
fun without splice is {0 : "hi", length : 1}
What you construct is not an array. I believe that your object may have a few features that let console recognize whether a given object is an array or not.
var arr = [];
var f = new fun('asd');
typeof arr; // "object"
typeof f; // "object"
arr instanceof Array; // "true"
f instanceof Array; // "false"
I'm not sure, how you can get that only "hi" result, because when I tried below code in a Fiddle, I get Object { 0: "hi", length: 1, splice: splice() }. Which is make sense because what it does is creating an object which has 3 attributes(length, splice, and index[0]).
function fun(a){
this.length = 1;
this.splice = [].splice;
this[0] = 'hi';
return this;
}
f = new fun('hi');
console.log(f); //get the object f
console.log(f[0]); //get the string inside index-0
console.log(f.length); //get the length value which is already assigned before
console.log(f.splice); //it returns a function, which I believe a splice function
Related
This is in continuation to the answer posted for the question "Convert JavaScript dot notation object to nested object".
The code works like a charm but I'm unable to wrap my head around how!! So a few days later + a situation where my console.logs actually exceed my lines of code :P.. Here's my question:
Below code for JavaScript function:
function deepen(o) {
var oo = {}, t, parts, part;
for (var k in o) {
t = oo;
parts = k.split('.');
var key = parts.pop();
while (parts.length) {
part = parts.shift();
t = t[part] = t[part] || {};
}
t[key] = o[k]
}
return oo;
}
console.log(
deepen({ 'ab.cd.e' : 'foo', 'ab.cd.f' : 'bar', 'ab.g' : 'foo2' })
);
It deepens a JSON object from :
{
'ab.cd.e' : 'foo',
'ab.cd.f' : 'bar',
'ab.g' : 'foo2' }
Into a nested object :
{ab: {cd: {e:'foo', f:'bar'}, g:'foo2'}}
I get the part where for each key value pair, the logic pops the last element post splitting into an array by ".".
That becomes the key.
What I'm not understanding is the below.
1) The function is returning 'oo' but the operations are all on 't'. The only relationship is that t is being assigned the'empty object' "oo" at the beginning of every iteration on the flat JSON.
2) after the "while (parts.length)" loop, oo miraculously has the nested structure whereas t has one level below it. if oo is assigned to t, how is that possible?
3) I don't see the function being called recursively. How is 00 getting nested beyond the first element of the flat JSON?
I'll first redefine the function with some better names, this way explanation is a lot easier to do.
function deepen(object) {
var nestedObject = {}, cursor, nestingPath, nodeKey;
for (var dotKey in object) {
cursor = nestedObject;
nestingPath = dotKey.split('.');
var leafKey = nestingPath.pop();
while (nestingPath.length) {
nodeKey = nestingPath.shift();
cursor = cursor[nodeKey] = cursor[nodeKey] || {};
}
cursor[leafKey] = object[dotKey];
}
return nestedObject;
}
My guess is that don't entirely know how the while loop functions. Important to know is that when two variables refer to the same object both change when you change one. They are the same object, but you've chosen to have two handles.
Let me provide an example:
object = {};
cursor = object;
cursor.foo = "bar";
object; //=> {foo: "bar"}
cursor; //=> {foo: "bar"}
cursor.a = {};
object; //=> {foo: "bar", a: {}}
cursor; //=> {foo: "bar", a: {}}
cursor = cursor.a;
object; //=> {foo: "bar", a: {}}
cursor; //=> {} <- this is ^
cursor.b = "c";
object; //=> {foo: "bar", a: {b: "c"}}
cursor; //=> {b: "c"}
The while loop is mostly based upon this principal. It's not easy to explain, but I hope the above clarifies things.
Another thing that might be confusing is the line:
cursor = cursor[nodeKey] = cursor[nodeKey] || {};
// read as
cursor = (cursor[nodeKey] = (cursor[nodeKey] || {}));
This could also be written as:
if (!cursor[nodeKey]) cursor[nodeKey] = {};
cursor = cursor[nodeKey];
This assigns a new object to the dynamic nodeKey property if the property isn't there (falsy). Then cursor is assigned to the nested object within, similar to my example above cursor = cursor.a.
First, you're not working with JSON, but a JS object. Most of the time, you should see object as HashMap<String, HashMap<String, ...>> ad infinitum, if you need a Java analogy.
Your questions:
t = oo means they both refer to the same instance created at the start of the function. Why do you use a second variable?
t = t[part] You literally assign entry of t to t
I didn't test the code, but I'm pretty sure it's buggy. Test what happens with object that have multiple names in top level, eg. {'a.b':1, 'b.a':1}. You don't need recursion though, you could use stack instead.
Regarding your code:
Use descriptive names and comments, especially when asking question where other people need to understand your code
Do not define all variables at the beginning of the function. That old habit comes from the dawn of C language and needs to die
for (var k in o) is not a recommended approach to iterate over object entries. Use Object.entries
There is no need to pop from parts array, it is reset every iteration. for(const part of parts) would work just as well
following the snippet bellow, why an array with one position is not working properly when using IN operation?
You can see that with 2 item the operation works fine.
Why this is happening?
What's the work around?
// helper function
function showResult(result) {
document.querySelector('#result').innerHTML += result+'<br/>';
}
var a = ["1"]
var b = "1"
showResult(b in a);
//false
var a = ["1","2"]
var b = "1"
showResult(b in a);
//true
<div id="result"></div>
That's not what in is for. in is not for searching an array for an element. It's for searching an object for a key.
Example:
var x = {a: 1, b: 2};
console.log('a' in x); // true
Your 2nd example works because the array (object) a contains a key called 1.
The in operator checks if an object has a key, not a value. Your second case returns true because any array with two elements has keys '0' and '1'. It doesn't matter that '1' also happens to be a value in that array; '1' in ['foo', 'bar'] is also true.
To test if an element is in an array, you should use Array.prototype.indexOf or in modern engines Array.prototype.includes. You can use the polyfill from either of those links to get support for that method in all engines:
// Most engines (IE 9+)
showResult( a.indexOf( b ) >= 0 );
// Modern engines only:
showResult( a.includes( b ) );
The first result is false because a does not have an index 1, while the second version of a has that index.
The in operator is not about affirming values, but properties (keys).
The confusion is maybe because CoffeeScript does use in for searching values. See here how it translates that into an indexOf
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 = 1if the last bit in testis 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);
deephas 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.
Bitwise operators do work on 32bit (un)signed integers. If you pass in anything that is not a number, it is implicitly coerced to a number. This is done using the valueOf/toString methods of the object as usual, and for your object {keyA: 1, keyB:2} will yield NaN. Then, this number is casted to an (U)int32, which gives 0 for NaN.
Check the spec for toUint32, follow it to ToNumber and on from there.
I have built an example using a couple of js classes for declaring Flag Enum and Flag Selections.
The source code is here in my github repo.
Basically to answer the question, and as mentioned by #Bergi, your class should implement valueOf() to return the value of the 'selections'.
// Example:
_w.Feature = new FlagEnum("Feature", {
CUSTOMER : "customer info" // 2
, ORDER : "order details" // 4
, DELIVERY : "delivery details" // 8
, DELIVERY_LIST : "listing of all deliveries" // 16
, DRIVER : "driver details" // 32
})
let [CUSTOMER, ORDER, DELIVERY, DELIVERY_LIST, DRIVER] = Feature.get_values()
features = new FlagSel(Feature, CUSTOMER | ORDER | DELIVERY)
// -or-
features = new FlagSel(Feature, [CUSTOMER, ORDER, DELIVERY])
// Using in code
// Check feature using bitwise mask:
if (features & ORDER){
pc("features includes ORDER")
}
// -or-
// Check feature using has method:
if (features.has(ORDER)){
pc("features includes ORDER")
}
// Managing values:
features.add(CUSTOMER)
features.rem(ORDER)
// Print available options
console.log(Feature)
// output: <FlagEnum - Feature> options: CUSTOMER, ORDER, DELIVERY, DELIVERY_LIST, DRIVER
// Print selected options
console.log(features)
// output: <FlagSel - Feature> CUSTOMER, ORDER, DELIVERY
Consider the following two ways of creating the instance of an object:
function f() { return { k: 'v'} }
var inst1 = f();
function F() { this.k = 'v'; }
var inst2 = new F();
The behavior of inst1 and inst2 is the same, the only difference is that a different constructor is saved to the object:
inst1.constructor; // Object()
inst2.constructor; // F()
What's the constructor of an array? See:
var xs = [];
xs.constructor; // Array()
Until this Point I understand the logic. But I bumped into the following:
var tags = document.getElementsByTagName("*");
typeof tags; // object
tags.constructor; // HTMLCollection()
So far it's similar to the inst2 example. But when I console.log with Firebug, I receive something like an Array with a 'named' constructor:
console.log(tags); // HTMLCollection[tag1, tag2, ...]
The block brackets confuse me, I would have expected curly one here. There must be an explanation to this, anybody knows the answer?
It sounds like the crux of your question lies in determining how Firebug displays your object. Firebug examines objects you passed to it, and chooses how it will display objects as a string based on what properties they have.
HTMLCollection is "array-like" so it has a length property, and stores its contents in properties named 0, 1, 2, etc. This makes it array-like and Firebug recognizes this, outputting the string representation of your object like it would an array.
In the case of Firebug, if it sees a length and a splice property, it will treat the object as array-like:
var MyArrayLike = function(){
this[0] = 1;
this.length = 1;
this.splice = function(){};
}
Firebug output:
-> new MyArrayLike();
<- [1]
Firebug represents (new Array(N)) as an array with N undefineds in it. I recently ran across a scenario that demonstrated that a sized array with all undefined values in it is different from a newly constructed, sized array. I'd like to understand the difference.
Suppose you want to generate a list of random integers between 0 and 1000.
function kilorange() {
return Math.floor(Math.random() * (1001));
}
no_random_numbers = (new Array(6)).map(kilorange);
my_random_numbers = [undefined, undefined, undefined,
undefined, undefined, undefined].map(kilorange);
I would have expected no_random_numbers and my_random_numbers to be equivalent, but they're not. no_random_numbers is another array of undefineds, whereas my_random_numbers is an array with six random integers in it. Furthermore, after throwing a console.count statement into kilorange, I learned that my function never gets called for the array created with the Array constructor.
What is the difference, and why does map (and presumably other iterable methods) not treat the above arrays the same?
The ES standard (15.4.4.19) defines the algorithm for map, and it's quite clear from step 8b that since your array doesn't actually have any of those elements, it will return an "empty" array with length 6.
As others have mentioned, it has to do with array objects in js being (unlike their rigid C counterparts) very dynamic, and potentially sparse (see 15.4 for the sparsity test algorithm).
When you use:
var a = new Array(N);
no values are stored in the new array and even the index "properties" are not created. That is why map won't do a thing on that array.
The fact that Firebug does that is a bug/feature of the Firebug. You should remember that it's console is an eval envelope. There are other bug/features in the Firebug console.
For example in Chrome console you'll see the above a array as [].
Look at this sample and run it: http://jsfiddle.net/ArtPD/1/ (it create the two arrays without using the map over them and then list the key/values of each one of them)
I would say that (new Array(6)) doesn't allocate "named" properties (so it doesn't create "1": undefined, "2": undefined...) while the other form [undefined, ... ] does.
In fact if I use a for (var i in ... ) the two outputs are:
no_random_numbers
my_random_numbers
0 undefined
1 undefined
2 undefined
3 undefined
4 undefined
5 undefined
Good question, good answers. I fiddled a bit with the map prototype from MDN. If it's adapted like this, map would work for a new Array([length])
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if ({}.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
T = thisArg;
}
A = new Array(len);
k = 0;
while(k < len) {
var kValue, mappedValue;
if (k in O || (O.length && !O[k])) {
// ^ added this
kValue = O[ k ];
mappedValue = callback.call(T, kValue, k, O);
A[ k ] = mappedValue;
}
k++;
}
return A;
};
Based on Casy Hopes answer you could also make a mapx-extension to be able to use map with some new Array([length]):
Array.prototype.mapx = function(callback){
return this.join(',').split(',').map(callback);
}
//usage
var no_random_numbers = new Array(6).mapx(kilorange);
To answer the title question (why presize arrays), the only use that I've come across for initializing an Array(n) array is to create an n-1 length string:
var x = Array(6).join('-'); // "-----"
To answer why you would presize an array, you'd do it so you don't have to do as many individual allocations. Allocating memory takes some time, and it might trigger a garbage collection run that can take a lot longer.
On a typical web page, you won't notice a difference, but if you're doing something major, it might help a fair bit.