11

I have something here related to array destructuring that I don't fully understand.

In the following example:

function foo( [a, b, c] ) {
    console.log(a, b, c)
}

foo( 1, 2, 3 );

When I run this I get the following error:

Uncaught TypeError: undefined is not a function

Now, I am not questioning the fact that this doesn't output 1, 2, 3 as one might expect since only the first value 1 actually get destructured (a = 1[0], b = 1[1], c = 1[2]).

But here's the thing:

I can perfectly write 1[0], 1[1], 1[2] and I get undefined for each of those.

Then why the foo function I wrote above throws an exception instead of simply returning 3 times undefined as I'd expect.

Indeed, if I write bar as following, I am getting 3 undefined as should happen.

function bar() {
    console.log( 1[0], 1[1], 1[2] )
}

bar();
// undefined undefined undefined

Can someone tell me what JS does in the first foo() and why the output it's not undefined undefined undefined?

4
  • 2
    I've not seen destructuring done in this way; are you able to point to some documentation on how this kind of destructuring works? Commented Jul 13, 2018 at 13:41
  • @tommyO No. The syntax should be correct. Indeed, by passing foo( [1, 2, 3] ). It gets properly deconstructed and properly assigned. Commented Jul 13, 2018 at 13:46
  • 1
    @OliverRadini I couldn't find on MDN. Have a look at: 2ality.com/2015/01/es6-destructuring.html Commented Jul 13, 2018 at 13:47
  • The problem is that Number.prototype[Symbol.iterator] is undefined. Not that I recommend it but If you do Number.prototype[Symbol.iterator] = function*(){} it will work. Commented Jul 13, 2018 at 13:49

5 Answers 5

5

Destructuring with an array pattern uses iteration in the background, i.e. the value being destructured must be iterable.

In fact, in Firefox, the error message seems more indicative:

TypeError: (destructured parameter) is not iterable

That is where the comparison you make with evaluating 1[0], 1[1], 1[2] goes wrong: that does not need 1 to be iterable.

A more correct comparison would be to do this:

console.log([...1]);
// or:
const [a, b, c] = 1;

...and that code will fail.

Sign up to request clarification or add additional context in comments.

Comments

5

Array destructuring is actually Iterator Destructuring that works with anything implementing Symbol.iterator method.

For example

function foo([a, b, c]) {
  console.log([a, b, c])
}

foo({
  * [Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
})

Number doesn't implement iterator protocol

console.log(1[Symbol.iterator])

That's why you get the error.

But if you implement it (NOT RECOMMENDED)

Number.prototype[Symbol.iterator] = function*() {
  yield * this.toString(2); // just an example
}

function foo([a, b,c]) {
  console.log(a, b, c);
}

foo(6)

3 Comments

I think you somehow destructured the second word in your answer :-P
@trincot LOL :)
Thanks, @YuryTarabanko. (Now) Totally makes sense. :)
1

This happens because the function foo() can only accept iterables. See the below given example:

function foo( [a, b, c] ) {
    console.log(a, b, c)
}

foo( [4, 5, 6] );  // works okay

foo( 3,4,5 ); // undefined is not a function

IMHO, spread operator is used as a gatherer in these type of scenarios like this:

function foo( ...[a, b, c] ) {
    console.log(a, b, c)
}

foo( ...[4, 5, 'v'] );  //works fine

foo(1,3,4); // also works fine

Why foo() throws an exception?

That's because of incompatible parameters (b/w caller & calee) which has nothing to do with the fact that in JavaScript 1[0] is undefined.

2 Comments

"This happens because the function foo() can only accept array or destructured array" This is actually not true.
@leonardofed can it accept other values than array? (Updated my answer)
0

The function foo() is completely different with bar() due to the fact that 1 is a valid number in javascript and trying to access 1[0] is undefined as it looks for the index 0 of value 1 which is surely undefined. That is why you get three undefined for 1[0], 1[1], 1[2]

Now, the single undefined from foo() is not from console.log() but it is from the error

Uncaught TypeError: undefined is not a function

As your function signature is incorrect. To use it in proper way you can use spread syntax:

function foo(...arg) {
    console.log(arg[0], arg[1], arg[2]);
}

foo( 1, 2, 3 );

1 Comment

You didn't answer my question. The question is not, why does bar() output undefined, but why does foo() throw an exception. Please read carefully.
0

It's because function foo is expecting an array/string so he can destruct it.

Since you're not passing anything it causes the destruction to fail, this is like doing

var arrayVariable = undefined
var [a, b, c] = arrayVariable // this will throw an error
var d = arrayVariable['a'] // this will throw an error

So to avoid throwing an error, provide an array argument

foo('') // undefined, undefined, undefined
foo('123') // 1, 2, 3
foo([]); // undefined, undefined, undefined
foo([1, 2, 3]) // 1, 2, 3

6 Comments

" function foo is expecting an array " nope. Try foo('abc')
"It's because function foo is expecting an array/string (something that you can access with prop[value])" yet again nope. Try foo({})
Yeah, my bad. What I meant was something that you can access with property[value]
Yes but you obviosly can access empty object's props. :)
"It's because function foo is expecting an array/string so he can destruct it.": it does not necessarily have to be an array or string.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.