Spread vs. Rest Syntax, Destructuring (D14)

devfish·2023년 1월 3일
0

Javascript

목록 보기
11/30

Key Points

  • spread vs. rest:
    • Spread syntax expands elements of an iterable, whereas rest syntax compresses them
    • Spread operator: useful for inputing multiple arguments into a function call (w/o manually listing them), combining arrays with other values and creating copies of an array (not just assigning a reference), shallow cloning of objects
    • Rest parameter: useful for declaring & defining functions with undefined number of arguments, and doing something to those arguments with an arbitrary ordinary parameter
  • rest vs. argument:
    • Array methods work only on rest
    • argument object is only available in non-arrow functions, and enables accessing and updating argument values via index.
  • dynamic vs. lexical scope
    • in lexical scope, what matters is where the function was defined, whereas in dynamic scope you need to look at where the function was called. javascript uses lexical scope!

Spread Syntax

  • "expands" an array into its elements (펼쳐줌)
myFunction(a, ...iterableObj, b) // function arguments 
[1, ...iterableObj, '4', 'five', 6] //array literals
{ ...obj, key: 'value' } //object literals
  • only an array can be 'spread' into an array (배열만 배열 안으로 펼쳐줄 수 있음)
const array = [1, 2, 3];
const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }

Function calls

function myFunction(x, y, z) {}
const args = [0, 1, 2];
myFunction(...args); //same as myFunction.apply(null, args)

function myFunction(v, w, x, y, z) {}
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

//apply for a new operator
const dateFields = [1970, 0, 1];  // 1 Jan 1970
const d = new Date(...dateFields); 

Array literals

  • can act like arr.slice() - does not affect the copied array
  • good for combining and copying arrays
  • only 1-level deep copy, unsuitable for copying multidimensional arrays
    • no native JS operation does a deep clone (same with Object.assign)
    • Web API structuredClone() allows deep copying of certain supported types
//powerful array literals
const parts = ['shoulders', 'knees'];
const lyrics = ['head', ...parts, 'and', 'toes'];

//copying arrays
const arr = [1, 2, 3];
const arr2 = [...arr]; // like arr.slice()

arr2.push(4); //arr2 becomes [1, 2, 3, 4], arr remains unaffected

//better than arr1.concat[arr2]
let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];

//unlike  Array.prototype.unshift.apply(arr1, arr2), creates a new arr1
let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1]; 
console.log(arr1); 

Object literals

  • good for shallow cloning (making a new object)
//Shallow cloning 
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };

const clonedObj = { ...obj1 }; // { foo: "bar", x: 42 }
const mergedObj = { ...obj1, ...obj2 }; // { foo: "baz", x: 42, y: 13 }

//Object.assign - can mutate an object
const obj1 = { foo: 'bar', x: 42 };
Object.assign(obj1, { x: 1337 });
console.log(obj1); // { foo: "bar", x: 1337 }


//re-implementing Object.assign
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const merge = (...objects) => objects.reduce((acc, cur) => ({ ...acc, ...cur }));

const mergedObj1 = merge(obj1, obj2); // { foo: 'baz', x: 42, y: 13 }

//what does not work
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const merge = (...objects) => ({ ...objects });

const mergedObj1 = merge(obj1, obj2); // { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }
const mergedObj2 = merge({}, obj1, obj2); // { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }

Rest Syntax

  • collects multiple elements and "condenses" them into a single element
  • the rest parameter must be the last parameter in the function definition!
function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}

sum(1,2,3) // 6
sum(1,2,3,4) // 10


function myFun(a, b, ...manyMoreArgs) {
  console.log("a", a); // a, one
  console.log("b", b); // a, one
  console.log("manyMoreArgs", manyMoreArgs); // manyMoreArgs, ["three", "four", "five", "six"]
}

myFun("one", "two", "three", "four", "five", "six");

Rest parameters vs argument object

  • arguments object is not a real array (more an array-like object), cannot use Array methods with it (map, sort, pop etc.)
  • arguments object includes all arguments passed to the function, whereas rest parameters are those, which are not given another name
  • each argument index can be set or re-assigned
  • non-strict functions with simple parameters (that is, no rest, default, or destructured parameters) will sync the new value of parameters with the arguments object
  • arguments.callee object can reference the currently executing function the arguments belong to. deprecated, forbidden in strict mode

argument object

  • an array-like object accessible inside functions that contains the values of the arguments passed to that function
  • useful for functions called with more arguments than they are formally declared to accept
function func1(a, b, c) {
  console.log(arguments[0]);// expected output: 1
  console.log(arguments[1]);// expected output: 2
  console.log(arguments[2]);// expected output: 3
  
  console.log(typeof arguments); //returns 'object'
}

func1(1, 2, 3);

//defining a function that concantenates several strings
function myConcat(separator) {
  const args = Array.prototype.slice.call(arguments, 1);
  return args.join(separator);
}

myConcat(', ', 'red', 'orange', 'blue'); // returns "red, orange, blue"
myConcat('; ', 'elephant', 'giraffe', 'lion', 'cheetah'); // returns "elephant; giraffe; lion; cheetah"
myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley'); // returns "sage. basil. oregano. pepper. parsley"

 //defining a function that creates HTML lists
function list(type) {
  let html = `<${type}l><li>`;
  const args = Array.prototype.slice.call(arguments, 1);
  html += args.join('</li><li>');
  html += `</li></${type}l>`; // end list
  return html;
}  
  
const listHTML = list('u', 'One', 'Two', 'Three');
/* listHTML is: "<ul><li>One</li><li>Two</li><li>Three</li></ul>" */

Combining with ordinary parameters

function multiply(multiplier, ...theArgs) {
  return theArgs.map((element) => multiplier * element);
}

const arr = multiply(2, 15, 25, 42);
console.log(arr); // [30, 50, 84]

From arguments to an array

  function sortRestArgs(...theArgs) {
  const sortedArgs = theArgs.sort();
  return sortedArgs;
}

console.log(sortRestArgs(5, 3, 7, 1)); // 1, 3, 5, 7

function sortArguments() {
  const sortedArgs = arguments.sort();
  return sortedArgs; // this will never happen
}

console.log(sortArguments(5, 3, 7, 1));
// throws a TypeError (arguments.sort is not a function)

//argument needs to be converted before using array methods
function fn(a, b) {
  const normalArray = Array.prototype.slice.call(arguments);
  // — or —
  const normalArray2 = [].slice.call(arguments);
  // — or —
  const normalArrayFrom = Array.from(arguments);

  const first = normalArray.shift(); // OK, gives the first argument
  const firstBad = arguments.shift(); // ERROR (arguments is not a normal array)
}
  
function fn(...args) {
  const normalArray = args;
  const first = normalArray.shift(); // OK, gives the first argument
}

Destructuring

  • unpacking values from arrays or properties from objects into distinct variables
  • destructure objects on the left-hand side of the assignment to define what values to unpack from the sourced variable

Array destructuring

const [a, b] = array;
const [a, , b] = array;
const [a = aDefault, b] = array;
const [a, b, ...rest] = array;
const [a, , b, ...rest] = array;
const [a, b, ...{ pop, push }] = array;
const [a, b, ...[c, d]] = array;

let a, b, a1, b1, c, d, rest, pop, push;
[a, b] = array;
[a, , b] = array;
[a = aDefault, b] = array;
[a, b, ...rest] = array;
[a, , b, ...rest] = array;
[a, b, ...{ pop, push }] = array;
[a, b, ...[c, d]] = array;

Object destructuring

const { a, b } = obj;
const { a: a1, b: b1 } = obj;
const { a: a1 = aDefault, b = bDefault } = obj;
const { a, b, ...rest } = obj;
const { a: a1, b: b1, ...rest } = obj;
const { [key]: a } = obj;

({ a, b } = obj); // brackets are required
({ a: a1, b: b1 } = obj);
({ a: a1 = aDefault, b = bDefault } = obj);
({ a, b, ...rest } = obj);
({ a: a1, b: b1, ...rest } = obj);
profile
la, di, lah

0개의 댓글