notion-035

😉 헷갈렸던 & 몰랐던 부분들만 정리하는 나만의 TIL
😯 모든 강의 내용은 적지 않아요!

오늘의 소감은?
코드의 잘못된 부분을 찾고 어떻게 해결할지 적으라는 문제 외에는 쉬웠습니다.
서술형 1번이었나..?

클로져의 개념에 대해서 한번 더 복습하는 좋은 시간이었습니다.
자..이제 선택 강의를 들으러 가 볼까요..




[1] JS 사전 문제 해설

한번 더 짚고 넘어가야 할 내용 위주로 정리해볼게요!


[1-1] 1번

function Cat(name, age) {
  this.name = name;
  this.age = age;
}

const haeyum = Cat("haeyum", 5);
console.log(haeyum.name);

이 코드를 보자마자 아 new가 빠졌다를 알 수 있었습니다.

정확한 원리는 함수가 new가 없이 실행이 되면, 위의 함수의 thiswindow를 가리키게 됩니다.

따라서 오류가 뜸을 알 수 있습니다.


[1-2] 2번

(function (name) {
  console.log(`hi ${name}`);
})("Jiyoung");

누가 봐도 즉시 실행함수이며, 선언 함과 동시에 실행됩니다. 따라서 hi Jiyoung이 출력됩니다.


[1-3] 3번

var idiots = {
  name: "idiots",
  genre: "jazz",
  members: {
    Jiyoung: {
      memberName: "Jiyoung",
      play: function () {
        console.log(`band ${this.name} ${this.memberName} play start`);
      },
    },
  },
};

idiots.members.Jiyoung.play();

일단 idiotsmembers객체에 접근 후 Jiyoung객체를 기준으로 봐야 합니다.

play() 함수 내 this.nameJiyoung내 정의되어 있지 않으니, undefined를 반환합니다.

따라서 band undefined Jiyoung play start가 출력됩니다.


[1-4] 4번

이 문제를 풀지 못했습니다!!!!!!

왜 틀렸는지는 알았지만, 어떻게 고쳐야할지 감이 오지 않았습니다.

function Band(members) {
  this.members = members;
  this.perform = function () {
    setTimeout(function () {
      this.members.forEach(function (member) {
        member.perform();
      });
    }, 1000); // 1000은 1초
  };
}

var theOralCigarettes = new Band([
  {
    name: "Jiyoung",
    perform: function () {
      console.log("Sing: a e u i a e u i");
    },
  },
]);

theOralCigarettes.perform();

setTimeout ~~ ,1000⇒ 1초 뒤에 ~~ 를 실행해라


  • 오류 발생 원인

JS에서는 항상 function scope에 유의를 해야합니다.

setTimeout안에 있는 function의 thisBand의 this를 가리키지 않습니다.
setTimeout안에서 정의된 function 내 scope를 가리키게 됩니다.

따라서 this.membersundefined이므로 오류가 발생하게 됩니다.
그럼 어떻게 고칠 수 있을까요?


  • 해결 방법

(1) arrow function

화살표 함수는 화살표 함수 자체로 function scope를 만들지 않습니다.
해당 화살표 함수의 상위 함수의 scope를 가지게 됩니다.

따라서 thisBand의 this를 가리키게 됩니다.

function Band(members) {
  this.members = members;
  this.perform = function () {
    setTimeout(() => {
      this.members.forEach(function (member) {
        member.perform();
      });
    }, 1000);
  };
}


(2) bind 사용

bind는 쉽게 말해 함수를 만드는 함수입니다.

bind를 붙인 곳은 setTimeout내 function 밖이기 때문에, Band의 this에 접근할 수 있습니다.

따라서 bind는 Band의 this를 setTimeout내 function에 넘겨주는 함수를 생성하게 됩니다.

function Band(members) {
  this.members = members;
  this.perform = function () {
    setTimeout(
      function () {
        this.members.forEach(function (member) {
          member.perform();
        });
      }.bind(this),
      1000
    );
  };
}


(3) 클로저 사용

Band의 thisthat의 변수에 넣었습니다.

따라서 setTimeout 내 function에서 that에 접근하여 this를 사용할 수 있습니다.

function Band(members) {
  var that = this;
  this.members = members;
  this.perform = function () {
    setTimeout(function () {
      that.members.forEach(function (member) {
        member.perform();
      });
    }, 1000);
  };
}


[1-5] 5번

const nums = [0, 1, 2, 3, 4];

for (var i = 0; i < nums.length; i += 1) {
  setTimeout(function () {
    console.log(`[${i}] number ${nums[i]} turn!`);
  }, i * 1000);
}

이미 i는 다 증가해 5이므로 [5] number undefined turn!이 5번 뜨게 됩니다.


  • 해결 방법

(1) IIFE (즉시 실행 함수)

i가 0, 1, 2, 3, 4일 때를 각각의 function scope로 가두어서 처리합니다.

따라서 setTimeout 실행 시점에 참고하는 idxIIFE에서 인자로 넘긴 i값을 쓰게 됩니다.

for (var i = 0; i < nums.length; i += 1) {
  (function (idx) {
    setTimeout(function () {
      console.log(`[${idx}] number ${nums[idx]} turn!`);
    }, i * 1000);
  })(i);
}


(2) var 대신 let 사용

let으로 선언할 경우, setTimeout 내에서 let i가 0일때, 1일때 각각 참조됩니다.

즉 각각의 scope가 생성된다는 것이 IIFE와 유사하게 동작합니다.

for (let i = 0; i < nums.length; i += 1) {
  setTimeout(function () {
    console.log(`[${i}] number ${nums[i]} turn!`);
  }, i * 1000);
}


(3) forEach 사용

forEach같은 경우는 배열 요소를 하나씩 꺼내서 반복하는 함수입니다.

forEachnums를 순회하면서 각각의 function을 만들기 때문에 idx의 값이 고유해집니다.

nums.forEach(function (num, idx) {
  setTimeout(() => {
    console.log(`[${idx}] number ${num} turn!`);
  }, idx * 1000);
});


[1-6] var, let, const

  • var는 function scope - 실행 시 맨 위로 var 선언이 끌어올려지기 때문에 호이스팅 발생
  • let, const는 block scope

  • var와 let은 변수 재할당이 가능합니다.
  • const는 변수 재할당이 불가능합니다.


[1-7] 클로져

outterFunc()를 잘 살펴봅시다.

outterFunc()가 실행 후, name 변수는 소멸해야 하나 console.log(name);이 잘 동작합니다.

function outterFunc() {
  const name = "Jiyoung";

  return function () {
    console.log(name); // function 외부에 있는 name을 참고하고 있습니다.
  };
}

const logName = outterFunc(); // logName은 클로져!
logName(); // Jiyoung

이렇게 만들어진 것을 클로져라고 합니다.

클로져는 함수, 함수가 선언된 어휘적 환경의 조합이라고도 합니다.


클로져로 private 효과를 줄 수 있습니다.

function outterFunc() {
  const name = "Jiyoung";
  let count = 0;

  function plus() {
    count += 1;
    // 각각의 함수 내부에서는 외부의 값인 count에 접근하고 있습니다.
  }

  function minus() {
    count -= 1;
  }

  function printName() {
    console.log(`name : ${name}`);
  }

  function printCount() {
    console.log(`count : ${count}`);
  }

  return {
    plus,
    minus,
    printName,
    printCount,
  };
}

const outter = outterFunc();
outter.plus();
outter.plus();
outter.printCount(); // 2
outter.minus();
outter.printCount(); // 1
outter.printName(); // Jiyoung
console.log(outterFunc.count); // undefined

해당 함수를 보면, return한 함수에는 접근하여 count를 조작할 수 있지만 count에는 직접 접근하지 못합니다.


출처

프로그래머스

댓글남기기