Why does slice not work directly on arguments? [duplicate] - javascript

This question already has answers here:
How to get a slice from "arguments"
(9 answers)
Closed 9 years ago.
Tested it out on this fiddle after looking at underscore.
This seems like a hack to call slice on arguments when it is not on the prototype chain.
Why is it not on the prototype chain when it obviously works on arguments.
var slice = Array.prototype.slice;
function test () {
return slice.call(arguments,1);
// return arguments.slice(1)
}
var foo = test(1,2,3,4);
_.each(foo, function(val){
console.log(val)
});

>>> Object.prototype.toString.call(arguments)
<<< "[object Arguments]"
>>> Array.isArray(arguments) //is not an array
<<< false
>>> arguments instanceof Array //does not inherit from the Array prototype either
<<< false
arguments is not an Array object, that is, it does not inherit from the Array prototype. However, it contains an array-like structure (numeric keys and a length property), thus Array.prototype.slice can be applied to it. This is called duck typing.
Oh and of course, Array.prototype.slice always returns an array, hence it can be used to convert array-like objects / collections to a new Array. (ref: MDN Array slice method - Array-like objects)

arguments is not a "real" array.
The arguments object is a local variable available within all
functions; arguments as a property of Function can no longer be used.
The arguments object is not an Array. It is similar to an Array, but
does not have any Array properties except length. For example, it does
not have the pop method. However it can be converted to a real Array.
You could do:
var args = Array.prototype.slice.call(arguments);

Arguments is not an Array. It's an Arguments object.
Fortunately, slice only requires an Array-like object, and since Arguments has length and numerically-indexed properties, slice.call(arguments) still works.
It is a hack, but it's safe everywhere.

Referring to MDN: »The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length. For example, it does not have the pop method. However it can be converted to a real Array:«
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments
In order to call slice, you have to get the slicefunction, from the Array-prototype.

Related

Question about delay function source code [duplicate]

I know it is used to make arguments a real Array, but I don‘t understand what happens when using Array.prototype.slice.call(arguments);.
What happens under the hood is that when .slice() is called normally, this is an Array, and then it just iterates over that Array, and does its work.
How is this in the .slice() function an Array? Because when you do:
object.method();
...the object automatically becomes the value of this in the method(). So with:
[1,2,3].slice()
...the [1,2,3] Array is set as the value of this in .slice().
But what if you could substitute something else as the this value? As long as whatever you substitute has a numeric .length property, and a bunch of properties that are numeric indices, it should work. This type of object is often called an array-like object.
The .call() and .apply() methods let you manually set the value of this in a function. So if we set the value of this in .slice() to an array-like object, .slice() will just assume it's working with an Array, and will do its thing.
Take this plain object as an example.
var my_object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
This is obviously not an Array, but if you can set it as the this value of .slice(), then it will just work, because it looks enough like an Array for .slice() to work properly.
var sliced = Array.prototype.slice.call( my_object, 3 );
Example: http://jsfiddle.net/wSvkv/
As you can see in the console, the result is what we expect:
['three','four'];
So this is what happens when you set an arguments object as the this value of .slice(). Because arguments has a .length property and a bunch of numeric indices, .slice() just goes about its work as if it were working on a real Array.
The arguments object is not actually an instance of an Array, and does not have any of the Array methods. So, arguments.slice(...) will not work because the arguments object does not have the slice method.
Arrays do have this method, and because the arguments object is very similar to an array, the two are compatible. This means that we can use array methods with the arguments object. And since array methods were built with arrays in mind, they will return arrays rather than other argument objects.
So why use Array.prototype? The Array is the object which we create new arrays from (new Array()), and these new arrays are passed methods and properties, like slice. These methods are stored in the [Class].prototype object. So, for efficiency sake, instead of accessing the slice method by (new Array()).slice.call() or [].slice.call(), we just get it straight from the prototype. This is so we don't have to initialise a new array.
But why do we have to do this in the first place? Well, as you said, it converts an arguments object into an Array instance. The reason why we use slice, however, is more of a "hack" than anything. The slice method will take a, you guessed it, slice of an array and return that slice as a new array. Passing no arguments to it (besides the arguments object as its context) causes the slice method to take a complete chunk of the passed "array" (in this case, the arguments object) and return it as a new array.
Normally, calling
var b = a.slice();
will copy the array a into b. However, we can’t do
var a = arguments.slice();
because arguments doesn’t have slice as a method (it’s not a real array).
Array.prototype.slice is the slice function for arrays. .call runs this slice function, with the this value set to arguments.
Array.prototype.slice.call(arguments) is the old-fashioned way to convert an arguments into an array.
In ECMAScript 2015, you can use Array.from or the spread operator:
let args = Array.from(arguments);
let args = [...arguments];
First, you should read how function invocation works in JavaScript. I suspect that alone is enough to answer your question. But here's a summary of what is happening:
Array.prototype.slice extracts the slice method from Array's prototype. But calling it directly won't work, as it's a method (not a function) and therefore requires a context (a calling object, this), otherwise it would throw Uncaught TypeError: Array.prototype.slice called on null or undefined.
The call() method allows you to specify a method's context, basically making these two calls equivalent:
someObject.slice(1, 2);
slice.call(someObject, 1, 2);
Except the former requires the slice method to exist in someObject's prototype chain (as it does for Array), whereas the latter allows the context (someObject) to be manually passed to the method.
Also, the latter is short for:
var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);
Which is the same as:
Array.prototype.slice.call(someObject, 1, 2);
// We can apply `slice` from `Array.prototype`:
Array.prototype.slice.call([]); //-> []
// Since `slice` is available on an array's prototype chain,
'slice' in []; //-> true
[].slice === Array.prototype.slice; //-> true
// … we can just invoke it directly:
[].slice(); //-> []
// `arguments` has no `slice` method
'slice' in arguments; //-> false
// … but we can apply it the same way:
Array.prototype.slice.call(arguments); //-> […]
// In fact, though `slice` belongs to `Array.prototype`,
// it can operate on any array-like object:
Array.prototype.slice.call({0: 1, length: 1}); //-> [1]
Its because, as MDN notes
The arguments object is not an array. It is similar to an array, but
does not have any array properties except length. For example, it does
not have the pop method. However it can be converted to a real array:
Here we are calling slice on the native object Array and not on its implementation and thats why the extra .prototype
var args = Array.prototype.slice.call(arguments);
Dont forget, that a low-level basics of this behaviour is the type-casting that integrated in JS-engine entirely.
Slice just takes object (thanks to existing arguments.length property) and returns array-object casted after doing all operations on that.
The same logics you can test if you try to treat String-method with an INT-value:
String.prototype.bold.call(11); // returns "<b>11</b>"
And that explains statement above.
Array.prototype.slice=function(start,end){
let res=[];
start=start||0;
end=end||this.length
for(let i=start;i<end;i++){
res.push(this[i])
}
return res;
}
when you do:
Array.prototype.slice.call(arguments)
arguments becomes the value of this in slice ,and then slice returns an array
It uses the slice method arrays have and calls it with its this being the arguments object. This means it calls it as if you did arguments.slice() assuming arguments had such a method.
Creating a slice without any arguments will simply take all elements - so it simply copies the elements from arguments to an array.
Let's assume you have: function.apply(thisArg, argArray )
The apply method invokes a function, passing in the object that will be bound to this
and an optional array of arguments.
The slice() method selects a part of an array, and returns the new array.
So when you call Array.prototype.slice.apply(arguments, [0]) the array slice method is invoked (bind) on arguments.
when .slice() is called normally, this is an Array, and then it just iterates over that Array, and does its work.
//ARGUMENTS
function func(){
console.log(arguments);//[1, 2, 3, 4]
//var arrArguments = arguments.slice();//Uncaught TypeError: undefined is not a function
var arrArguments = [].slice.call(arguments);//cp array with explicity THIS
arrArguments.push('new');
console.log(arrArguments)
}
func(1,2,3,4)//[1, 2, 3, 4, "new"]
Maybe a bit late, but the answer to all of this mess is that call() is used in JS for inheritance.
If we compare this to Python or PHP, for example, call is used respectively as super().init() or parent::_construct().
This is an example of its usage that clarifies all:
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Reference: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
/*
arguments: get all args data include Length .
slice : clone Array
call: Convert Object which include Length to Array
Array.prototype.slice.call(arguments):
1. Convert arguments to Array
2. Clone Array arguments
*/
//normal
function abc1(a,b,c){
console.log(a);
}
//argument
function: function abc2(){
console.log(Array.prototype.slice.call(arguments,0,1))
}
abc1('a','b','c');
//a
abc2('a','b','c');
//a

What does array.prototype.includes.call(x, y) do?

I am wondering what array.prototype.includes.call(x, y); does.
I figured out that includes() checks if an array contains the given value and returns true or false.
I also found out that call() will call this with optional parameters.
My problem is, that I don't get what happens here, if it's concatenated like it is here.
Here is the explanation (from Mozilla Developer):
includes() method is intentionally generic. It does not require this
value to be an Array object, so it can be applied to other kinds of
objects (e.g. array-like objects).
The example below illustrates includes() method called on the
function's arguments object.
(function() {
console.log(Array.prototype.includes.call(arguments, 'a')) // true
console.log(Array.prototype.includes.call(arguments, 'd')) // false
})('a','b','c')
Array-like objects have some similarities to an Array (like the property length), but do not have functions like map, slice etc. Another array-like object is HTMLCollection, which you can get from document.getElementsByTagName('div') in the console of your browser.
You compare the results from document.getElementsByTagName('div').__proto__ vs [].__proto and see getters and setters differ, but both offer length.

Does Object.keys(anObject) return anObject's prototype? [duplicate]

This question already has answers here:
Why does javascript's "in" operator return true when testing if 0 exists in an array that doesn't contain 0?
(6 answers)
Closed 4 years ago.
I'm reading Eloquent JavaScript's Map section and I'm having trouble understanding its last paragraph:
If you do have a plain object that you need to treat as a map for some reason, it is useful to know that Object.keys returns only an object’s own keys, not those in the prototype. As an alternative to the in operator, you can use the hasOwnProperty method, which ignores the object’s prototype.
I then assume that Object.keys does not return the properties and methods an object gets from the inheritance of its prototype. So I tried the following:
var anObject = {};
console.log(Object.keys(anObject)); //Array []
console.log("toString" in Object.keys(anObject)); //true
console.log(anObject.hasOwnProperty("toString")); //false
Apparently, toString is in the array returned by Object.keys(anObject), but an empty array is returned when I logged its keys? Did I understand the passage wrongly?
You understood the passage correctly, the usage of in here is wrong though.
It returns true, since the Array that Object.keys(anObject) returns has a property called toString in it's prototype. in also checks the prototype, while hasOwnProperty only checks the properties an Object actually has (meaning the properties that were "manually added" to the empty Object).
But it doesn't have an "own property" with the name toString
let anObject = {};
let keys = Object.keys(anObject);
console.log("toString" in keys); //true, since there is a property "toString" in the keys.prototype object
console.log(keys.hasOwnProperty("toString")); //false, since there aren't any properties in the object itself
Object.keys(anObject) returns an array, and toString is a valid method on arrays.
Since in checks for a property in the object, and in its prototype chain, the expression "toString" in Object.keys(anObject) returns true.
But this doesn't mean that toString is an owned property of anObject. That's why anObject.hasOwnProperty("toString") returns false.
The in operator returns true if the specified property is in the specified object or its prototype chain.
As we all know, arrays are special types of object in Javascript with methods/properties like forEach(), length, map(), toString(), sort() and more. These aforementioned methods are present in the prototype of the array and not in the array itself. Whereas, hasOwnProperty only checks the properties the Object actually has and not in its prototype. Read more about the in operator here. Also, use Object.prototype.hasOwnProperty.call(obj, prop) instead of obj.hasOwnProperty(prop). Read Why? here.

How JS 'arguments' object gets converted to array

Looking for more details on what exactly is happening when following is executed:
function useArray(){
var args = [].slice.call(arguments)
console.log(args)
}
How come slice being a function with 3 parameters (source array, start and end positions to copy) threats arguments correctly? Method call needs correct value of first argument as this but it seems arguments gets turned into Array object?
And why this doesn't work:
var args = [].slice(arguments)
We're using [].slice to get at the Array::slice method. arguments doesn't have a slice() of its own, but it is array-like.
Although it's a member of Array, slice() will work well enough on array-like objects -- things that have .length and can be indexed with [0..n].
slice() with no parameters returns a copy of the entire array. Its arguments are both optional.
So it's as if arguments had a slice() method, and we called arguments.slice() to get a copy of it (as an array).
[].slice(arguments) is just calling Array::slice() on an empty array, with arguments as the begin parameter, which makes no sense since begin (if present) should be a number.

arguments vs Array.prototype.slice.call(arguments,0)

What is the difference between using arguments and Array.prototype.slice.call(arguments,0) in a function?
I don't see there is any much difference between both, so How would I know when I am supposed to use which one?
function arr(){
return arguments; // or return Array.prototype.slice.call(arguments,0);
}
arr([1,2,3],[4,5,6]);
The difference is that arguments is an "array-like" object, not an array.
You can convert the arguments object to a real array by slicing it, like so
Array.prototype.slice.call(arguments, 0);
This gives you an array, with array properties like forEach, pop etc. which objects like arguments don't have (except length, which arguments do have).
It is generally (almost) never a good idea to slice the arguments object, MDN gives the warning
You should not slice on arguments because it prevents optimizations in
JavaScript engines (V8 for example). Instead, try constructing a new
array by iterating through the arguments object.
Also there should be no real need to pass arguments to a function, only to return them.
The arguments object is not a real array. It is a special type of object and does not have any Array properties except "length".
To make an array from the arguments object, use Array.prototype.slice.call(arguments, 0);
arguments variable is special kind of Object, and is not Array. So, you can't use .forEach, .map, '.push' and other array functions with it.
You have to convert arguments to Array and then you can work with value as array
function test(){
console.log(arguments.forEach); // undefined
var argsArray = Array.prototype.slice.call(arguments,0);
console.log(argsArray.forEach); // function
}
test();

Categories