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)
[1, ...iterableObj, '4', 'five', 6]
{ ...obj, key: 'value' }
- only an array can be 'spread' into an array (배열만 배열 안으로 펼쳐줄 수 있음)
const array = [1, 2, 3];
const obj = { ...array };
Function calls
function myFunction(x, y, z) {}
const args = [0, 1, 2];
myFunction(...args);
function myFunction(v, w, x, y, z) {}
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
const dateFields = [1970, 0, 1];
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
const parts = ['shoulders', 'knees'];
const lyrics = ['head', ...parts, 'and', 'toes'];
const arr = [1, 2, 3];
const arr2 = [...arr];
arr2.push(4);
let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
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)
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const clonedObj = { ...obj1 };
const mergedObj = { ...obj1, ...obj2 };
const obj1 = { foo: 'bar', x: 42 };
Object.assign(obj1, { x: 1337 });
console.log(obj1);
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);
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const merge = (...objects) => ({ ...objects });
const mergedObj1 = merge(obj1, obj2);
const mergedObj2 = merge({}, obj1, obj2);
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)
sum(1,2,3,4)
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a);
console.log("b", b);
console.log("manyMoreArgs", manyMoreArgs);
}
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]);
console.log(arguments[1]);
console.log(arguments[2]);
console.log(typeof arguments);
}
func1(1, 2, 3);
function myConcat(separator) {
const args = Array.prototype.slice.call(arguments, 1);
return args.join(separator);
}
myConcat(', ', 'red', 'orange', 'blue');
myConcat('; ', 'elephant', 'giraffe', 'lion', 'cheetah');
myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley');
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>`;
return html;
}
const listHTML = list('u', 'One', 'Two', 'Three');
Combining with ordinary parameters
function multiply(multiplier, ...theArgs) {
return theArgs.map((element) => multiplier * element);
}
const arr = multiply(2, 15, 25, 42);
console.log(arr);
From arguments to an array
function sortRestArgs(...theArgs) {
const sortedArgs = theArgs.sort();
return sortedArgs;
}
console.log(sortRestArgs(5, 3, 7, 1));
function sortArguments() {
const sortedArgs = arguments.sort();
return sortedArgs;
}
console.log(sortArguments(5, 3, 7, 1));
function fn(a, b) {
const normalArray = Array.prototype.slice.call(arguments);
const normalArray2 = [].slice.call(arguments);
const normalArrayFrom = Array.from(arguments);
const first = normalArray.shift();
const firstBad = arguments.shift();
}
function fn(...args) {
const normalArray = args;
const first = normalArray.shift();
}
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);
({ a: a1, b: b1 } = obj);
({ a: a1 = aDefault, b = bDefault } = obj);
({ a, b, ...rest } = obj);
({ a: a1, b: b1, ...rest } = obj);