오늘은 하지 못했던 코플릿들과 과제들을 정리하려고 한다.
중요한 개념들을 정리한다.
코언즈에 나오는 중요한 개념
mdn에 따르면 클로저의 정의는 다음과 같습니다. 반드시 기억하시기 바랍니다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.
클로저는 함수와 함수가 선언된 어휘적 환경의 조합을 말한다.
이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
여기서의 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"입니다.
특이하게도 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 합니다.
유어클레스 영상에서 언급되는 "외부함수의 변수에 접근할 수 있는 내부함수"를 클로져 함수로 부르는 이유도 그렇습니다.
클로저는 내부(inner) 함수가 외부(outer) 함수의 지역 변수에 접근할 수 있습니다.
이를 유념하시고 클로저의 유즈 케이스를 검색해 보시기 바랍니다. 아래 검색 키워드를 활용합니다.
function factories
namespacing private variables/functions
it('lexical scope와 closure에 대해 다시 확인합니다.', function () {
let age = 27;
let name = 'jin';
let height = 179;
function outerFn() {
let age = 24;
name = 'jimin';
let height = 178;
function innerFn() {
age = 26;
let name = 'suga';
return height;
}
innerFn();
expect(age).to.equal(26);
expect(name).to.equal('jimin');
return innerFn;
}
const innerFn = outerFn();
expect(age).to.equal(27);
expect(name).to.equal('jimin');
expect(innerFn()).to.equal(178);
});
});
it('Object의 속성(property)를 다루는 방법을 확인합니다.', function () {
const megalomaniac = { mastermind: 'Agent Smith', henchman: 'Agent Smith' };
expect('mastermind' in megalomaniac).to.equal(true);/// 이거는 뭐지??
megalomaniac.mastermind = 'Neo';
expect(megalomaniac['mastermind']).to.equal('Neo');
expect('secretary' in megalomaniac).to.equal(false);
megalomaniac.secretary = 'Agent Smith';
expect('secretary' in megalomaniac).to.equal(true);
delete megalomaniac.henchman;
expect('henchman' in megalomaniac).to.equal(undefined);
});
이거는 무슨 개념이지?
it('Object를 함수의 인자로 전달할 경우, reference가 전달됩니다.', function () {
const obj = {
mastermind: 'Joker',
henchwoman: 'Harley',
relations: ['Anarky', 'Duela Dent', 'Lucy'],
twins: {
'Jared Leto': 'Suicide Squad',
'Joaquin Phoenix': 'Joker',
'Heath Ledger': 'The Dark Knight',
'Jack Nicholson': 'Tim Burton Batman',
},
};
function passedByReference(refObj) {
refObj.henchwoman = 'Adam West';
}
passedByReference(obj);
expect(obj.henchwoman).to.equal('Adam West');
const assignedObj = obj;
assignedObj['relations'] = [1, 2, 3];
expect(obj['relations']).to.deep.equal([1,2,3]);
const copiedObj = Object.assign({}, obj);
copiedObj.mastermind = 'James Wood';
expect(obj.mastermind).to.equal('Joker');
->reference가 전달되어서 copiedObj는 카피본이다.
obj.henchwoman = 'Harley';
expect(copiedObj.henchwoman).to.equal('Adam West');
->왜냐하면 위에 passedByReference에서 obj를 변경해주었고.
-> copied는 121번째 줄에서 다시 만들어 주었고 obj는 원본이고 copiedObj는 복사본인데 이게 다른거니까. 위에서 변한 copied의 값을 가져온다.
delete obj.twins['Jared Leto'];
expect('Jared Leto' in copiedObj.twins).to.equal(false);
->함수 실행 위치를 잘 봐야한다. copiedObj는 delete보다 먼저 실행되었다.
///읭???
마지막 테스트 코드의 결과가 예상과는 달랐을 수도 있습니다.
'Object.assign'을 통한 복사는 reference variable은 주소만 복사하기 때문입니다.
이와 관련하여 얕은 복사(shallow copy)와 깊은 복사(deep copy)에 대해서 학습하시기 바랍니다.
가이드가 될 만한 학습자료를 첨부합니다.
https://scotch.io/bar-talk/copying-objects-in-javascript
https://medium.com/watcha/깊은-복사와-얕은-복사에-대한-심도있는-이야기-2f7d797e008a
});
});
이거 가 중요하다...
function copy(mainObj) {
let objCopy = {}; // objCopy will store a copy of the mainObj
let key;
for (key in mainObj) {
objCopy[key] = mainObj[key]; // copies each property to the objCopy object
}
return objCopy;
}
const mainObj = {
a: 2,
b: 5,
c: {
x: 7,
y: 4,
},
}
console.log(copy(mainObj));
let obj = {
a: 1,
b: {
c: 2,
},
}
let newObj = Object.assign({}, obj);
console.log(newObj); // { a: 1, b: { c: 2} }
obj.a = 10;
console.log(obj); // { a: 10, b: { c: 2} }
obj는 바뀌었다.
console.log(newObj); // { a: 1, b: { c: 2} }
newObj는 안바뀌었다.
newObj.a = 20;
console.log(obj); // { a: 10, b: { c: 2} }
obj는 안바뀌었다.
console.log(newObj); // { a: 20, b: { c: 2} }
newObj는 바뀌었다.
newObj.b.c = 30;
console.log(obj); // { a: 10, b: { c: 30} }
obj가 왜 바뀌지?
console.log(newObj); // { a: 20, b: { c: 30} }
newObj는 바뀌었다.
// Note: newObj.b.c = 30; Read why..
let obj = {
a: 1,
b: 2,
};
let objCopy = Object.assign({}, obj);
console.log(objCopy);
// Result - { a: 1, b: 2 }
let obj = {
a: 1,
b: 2,
};
let objCopy = Object.assign({}, obj);
console.log(objCopy); // result - { a: 1, b: 2 }
objCopy.b = 89;
console.log(objCopy); // result - { a: 1, b: 89 }
console.log(obj); // result - { a: 1, b: 2 }
it('여러 개의 객체를 병합할 수 있습니다.', function () {
const fullPre = {
cohort: 7,
duration: 4,
mentor: 'hongsik',
};
const me = {
time: '0156',
status: 'sleepy',
todos: ['coplit', 'koans'],
};
const merged = { ...fullPre, ...me };
변수 'merged'에 할당된 것은 'obj1'과 'obj2'의 value일까요, reference일까요?
만약 값(value, 데이터)이 복사된 것이라면, shallow copy일까요, deep copy일까요?
expect(merged).to.deep.equal({
cohort: 7,
duration: 4,
mentor: 'hongsik',
time: '0156',
status: 'sleepy',
todos: ['coplit', 'koans'],
});
});
변수 'merged'에 할당된 것은 'obj1'과 'obj2'의 value일까요, reference일까요?
만약 값(value, 데이터)이 복사된 것이라면, shallow copy일까요, deep copy일까요?
이거 어렵다.
function getAllParamsByRestParameter(...args) {
return args;
}
const restParams = getAllParamsByRestParameter('first', 'second', 'third');
expect(restParams).to.deep.equal(['first','second','third']); ////// ...args 를 했으니까 이게 배열이 되는거야
// 그러니까 restPrams는 ...args를 썼고 그걸 그대로 가져왔어 ...arg를 쓰니까. ['first','second','third']가 나왔어.
expect(Array.isArray(restParams)).to.deep.equal(true);//이거는 배열이라는거지
// arguments를 통해 '비슷하게' 함수의 인자들을 다룰 수 있습니다. (spread syntax 도입 이전)
// arguments는 모든 함수의 실행 시 자동으로 생성되는 '객체'입니다.
function getAllParamsByArgumentsObj() {
return arguments;
}
const argumentsObj = getAllParamsByArgumentsObj('first', 'second', 'third');
expect(Object.keys(argumentsObj)).to.deep.equal(['0','1','2']); /// 오 key가 string type으로 나오는구나
expect(Object.values(argumentsObj)).to.deep.equal(['first','second','third']);
/// arguments도 쓰니까 ['first','second','third'] 가 나왔어..
// arguments와 rest parmeter를 통해 배열로 된 인자(args)의 차이를 확인하시기 바랍니다.
expect(Array.isArray(argumentsObj)).to.deep.equal(false);
이거는 객체라는건가?
expect(restParams === argumentsObj).to.deep.equal(false);
expect(typeof restParams).to.deep.equal('object'); // 이거는 배열인데 배열의 상위 개념이니까 객체이고
expect(typeof argumentsObj).to.deep.equal('object'); // 이거는 그냥 객체 이고
const argsArr = Array.from(argumentsObj);
//이거는 뭐야?
expect(restParams === argumentsObj).to.deep.equal(false);
expect(typeof restParams).to.deep.equal('object'); // 이거는 배열인데 배열의 상위 개념이니까 객체이고
expect(typeof argumentsObj).to.deep.equal('object'); // 이거는 그냥 객체 이고
const argsArr = Array.from(argumentsObj);
//이거는 뭐야?
expect(Array.isArray(argsArr)).to.deep.equal(true);
expect(argsArr).to.deep.equal(['first','second','third']);
expect(argsArr === restParams).to.deep.equal(false); //다른 주소다.
요약:arguments가 배열로 만들어줌,
restParameter가 배열로 만들어줌.
Array.from()
Array.from() 메서드는 유사 배열 객체(array-like object)나반복 가능한 객체(iterable object)를 얕게 복사해새로운Array 객체를 만듭니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/from


map이랑 filter 차이 위에건 filter 아랫건 map
Array.from과 화살표 함수 사용하기
// Using an arrow function as the map function to
// manipulate the elements
Array.from([1, 2, 3], x => x + x);
// [2, 4, 6]
// Generate a sequence of numbers
// Since the array is initialized with `undefined` on each position,
// the value of `v` below will be `undefined`
Array.from({length: 5}, (v, i) => i);
// [0, 1, 2, 3, 4]
it('Rest Parameter는 인자의 수가 정해져 있지 않은 경우에도 유용하게 사용할 수 있습니다.', function () {
function sum(...nums) {
let sum = 0;
for (let i = 0; i < nums.length; i++) {
sum = sum + nums[i];
}
return sum;
}
expect(sum(1, 2, 3)).to.equal(6);
expect(sum(1, 2, 3, 4)).to.equal(10);
});