호기심으로 시작한 Frontend 꾸준함으로 채워나가는 Developer

[JavaScript] Koans 본문

Codestates 부트캠프/Section01 - TIL

[JavaScript] Koans

RachelLee 2022. 7. 13. 22:28

Koans

JS 02. Types-part1

expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.

it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect(1 + '1').to.equal('11');
  });

it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect(123 - '1').to.equal(122);
  });

 it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect(1 + true).to.equal(**2**);
  });
  **//true= 숫자로 1, false= 숫자로 0**

 it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () {
    expect('1' + true).to.equal('1true');
  });

JS 03. LetConst

it("'const'로 선언된 객체의 경우, 속성을 추가하거나 삭제할 수 있습니다.", function () {
    const obj = { x: 1 };
    expect(obj.x).to.equal(1);

    delete obj.x;
    expect(obj.x).to.equal(**undefined**);

    // 여전히 재할당은 금지됩니다.
    // obj = { x: 123 };

    obj.occupation = 'SW Engineer';
    expect(obj['occupation']).to.equal('SW Engineer');
  });

JS 04. Scope

함수 호이스팅 (hoisting)

it('lexical scope에 대해서 확인합니다.', function () {
    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"**);
  });

클로저(closure)

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);
});

<aside> ✔️ lexical scopeclosure에 대해 다시 확인하기!!

</aside>

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)
  });
});

JS 06. Types-part2

const ages = [22, 23, 27];
    allowedToDrink = ages;
    expect(allowedToDrink === ages).to.equal(true);
    expect(allowedToDrink === [22, 23, 27]).to.equal(**false**);

    const nums1 = [1, 2, 3];
    const nums2 = [1, 2, 3];
    expect(nums1 === nums2).to.equal(**false**);

/* 
    배열 nums1과 배열 num2에는 동일한 데이터 [1, 2, 3]이 들어있는 게 분명해 보이는데, 이 둘은 같지가 않습니다.
    사실 변수 num1와 num2는 배열이 아닙니다. 
    참조 타입의 변수에는 (데이터에 대한) 주소만이 저장된다는 것을 떠올려 봅시다.

    정확히 말해서 변수 num1은 데이터 [1, 2, 3]이 저장되어 있는 메모리 공간(heap)을 가리키는 주소를 담고 있습니다.
    따라서 위의 코드는 각각 다음의 의미를 가지고 있습니다.
    
      const nums1 = [1, 2, 3]; // [1, 2, 3]이 heap에 저장되고, 이 위치의 주소가 변수 num1에 저장된다.
      const nums2 = [1, 2, 3]; // [1, 2, 3]이 heap에 저장되고, 이 위치의 주소가 변수 num2에 저장된다.
    이제 heap에는 두 개의 [1, 2, 3]이 저장되어 있고, 각각에 대한 주소가 변수 num1, num2에 저장되어 있습니다.
    이게 비효율적으로 보일수도 있습니다. 굳이 같은 데이터를 왜 한번 더 저장하는 지 이해하기란 쉽지 않습니다. */

JS 07. Array

it('Array의 기본을 확인합니다.', function () {
    const emptyArr = [];
    expect(typeof emptyArr === 'array').to.equal(false);
    expect(emptyArr.length).to.equal(0);
it('Array 메소드 slice를 확인합니다.', function () {
    const arr = ['peanut', 'butter', 'and', 'jelly'];

		expect(arr.slice(2, 2)).to.deep.equal([]);
    expect(arr.slice(2, 20)).to.deep.equal(['and', 'jelly']);
    expect(arr.slice(3, 0)).to.deep.equal([]);
    expect(arr.slice(3, 100)).to.deep.equal(['jelly']);
    expect(arr.slice(5, 1)).to.deep.equal([]);
		
		expect(arr.slice(0)).to.deep.equal(['peanut', 'butter', 'and', 'jelly']);
  });
it('Array를 함수의 전달인자로 전달할 경우, reference가 전달됩니다.', function () {
    // call(pass) by value와 call(pass) by reference의 차이에 대해서 학습합니다.
    const arr = ['zero', 'one', 'two', 'three', 'four', 'five'];

		const copiedArr = arr.slice();
    copiedArr[3] = 'changed in copiedArr';
    expect(arr[3]).to.equal( 'three', 'four', 'five');
  });

JS 08. Object

it('Object의 기본을 확인합니다.', function () {
    const emptyObj = {};
    expect(typeof emptyObj === 'object').to.equal(**true**);
    expect(emptyObj.length).to.equal(**undefined)**;
const megalomaniac = {
      mastermind: 'Joker',
      henchwoman: 'Harley',
      getMembers: function () {
        return [this.mastermind, this.henchwoman];
      },
      relations: ['Anarky', 'Duela Dent', 'Lucy'],
}
expect(megalomaniac.length).to.equal(**undefined**);

/* 보통의 배열의 경우 .length 를 사용하여 길이값을 구할 수 있다.

하지만 객체의 경우 Object.keys를 사용하여 구할 수 있다.

Object.keys([객체명].values).length /*
it("'this'는 method를 호출하는 시점에 결정됩니다.", function () {
    const currentYear = new Date().getFullYear();
    const megalomaniac = {
      mastermind: 'James Wood',
      henchman: 'Adam West',
      birthYear: 1970,
      calculateAge: function (currentYear) {
        return currentYear - this.birthYear;
      },
      changeBirthYear: function (newYear) {
        this.birthYear = newYear;
      },
    };

    expect(currentYear).to.equal(2022);
    expect(megalomaniac.calculateAge(currentYear)).to.equal(52);

    megalomaniac.birthYear = 2000;
    expect(megalomaniac.calculateAge(currentYear)).to.equal(22);

    megalomaniac.changeBirthYear(2010);
    expect(megalomaniac.calculateAge(currentYear)).to.equal(12);
});
더보기
* !!Advanced [this.mastermind]? this.birthYear? this가 무엇일까요?
   * 
   * method는 '어떤 객체의 속성으로 정의된 함수'를 말합니다. 위의 megalomaniac 객체를 예로 든다면,
   * getMembers는 megalomaniac 객체의 속성으로 정의된 함수인 '메소드'라고 할 수 있습니다. megalomaniac.getMembers()와 같은 형태로 사용(호출)할 수 있죠.
   * 사실은, 전역 변수에 선언한 함수도 웹페이지에서 window 객체의 속성으로 정의된 함수라고 할 수 있습니다. 
   * window. 접두사 없이도 참조가 가능하기 때문에(window.foo()라고 사용해도 됩니다.), 생략하고 쓰는 것뿐입니다. 이렇듯, method는 항상 '어떤 객체'의 method입니다.
   * 따라서 호출될 때마다 어떠한 객체의 method일 텐데, 그 '어떠한 객체'를 묻는 것이 this입니다.
   * 예시로, obj이라는 객체 안에 foo라는 메서드를 선언하고, this를 반환한다고 했을 때 ( 예: let obj = {foo: function() {return this}}; )
   * obj.foo() === obj 이라는 코드에 true라고 반환할 것입니다.
   * this는 함수의 호출에 따라서 값이 달라지기도 합니다. (apply나 call, bind에 대해서는 하단의 학습자료를 통해 더 공부해 보세요.)
   * 
   * 그러나 화살표 함수는 다릅니다. 자신의 this가 없습니다.
   * 화살표 함수에서의 this는 자신을 감싼 정적 범위(lexical context)입니다. (전역에서는 전역 객체를 가리킵니다.)
   * 일반 변수 조회 규칙(normal variable lookup rules)을 따르기 때문에, 현재 범위에서 존재하지 않는 this를 찾을 때, 화살표 함수 바로 바깥 범위에서 this를 찾습니다.
   * 그렇기에 화살표 함수를 사용할 때, 이러한 특이점을 생각하고 사용해야 합니다.

   */