function myFunc(theArr) {
theArr[0] = 30;
}
const arr = [45];
console.log(arr[0]); // 45
myFunc(arr);
console.log(arr[0]); // 30
const square = function (number) {
return number * number;
}
const x = square(4); // `x` 의 값은 16 입니다.
JS에서 변수와 함수를 선언하는 위치와는 상관없이 스코프(scope)의 최상위로 끌어올려지는 것
hoisting();
function hoisting() {
console.log("hi");
}
위 코드는 아래와 같이 해석된다.
function hoisting() {
console.log("hi");
}
hoisting();
위와 같이 변수나 함수 선언문에는 호이스팅이 발생한다.
함수 표현식에서는 함수의 이름만 호이스팅되고, 함수 표현식 자체는 끌어올리지 않는 것 유의하자.
추가로, let과 const를 이용하여 변수를 선언하면, 호이스팅이 발생하지 않아 스코프를 더욱 명확하게 관리할 수 있다.
describe("scope 대해서 학습합니다.", function () {
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";
};
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");
});
it("lexical scope에 대해서 확인합니다.", function () {
// message
let message = "Outer";
function getMessage() {
//
return message;
}
function shadowGlobal() {
// message
let message = "Inner";
return message;
}
function shadowGlobal2(message) {
// message
return message;
}
function shadowParameter(message) {
// message
// let message = 'Parameter';
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");
});
it("default parameter에 대해 확인합니다.", function () {
function defaultParameter(num = 5) {
return num;
}
expect(defaultParameter()).to.equal(5);
expect(defaultParameter(10)).to.equal(10);
function pushNum(num, arr = []) {
arr.push(num);
return arr;
}
expect(pushNum(10)).to.deep.equal([10]);
expect(pushNum(20)).to.deep.equal([20]);
expect(pushNum(4, [1, 2, 3])).to.deep.equal([1, 2, 3, 4]);
});
it("클로저(closure)에 대해 확인합니다.", function () {
function increaseBy(increaseByAmount) {
return function (numberToIncrease) {
return numberToIncrease + increaseByAmount;
};
}
const increaseBy3 = increaseBy(3);
const increaseBy5 = increaseBy(5);
expect(increaseBy3(10)).to.equal(13);
expect(increaseBy5(10)).to.equal(15);
expect(increaseBy(8)(6) + increaseBy(5)(9)).to.equal(28);
});
it("lexical scope와 closure에 대해 다시 확인합니다.", function () {
// let은 선언. 만약 해당하는 부분을 찾았다면 let을 가져오는게 맞고
// let이 없이 가져오라고 했다면, 상위 함수에서 let이 선언된 부분을 가져온다.
let age = 27;
let name = "jin";
let height = 179;
function outerFn() {
let age = 24; // outerFn의 지역 변수
name = "jimin"; // outerFn '상위' 함수의 지역 변수 <- 선언 기준, 바꾼 거 기준 아님.
let height = 178;
function innerFn() {
age = 26; // outerFn의 지역 변수
let name = "suga"; // innerFn의 지역 변수
return height; // outerFn의 지역 변수
}
innerFn();
expect(age).to.equal(26); // <- inner가 outer에서 선언한 age 를 바꿈
expect(name).to.equal("jimin"); // <- outer가 글로벌 name을 바꿈
return innerFn;
}
// innerFn(); 을 실행했다면 outerFn 안에 있는 거니까 undefinded 나옴
// 이거 innerFn은 위 innerFn 함수랑 다름, 이름만 비슷할 뿐
const innerFn = outerFn(); // outerFn 함수를 실행해서 innerFn을 반환받았다.
//const innerFn에 outerFn을 저장해야만 outerFn에서 실행된 innerFn을 사용할 수 있음
expect(age).to.equal(27); // innerFn이 age를 바꾼 것은 outerFn까지만 영향을 준다.
expect(name).to.equal("jimin");
expect(innerFn()).to.equal(178); // const innerFn = outerFn(); 저장이 되었기 때문에 실행 가능.
});
});
describe("화살표 함수에 관해서", function () {
it("함수 표현식 사용법을 복습합니다", function () {
const add = function (x, y) {
return x + y;
};
expect(add(5, 8)).to.eql(13);
});
it("화살표 함수 사용법을 익힙니다", function () {
const add = (x, y) => {
return x + y;
};
expect(add(10, 20)).to.eql(30);
// 리턴을 생략
const subtract = (x, y) => x - y;
expect(subtract(10, 20)).to.eql(-10);
// 소괄호 가능
const multiply = (x, y) => (x * y);
expect(multiply(10, 20)).to.eql(200);
// 파라미터가 하나일 경우 소괄호 생략이 가능
const divideBy10 = (x) => x / 10;
expect(divideBy10(100)).to.eql(10);
});
it("화살표 함수를 이용해 클로저를 표현합니다", function () {
const adder = (x) => {
return (y) => {
return x + y;
};
};
// adder(50) 함수를 실행시킨 결과(return된 함수)를 매개변수 10을 넣어서 실행시킨다.
expect(adder(50)(10)).to.eql(60);
const subtractor = (x) => (y) => x - y;
// const subtractor = function (x) {
// function a(y) {
// return x - y;
// }
// return a;
// };
// const asdg = subtractor(50)
// let last = asdg(10)
// last = subtractor(50)(10)
// expect(last).to.eql(40);
expect(subtractor(50)(10)).to.eql(40); // subtractor(50) 함수를 실행시킨 결과에 매개변수 10을 넣어서 실행.
// return되는 값은 x-y를 거친 50-10 = 10이다.
const htmlMaker = (tag) => (textContent) =>
`<${tag}>${textContent}</${tag}>`;
expect(htmlMaker("div")("code states")).to.eql(`<div>code states</div>`);
// liMaker 변수에 매개변수 "li"를 갖는 htmlMaker를 실행해서 리턴된 값을 저장한다.
// 형태 꼬릿말 참고
const liMaker = htmlMaker("li");
expect(liMaker("1st item")).to.eql(`<li>1st item</li>`); // 매개변수 "1st item" 을 넣어주고 리턴 값을 받는다.
expect(liMaker("2nd item")).to.eql(`<li>2nd item</li>`);
});
});
// 함수 표현식
// liMaker = function (textContent){
// return `<li>${textContent}</li>`;
// }