h e 1 1 o !
koans 본문
SCOPE - 호이스팅 / 함수선언식 & 함수표현식
호이스팅이란?
변수의 사용이 정의보다 앞서 등장하지만 동작하는 현상.
자바스크립트 및 액션스크립트 코드를 인터프리터가 로드할 때, 변수의 정의가 그 범위에 따라 선언과 할당으로 분리되어 변수, 함수의 선언을 항상 컨텍스트 내의 최상위로 끌어올려 처리하는 것을 의미한다.
JavaScript는 함수의 코드를 실행하기 전에 함수 선언에 대한 메모리부터 할당합니다. 덕분에 함수를 호출하는 코드를 함수 선언보다 앞서 배치할 수 있습니다. (그럼 왜 호이스팅 현상이 특이한거지? 더 자세한 내용은 나중에 공부하기로)
변수, 함수 선언 방법에 따라 호이스팅이 일어나는지가 달라진다.
모든 선언에는 사실 호이스팅이 일어난다. 하지만, let, const, class를 이용한 선언문은 호이스팅이 일어나지 않는 것처럼 보임.
호이스팅 일어나는 변수와 함수식
var, 함수선언식 -> 호이스팅
let, const, class, 함수표현식 -> 호이스팅 X
변수 먼저, 함수 다음
할당 된 변수 먼저, 할당 안된 변수 다음
문제
it('함수 선언식(declaration)과 함수 표현식(expression)의 차이를 확인합니다.', function () {
let funcExpressed = 'to be a function';
expect(typeof funcDeclared).to.equal('function');
expect(typeof funcExpressed).to.equal('string');
function funcDeclared() { //함수 선언식. 호이스팅이 일어나 위로.
return 'this is a function declaration';
}
funcExpressed = function () { //함수 표현식. 호이스팅이 일어나지 않아 순서대로 실행됨
return 'this is a function expression';
};
// 자바스크립트 함수 호이스팅(hoisting)에 대해서 검색해 봅니다.
const funcContainer = { func: funcExpressed };
expect(funcContainer.func()).to.equal('this is a function expression');
funcContainer.func = funcDeclared;
expect(funcContainer.func()).to.equal('this is a function declaration');
});
SCOPE - parameter
let message = 'Outer';
function getMessage() {
return message;
}
function shadowGlobal() {
let message = 'Inner';
return message;
}
function shadowGlobal2(message) {
return message;
}
function shadowParameter(message) {
message = 'Do not use parameters like this!';
return message;
}
expect(getMessage()).to.equal('Outer');
expect(shadowGlobal()).to.equal('Inner');
expect(shadowGlobal2('Parameter')).to.equal('Parameter');
expect(shadowParameter('Parameter')).to.equal('Do not use parameters like this!');
expect(message).to.equal('Outer');
});
SCOPE - closure
문제
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(---);
expect(innerFn()).to.equal(---);
});
'jimin' 178
화살표 함수와 클로저
화살표 함수
const add = (x, y) => {
return x + y
}
// 아래와 같음
const add2 = (x, y) => x + y
화살표 함수 클로저
const adder = x => {
return y => {
return x + y
}
}
// 아래와 같음
const adder2 = x => y => x + y
원시자료형, 참고자료형
it('원시 자료형을 변수에 할당할 경우, 값 자체의 복사가 일어납니다.', function () {
let overTwenty = true;
let allowedToDrink = overTwenty;
overTwenty = false;
expect(overTwenty).to.equal(false);
expect(allowedToDrink).to.equal(true);
let variable = 'variable';
let variableCopy = 'variableCopy';
variableCopy = variable;
variable = variableCopy;
expect(variable).to.equal('variable');
});
it('원시 자료형 또는 원시 자료형의 데이터를 함수의 전달인자로 전달할 경우, 값 자체의 복사가 일어납니다.', function () {
let currentYear = 2020;
function afterTenYears(year) {
year = year + 10; //currentyear가 변수로 전달되지만, 지역변수 year로 새롭게 선언된 것
}
afterTenYears(currentYear);
expect(currentYear).to.equal(---);
function afterTenYears2(currentYear) {
currentYear = currentYear + 10;
return currentYear;
}
let after10 = afterTenYears2(currentYear);
expect(currentYear).to.equal(---);
expect(after10).to.equal(---);
// 사실 함수의 전달인자도 변수에 자료(data)를 할당하는 것입니다.
// 함수를 호출하면서 넘긴 전달인자가 호출된 함수의 지역변수로 (매 호출 시마다) 새롭게 선언됩니다.
});
console.log(currentyear)
//2020 2020 2030 //2020
참조형 데이터 비교
const ages = [22, 23, 27];
allowedToDrink = ages;
expect(allowedToDrink === ages).to.equal(----);
expect(allowedToDrink === [22, 23, 27]).to.equal(---);
//true, false
const person = {
son: {
age: 9,
},
};
const boy = person.son;
boy.age = 20;
expect(person.son.age).to.equal(--);
expect(person.son === boy).to.equal(--);
expect(person.son === { age: 9 }).to.equal(--);
expect(person.son === { age: 20 }).to.equal(--);
20 true false falseddd
ARRAY
it('Array의 요소(element)를 다루는 방법을 확인합니다.', function () {
const arr = [];
expect(arr).to.deep.equal([]);
arr[0] = 1;
expect(arr).to.deep.equal([1]);
arr[1] = 2;
expect(arr).to.deep.equal([1, 2]);
arr.push(3);
expect(arr).to.deep.equal([1, 2, 3]);
const poppedValue = arr.pop(); // *
expect(poppedValue).to.equal(3); // **
expect(arr).to.deep.equal([1, 2]); // ***
});
*에서 기존의 arr도 pop이되고, 그 pop으로 삭제한 값을 poppedValue라는 새로운 변수에 할당함.
그러니 arr는 pop이 반영된 [1, 2]
it('Array 메소드 slice를 확인합니다.', function () {
const arr = ['peanut', 'butter', 'and', 'jelly'];
expect(arr.slice(1)).to.deep.equal(['butter', 'and', 'jelly']);
expect(arr.slice(0, 1)).to.deep.equal(['peanut']);
expect(arr.slice(0, 2)).to.deep.equal(['peanut', 'butter']);
expect(arr.slice(2, 2)).to.deep.equal();
expect(arr.slice(2, 20)).to.deep.equal();
expect(arr.slice(3, 0)).to.deep.equal();
expect(arr.slice(3, 100)).to.deep.equal();
expect(arr.slice(5, 1)).to.deep.equal();
// arr.slice는 arr의 값을 복사하여 새로운 배열을 리턴합니다.
// 아래의 코드는 arr 전체를 복사합니다. 자주 사용되니 기억하시기 바랍니다.
expect(arr.slice(0)).to.deep.equal(['peanut', 'butter', 'and', 'jelly']);
});
[], ['and', 'jelly'], [], ['jelly'], []
slice는 새로운 배열을 반환한다. slice(a,b)
- a>b -> 빈 배열
- a = b -> 빈 배열
- a>arr.length -> 빈배열
- b>arr.length -> a부터 다 나옴
Array를 함수의 전달인자로 전달할 경우, reference가 전달됩니다.
slice메서드로 배열을 할당하면 복사됨. 배열을 인자로 전달하고 재할당하면 원본도 바뀜
shift, unshift, pop 메서드는 mutable
it('Array 메소드 shift와 unshift를 확인합니다.', function () {
const arr = [1, 2];
arr.unshift(3);
expect(arr).to.deep.equal([3, 1, 2]);
const shiftedValue = arr.shift(); //pop처럼 삭제된 값이 담기고 원본이 변화
expect(shiftedValue).to.deep.equal(3);
expect(arr).to.deep.equal([1, 2]);
});
mutable Array method
- prototype. pop()
- prototype. push()
- prototype. shift()
- prototype. unshift()
- prototype. reverse()
- prototype. sort()
- prototype. splice()
객체의 길이는 undefined
const emptyObj = {};
emptyObj.length // undefined
const emptyarr = [];
emptyarr.length // 0
object property 다루는 방법. 'key' in objectname
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(false);
});
this에 관한 문제
이해하고 있으나 this에 대해 더 자세히 공부할 때 정리하기
value와 reference / shallow copy와 deep copy (추가하기)
shallow 카피는 레퍼런스가 아니다.
원시자료형은 밸류를 복사하고 참조자료형은 reference로 원본 변수, 함수를 참조한다.
얕은 복사와 깊은 복사는 참조자료형의 복사에
얕은 복사와 깊은 복사
얕은복사: 객체 안의 객체는 원본과 연동. assingn, spread syntax.
얕은 복사 - '객체 안의 객체' 기억하기
중첩된 객체를 복사 시, 가장 바깥 객체만 복사되어 내부 객체는 참조 관계가 유지.
깊은 복사~ 레퍼런스 중간 형식이다.
참조는 완전히 연동, 얕은 복사는 내부 객체가 연동됨.
https://fromnowwon.tistory.com/entry/object-reference-copy
it('Object를 함수의 전달인자로 전달할 경우, reference가 전달됩니다.', function () {
const obj = {
mastermind: 'Joker',
henchwoman: 'Adam West',
relations: [1, 2, 3],
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');
obj.henchwoman = 'Harley';
expect(copiedObj.henchwoman).to.equal('Adam West');
delete obj.twins['Jared Leto'];
expect('Jared Leto' in copiedObj.twins).to.equal(false);
/*
마지막 테스트 코드의 결과가 예상과는 달랐을 수도 있습니다.
'Object.assign'을 통한 복사는 reference variable은 주소만 복사하기 때문입니다.
이와 관련하여 얕은 복사(shallow copy)와 깊은 복사(deep copy)에 대해서 학습하시기 바랍니다.
가이드가 될 만한 학습자료를 첨부합니다.
https://scotch.io/bar-talk/copying-objects-in-javascript
https://medium.com/watcha/깊은-복사와-얕은-복사에-대한-심도있는-이야기-2f7d797e008a
*/
});
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'],
});
rest parameter
function getAllParamsByRestParameter(...args) { /
return args;
}
getAllParamsByArgumentsObj('first', 'second', 'third');
//['first', 'second', 'third'] 배열로 나옴
function getAllParamsByArgumentsObj() {
return arguments;
}
const argumentsObj = getAllParamsByArgumentsObj('first', 'second', 'third');
//['first', 'second', 'third', callee: ƒ, Symbol(Symbol.iterator): ƒ]
// object로 나옴
Array.from
유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕게 복사해 새로운Array 객체를 만듭니다.
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
function getAllParams(required1, required2, ...args) {
return [required1, required2, args];
}
expect(getAllParams(123)).to.deep.equal([ 123, undefined, [] ]);
rest parameter 자리가 비어 있으면 빈 배열이 나옴
구조분해할당 - 객체의 단축 문법
const name = '김코딩'
const age = 28
const person = {
name,
age,
level: 'Junior',
}
expect(person).to.eql({
name,
age,
level: 'Junior',
})
})
person // {name: '김코딩', age: 28, level: 'Junior'}
'p r o g r a m m i n g' 카테고리의 다른 글
고차함수 (0) | 2022.05.24 |
---|---|
section 1 기술면접 준비 (0) | 2022.05.23 |
DOM (0) | 2022.05.17 |
closure (0) | 2022.05.13 |
scope (1) | 2022.05.12 |