2

Given the below JSBIN why A is undefined?

https://jsbin.com/bisomevivu/edit?html,js,console,output

HTML:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <input data-custom="CUST" data-standard="STD" id="input">
  <button id="button">Test</test>
</body>
</html>

JS:

const [button, input] = ["button", "input"].map(id=> document.getElementById(id));

button.addEventListener("click", ()=>{
  
  const {dataset: {custom}, ...props} = input;
  const {value: A} = props;
  const {dataset: {standard}, value: B} = input;
  
  
  console.info(`A: '${A}' - B: '${B}'`);
  
});

The regular destructuring works, the rest one doesn't

4
  • 3
    Note that <button> is terminated by </button>, not by </test>. Also remember that you have runnable snippets available: no need for a jsbin lnk, just edit your post and add it as runnable here on SO (without the html/head bits, those are not necessary). Commented Jul 29, 2024 at 17:39
  • 2
    value is not an own property. Hence why ...rest doesn't capture it. Commented Jul 29, 2024 at 17:43
  • Your question is unclear. What are you expecting the value of A and B to be? It looks like perhaps A is supposed to hold the value of the input element, and B is supposed to also hold the value of the input, but just obtained in a slightly less convoluted manner. What are you trying to do? Commented Jul 29, 2024 at 17:46
  • 1
    @VLAZ Please make that an answer Commented Jul 29, 2024 at 17:51

1 Answer 1

2

The rest property in a destructuring assignment will only collect own properties. Not ones from the prototype.

However, named destructuring will access any value, even from the prototype

Simple example:

const base = {};
base.x = "xerophyte";
base.y = "yumberry";
base.z = "zucchini";

const derived = Object.create(base);
derived.a = "apple";
derived.b = "banana";
derived.c = "cherry";

console.log(
  '"a" in derived             :', 
  "a" in derived);              //true
console.log(
  'derived.hasOwnProperty("a"):',
  derived.hasOwnProperty("a"));


console.log(
  '"x" in derived             :', 
  "x" in derived);             //true
console.log(
  'derived.hasOwnProperty("x"):',
  derived.hasOwnProperty("x")); //false
  
const {...rest} = derived;
console.log(
  "x" in rest, //false
  "y" in rest, //false
  "z" in rest, //false
  rest,        // { "a": "apple", "b": "banana", "c": "cherry" }
);

const {a, x} = derived;

console.log(a, x); //apple xerophyte
.as-console-wrapper { max-height: 100% !important; }

With DOM nodes, value is an inherited property for inputs:

const input = document.querySelector("#example");

console.log(
  '"value" in input             :', 
  "value" in input);              //true
console.log(
  'input.hasOwnProperty("value"):',
  input.hasOwnProperty("value")); //false
  
const {...rest} = input;
console.log("value" in input); //false
<input type="text" id="example" value="hello"/>

Therefore, when restructuring with the rest property, the value attribute is not collected. But it is available when directly accessed as named property const {value} = input;

See more about destructuring assignment on MDN

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

5 Comments

interesting thanks.
this is a design flaw btw as it forces people to know more about the object than they really should...
I know I can gather up all non-owned props by map(x=>{}) and then freely use rest or I have to pass all non-owned props individually. This is a deal breaker for the extensibility scenarios where props are unknown in advance.
@vladimir I don't see the problem. Just don't use rest syntax for objects you don't control? In particular for DOM elements, you don't want to create a copy anyway, you want to reference the original live object.
the problem is having an object as {a, b, c... z} you can't safely assume that {a, b, ...rest}'s rest would accurately represent the rest of the object. rest owned props is what it is. I have a component which has 'componentType' non-owned attribute (which is how I caught this issue) - so I have to give that values an expedited handling otherwise it won't make to the point where it's needed. The whole value of rest is to be able to segregate the values between "needed for this call" and "needed later", I'm sure you know what I'm, taking about.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.