6
\$\begingroup\$

Provided Object.prototype hasn't been modified, will the following snippet work in major browsers?

var obj = {length:0};
var push = Array.prototype.push;
push.call(obj,'1st value')
push.call(obj,'2nd value');

Many have responded that the above code is bad practice, but what makes code like this any better?

Array.prototype.slice(arguments,0)

Both are calling Array.prototypes on non Arrays.

\$\endgroup\$

migrated from stackoverflow.com Jul 24 '11 at 10:05

This question came from our site for professional and enthusiast programmers.

  • 4
    \$\begingroup\$ Huh? What are you talking about? \$\endgroup\$ – Lightness Races in Orbit Jul 21 '11 at 18:48
  • 1
    \$\begingroup\$ Interesting to see code like that. It's bad code but it works \$\endgroup\$ – Raynos Jul 21 '11 at 18:49
  • \$\begingroup\$ Is it cross browser... \$\endgroup\$ – William Jul 21 '11 at 18:50
  • 2
    \$\begingroup\$ I hope you're bulletproof if someone else has to maintain that eventually. Edit: great, makes no sense now due to title change :p \$\endgroup\$ – Jonny Buchanan Jul 21 '11 at 18:51
  • 2
    \$\begingroup\$ @Raynos: at the time Tomalak posted his question, Lime hadn't yet said "is it cross browser". Look at the comment/answer timestamps before posting garbage like that. \$\endgroup\$ – Marc B Jul 21 '11 at 18:51
4
\$\begingroup\$

I'm going to say no, because this is an array. It works, but you are hammering a screw at this point. Simply use an array.

\$\endgroup\$
  • \$\begingroup\$ +1 Like the analogy... People often extend Array.prototype so I was looking for an alternative that allowed me to loop through the properties/methods safely along with having array support \$\endgroup\$ – William Jul 21 '11 at 18:50
  • \$\begingroup\$ But by making it an object, it allows you to add properties using the .property syntax, unlike arrays. \$\endgroup\$ – Jcubed Jul 21 '11 at 18:54
  • \$\begingroup\$ @Jcubed I think you meant if it was a function. Because adding properties to Object.prototype is absolutely forbidden. Well unless I misunderstood you... \$\endgroup\$ – William Jul 21 '11 at 18:56
1
\$\begingroup\$

I'm not going to go into the depths of implementation detail with this one; I'm just going to say that you're invoking a single Array function on an object that is not an Array.

It's like you're driving along the motorway in a box that may have any of: steering wheel, tyres, axle, floor, seats ... and you don't know which.

Depending upon how Array is implemented on any given browser, this may completely not work at all. Breaking the API in this manner is a silly idea.

So, no.

\$\endgroup\$
  • \$\begingroup\$ Exactly...I'm trying not to break the API, that's why I asked if this is cross browser. There are lots of things that probably shouldn't work but definitely do work cross browser. For example: 0 == '' // true 0 =='0' // true \$\endgroup\$ – William Jul 21 '11 at 19:04
  • \$\begingroup\$ @Lime: I believe that those examples are guaranteed to work by the Javascript standard. Messing about with Array's internals is not. You are breaking the API by the act of trying this; whether the resulting code "works" on your specific browser (or any other browser) is a different matter. \$\endgroup\$ – Lightness Races in Orbit Jul 21 '11 at 19:05
  • \$\begingroup\$ Yeah and all the ECMAScript guarantees are always followed by all browsers. (IE cough...) \$\endgroup\$ – William Jul 21 '11 at 19:07
  • \$\begingroup\$ I'm not really sure what your point is any more. I stand by my answer! \$\endgroup\$ – Lightness Races in Orbit Jul 21 '11 at 19:12
  • \$\begingroup\$ I totally agree that generally speaking it isn't a good idea,nor would I probably put this in code...but I've noticed Array.prototype.slice.push(obj,0) is often used on Objects like arguments for example. Is this also bad practice? Cause I'm kinda confused where the line is. \$\endgroup\$ – William Jul 21 '11 at 19:15
1
\$\begingroup\$

Question 1:

What's the different between Code A and Code B?

Code A:

var obj = {length:0};
var push = Array.prototype.push;
push.call(obj,'1st value')
push.call(obj,'2nd value');

Code B:

Array.prototype.slice(arguments,0)

Answer:

First off, Code B is wrong and should be the following.

Code B2:

function test(){
    var args = Array.prototype.slice.call(arguments,0);
    return args;
}

Unless defined, arguments exist within the scope of a function. Basically arguments is an object literal with the initial state of {length:0}. When a function is called with an argument, the argument is stored in the object arguments by the index corresponding to the argument position.

Example:

function getFirstArgument(){
    return arguments[0];
}
console.log( getFirstArgument(1,2,3) === 1 );

The purpose of Code B2 is to convert the arguments object into an array so you can get access to the array prototype functions easily.

Example:

// returns the arguments as an array in reverse order.
function reverseArguments(){
    return Array.prototype.slice.call(arguments).reverse();
}
console.log( reverseArguments(1,2,3).join(",") === "3,2,1" );

So as you can see, both obj in Code A and arguments in Code B are the same object initially. However, Code A is adding values to obj, while Code B2 is converting arguments to an array.

More information here:

What's the use of array.prototype.slice.call(arguments,0)

MDN Array.slice

Question 2:

Will "Code A" work in major browsers?

Answer: Most likely. However, if you want to guarantee that it works, then rewrite the push fuction.

var push = function( obj, val ){
    if( typeof obj !== "object" ){
        return;
    }
    obj.length = obj.length || 0;
    obj[ obj.length ] = val;
    return ++obj.length;
};

Usage:

var obj = {};
console.log( push( obj, 1 ) === 1 );
console.log( push( obj, 1 ) === 2 );
console.log( JSON.stringify( obj ) === "{"0":1,"1":1,"length":2}" );
\$\endgroup\$
0
\$\begingroup\$

Working with Javascript is like working with templates in C++. If it has what the function need it works. In this case length might not suffice for every browser. It is true that it is possible to use built-in functions like that but if that function tries to access a property which is not available in your object it will fail.

Now coming to your second point. AFAIK, ECMA script standard does not push any internal operation and even if it does browsers do not use them as hard rules, they take it as guidelines. So it is possible that your code will not work on every browser, but arguments object is implemented by the browser as well, to be compatible with its array architecture. Therefore using Array.prototype.slice.push(arg, 0); is relatively safer than using your custom object with built in array functions.

\$\endgroup\$
  • \$\begingroup\$ What makes arguments compatible, because it definitely doesn't inherit form 'array.prototype'? \$\endgroup\$ – William Jul 24 '11 at 22:20
  • \$\begingroup\$ It is supposed to be compatible, since it is basically an array of arguments, but as I said it is relatively safer. It is not a strongly typed language and you cannot be sure. \$\endgroup\$ – Cem Kalyoncu Jul 25 '11 at 0:23
-1
\$\begingroup\$

It will work without crashing. The exact result will be the object with properties "0", "1" and the "length"==2. There is the scope replacement becasue of the "call" function using. However it's not very good practice to use this solution becasue of bad readability and maintainability. I suggest to do it excplicitly.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.