From 6103cdec4df4e5577733a726108ddd4a1c416607 Mon Sep 17 00:00:00 2001 From: Hong-min1010 Date: Tue, 19 Nov 2024 10:20:31 +0900 Subject: [PATCH] Add files via upload --- 01_Introduction.js | 80 +++++++++++++++++ 02_Types-part1.js | 67 +++++++++++++++ 03_LetConst.js | 44 ++++++++++ 04_Scope.js | 140 ++++++++++++++++++++++++++++++ 05_ArrowFunction.js | 52 ++++++++++++ 06_Types-part2.js | 203 ++++++++++++++++++++++++++++++++++++++++++++ 07_Array.js | 94 ++++++++++++++++++++ 08_Object.js | 156 ++++++++++++++++++++++++++++++++++ 09_SpreadSyntax.js | 122 ++++++++++++++++++++++++++ 10_Destructuring.js | 137 ++++++++++++++++++++++++++++++ 10 files changed, 1095 insertions(+) create mode 100644 01_Introduction.js create mode 100644 02_Types-part1.js create mode 100644 03_LetConst.js create mode 100644 04_Scope.js create mode 100644 05_ArrowFunction.js create mode 100644 06_Types-part2.js create mode 100644 07_Array.js create mode 100644 08_Object.js create mode 100644 09_SpreadSyntax.js create mode 100644 10_Destructuring.js diff --git a/01_Introduction.js b/01_Introduction.js new file mode 100644 index 0000000..99947ee --- /dev/null +++ b/01_Introduction.js @@ -0,0 +1,80 @@ +describe('expect에 대해서 학습합니다.', function () { + /* + 아래 코드는 여러분이 작성한 함수가 주어진 입력값에 대해서 리턴하는 값이 기대하는 값과 같은지를 비교하는 것입니다. + + 이때 테스트하는 값과 기대값을 비교하기 위해 expect 함수를 사용합니다. + expect의 사용법은 아래와 같습니다. + + expect(테스트하는값).기대하는조건 + expect(isEven(3)).to.be.true => 'isEven(3)'의 결과값은 참(true)이어야 한다' + expect(1 + 2).to.equal(3) => 'sum(1, 2)의 결과값은 3과 같아야(equal) 한다' + + '기대하는조건'에 해당하는 함수를 matcher라고 합니다. + '참인 것이어야 한다' => to.be.true + '3과 같아야 한다' => to.equal(3) + + mocha, chai framework에는 다양한 matcher가 있지만, 이번 과제에서는 그 일부가 사용됩니다. + 궁금하시면 아래 링크를 통해 확인하시기 바랍니다. (어떤 것들이 있는지만 확인하는 수준이면 충분합니다.) + https://www.chaijs.com/api/bdd/ + + 여러분들은 expect를 활용하여 모든 Koans 과제를 완성하시면 됩니다. + 즉, 각 테스트 케이스가 테스트를 통과하도록 테스트 코드를 적절하게 변형하여야 합니다. + 단순히 테스트를 통과하는 것을 넘어, '왜 통과하는지'를 고민하는 시간이 되었으면 합니다. + */ + + it('테스트하는 값(expect의 전달인자)이 true인지의 여부를 검사합니다.', function () { + /* + 첫 문제는 가볍게 풀어봅시다. + expect 함수의 전달인자를 아래와 같이 false 대신 true로 변경하여 테스트를 통과하세요. + expect(true).to.be.true; + */ + // TODO: 테스트가 통과될 수 있도록(테스트하는 값이 true가 되도록) expect의 첫 번째 전달인자를 수정합니다. + expect(true).to.be.true; + }); + + it('테스트하는 값(expect의 전달인자)이 falsy 여부를 검사합니다.', function () { + // 반대의 경우에는 어떻게 해야할까요? + // TODO: 테스트가 통과될 수 있도록(테스트하는 값이 false가 되도록) expect의 첫 번째 전달인자를 수정합니다. + expect(false).to.be.false; + }); + + /* + 2가지 matcher(.true, .false)를 통해 '기대하는 값'이 true인지 false인지 확인하는 방법을 학습했습니다. + 이 때, '기대하는 값'은 표현식(expression)이거나 함수의 실제 실행 결과입니다. + 1) 표현식: true || false, 1 + 1, 10 * 3 + 2) 함수의 실행: isEven(3), sum(1, 2) + + 보시면 알 수 있듯이, '기대하는 값'은 true, false 뿐만 아니라 어떤 구체적인 값인 경우가 있습니다. + 이 경우 '기대하는 값'이 '특정 값'과 같은지를 비교해서 테스트를 진행하면 됩니다. + 가장 간단한 방법은 비교 연산자 === 을 사용하는 방법이 있습니다. + '기대하는 값과 특정 값을 비교한 결과'가 true인지 확인하면 됩니다. + expect('기대하는 값과 특정 값을 비교한 결과').to.be.true; + */ + it("'테스트하는 값'을 '기대하는 값'과 비교한 결과가 참 인지 확인합니다.", function () { + // '테스트하는 값'은 우리가 작성한 어떤 코드의 실제 실행 결과 값이므로 '실제 값'이라고 불러도 됩니다. + let actualValue = 1 + 1; + let expectedValue = 2; // TODO: 'FILL_ME_IN'을 변경하여 테스트 케이스를 완성합니다. + expect(actualValue === expectedValue).to.be.true; + }); + + /* + 이처럼 to.be.true, to.be.false 만을 가지고도 많은 테스트 케이스를 작성할 수 있습니다. + 하지만 이는 직관적이지 않고 다소 불편합니다. + 두 값 A와 B를 '비교한 결과'가 참인지를 확인하는 대신에 직접 A가 B와 같은지 확인하는 matcher가 없을까요? + .equal이 바로 그런 역할을 합니다. 아래 테스트 코드는 '테스트하는값'이 '기대하는값'과 같은지 직접 확인합니다. + expect('테스트하는값').to.equal('기대하는값'); + + 이제 'FILL_ME_IN'을 적절한 값으로 변경하여 테스트 케이스를 완성하시면 됩니다. + 이후에도 같은 방식으로 'FILL_ME_IN' 변경하면 됩니다. + */ + it('Matcher .equal 의 사용법을 학습합니다.', function () { + let expectedValue = 2; // TODO + // .equal은 두 값이 타입까지 엄격하게 같은지 검사(strict equality, ===)합니다. + expect(1 + 1).to.equal(expectedValue); + }); + + it('Matcher .equal의 사용법을 학습합니다.', function () { + let actualValue = (1 + 1).toString(); + expect(actualValue).to.equal("2"); // TODO + }); +}); diff --git a/02_Types-part1.js b/02_Types-part1.js new file mode 100644 index 0000000..b3157e8 --- /dev/null +++ b/02_Types-part1.js @@ -0,0 +1,67 @@ +describe('type에 대해서 학습합니다.', function () { + it("비교연산자 '=='는 두 값의 일치 여부를 느슨하게 검사(loose equality)합니다.", function () { + let actualValue = 1 + 1; + let expectedValue = 2; + expect(actualValue == expectedValue).to.be.true; + + /* + 혹시 'FILL_ME_IN'을 '2'(문자열 '2')로 변경해도 테스트가 통과된다는 사실을 알고 계십니까? + '=='의 실행 중 타입 변환(type coercion)이 일어나기 때문입니다. + 이는 다소 복잡하고, 명확한 규칙이 존재하지 않습니다. + 느슨한 동치 연산자 '=='의 느슨함을 보여주는 예시가 아래에 있습니다. + 아래 테스트 코드들의 주석을 제거해도 이 테스트는 통과합니다. + */ + + // expect(0 == false).to.be.true; + // expect('' == false).to.be.true; + // expect([] == false).to.be.true; + // expect(![] == false).to.be.true; + // expect([] == ![]).to.be.true; + // expect([] == '').to.be.true; + // expect([] == 0).to.be.true; + // expect([''] == '').to.be.true; + // expect([''] == 0).to.be.true; + // expect([0] == 0).to.be.true; + }); + + /* + 사실 느슨한 동치 연산(loose equality)는 프로그램의 작동을 예측하기 다소 어렵게 만듭니다. + '=='의 특성을 정확하게 외워서 모든 상황에 대응하겠다는 자세는 접어두시기 바랍니다. + 매우 비효율적일뿐더러 일반적인 컴퓨터 프로그래밍 언어의 관습(convention)과도 거리가 먼 자세입니다. + 지금부터는 엄격한 동치 연산(strict equality) '==='을 사용하시기 바랍니다. + */ + + it("비교연산자 '==='는 두 값의 일치 여부를 엄격하게 검사(strict equality)합니다.", function () { + let actualValue = 1 + 1; + let expectedValue = 2; + expect(actualValue === expectedValue).to.be.true; + // 이제 'FILL_ME_IN'을 대신할 수 있는 건 number 타입의 2뿐입니다. + // 문자열 '2'는 테스트를 통과하지 못합니다. + }); + + 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); + }); + + it('expect의 전달인자로 들어간 표현식의 평가(evaluation) 결과를 예측해 봅니다.', function () { + expect('1' + true).to.equal("1true"); + }); + + /* + 지금까지 본 것처럼 자바스크립트에는 다소 이해하기 힘든 부분들이 존재합니다. + 아래처럼 자바스크립트의 별난(quirky) 부분들을 따로 모아둔 저장소도 있을 정도입니다. + https://github.com/denysdovhan/wtfjs + + 여기서도 동치 연산을 학습하면서 당부한 자세가 요구됩니다. + 이런 별난 특성들을 전부 외워서 모든 상황에 대응하려고 하지 말고, 올바른 코딩 습관을 기르시기 바랍니다. + 대표적으로 최대한 같은 타입끼리 연산을 하고, 즉 엄격한 동치 연산('===')을 사용하고, 조건문에 비교 연산을 명시하는 것이 훨씬 좋습니다. + */ +}); diff --git a/03_LetConst.js b/03_LetConst.js new file mode 100644 index 0000000..0ae67e7 --- /dev/null +++ b/03_LetConst.js @@ -0,0 +1,44 @@ +describe("'const'에 대해서 학습합니다.", function () { + it("'const'로 선언된 변수에는 재할당(reassignment)이 금지됩니다.", function () { + // 아래 코드에서 문제가 되는 부분을 삭제합니다. + const constNum = 0; + expect(constNum).to.equal(0); + + const constString = 'I am a const'; + expect(constString).to.equal('I am a const'); + }); + + it("'const'로 선언된 배열의 경우 새로운 요소를 추가하거나 삭제할 수 있습니다.", function () { + const arr = []; + const toBePushed = 42; + arr.push(toBePushed); + expect(arr[0]).to.equal(42); + + // 여전히 재할당은 금지됩니다. + // arr = [1, 2, 3]; + }); + + 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'); + }); + + /* + 재할당도 안되는 'const' 키워드를 굳이 써야하는지 이해가 안 될수도 있습니다. + 'let' 키워드는 재할당이 가능하기 때문에 여러모로 편하고, 큰 문제도 없어 보이기 때문입니다. + 이에 대해서 잠시 고민하신 후, 'const'가 추천되는 이유에 대해 직접 찾아보시기 바랍니다. + + 동기 부여를 위해 구글 자바스크립트 코딩 스타일 가이드를 소개해 드립니다. + 세계의 탑코더들이 있는 구글 스타일 가이드는 선언 키워드에 대해서 어떻게 안내하고 있을까요? + https://google.github.io/styleguide/jsguide.html#features-use-const-and-let + */ +}); diff --git a/04_Scope.js b/04_Scope.js new file mode 100644 index 0000000..875ffb5 --- /dev/null +++ b/04_Scope.js @@ -0,0 +1,140 @@ +describe('scope 대해서 학습합니다.', function () { + // scope는 변수의 값(변수에 담긴 값)을 찾을 때 확인하는 곳을 말합니다. 반드시 기억하시기 바랍니다. + 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'); + }); + + 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'); + }); + + it('default parameter에 대해 확인합니다.', function () { + function defaultParameter(num = 5) { + // 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; + // increaseByAmount => 3 + }; + } + + 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); + + /* + 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); + }); +}); diff --git a/05_ArrowFunction.js b/05_ArrowFunction.js new file mode 100644 index 0000000..32afdce --- /dev/null +++ b/05_ArrowFunction.js @@ -0,0 +1,52 @@ +describe('화살표 함수에 관해서', function () { + it('함수 표현식 사용법을 복습합니다', function () { + const add = function (x, y) { + return x + y + } + + expect(add(5, 8)).to.eql(13) + }) + + it('화살표 함수 사용법을 익힙니다', function () { + // 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 + } + } + + expect(adder(50)(10)).to.eql(60) + + const subtractor = x => y => { + return x - y + } + + expect(subtractor(50)(10)).to.eql(40) + + const htmlMaker = tag => textContent => `<${tag}>${textContent}` + expect(htmlMaker('div')('javascript')).to.eql('
javascript
') + + const liMaker = htmlMaker('li') //->
  • undefined
  • + expect(liMaker('1st item')).to.eql('
  • 1st item
  • ') + expect(liMaker('2nd item')).to.eql('
  • 2nd item
  • ') + }) +}) diff --git a/06_Types-part2.js b/06_Types-part2.js new file mode 100644 index 0000000..7387caf --- /dev/null +++ b/06_Types-part2.js @@ -0,0 +1,203 @@ +describe('primitive data type과 reference data type에 대해서 학습합니다.', function () { + /* + + * 아래 주석이 이해하기 어렵다면, 유어클래스 Lesson - Primitive & Reference를 복습하세요 :) + + 자바스크립트에서 원시 자료형(primitive data type 또는 원시값)은 객체가 아니면서 method를 가지지 않는 아래 6가지의 데이터를 말합니다. + string, number, bigint, boolean, undefined, symbol, (null) + */ + it('원시 자료형은 값 자체에 대한 변경이 불가능(immutable)합니다.', function () { + let name = 'javascript'; + expect(name).to.equal('javascript'); + // 원본은 안바꿈 -> 대문자로 바꾸려면 nmae = nmae.toUppercase + expect(name.toUpperCase()).to.equal('JAVASCRIPT'); + expect(name).to.equal('javascript'); + // 새로운 값으로 재할당은 가능합니다. + name = name.toUpperCase(); + expect(name).to.equal('JAVASCRIPT'); + + /* + 원시 자료형은 값 자체에 대한 변경이 불가능하다고 하는데, 한 변수에 다른 값을 할당하는 것은 변경이 된 것이 아닌가요? + let num1 = 123; + num2 = 123456; + 원시 자료형 그 자체('hello', 123, 456n, true 등)와 원시 자료형이 할당된 변수는 구분되어야 합니다. + 사과 박스에 귤을 담았다고 해서, 귤이 갑자기 사과가 되지는 않는 것과 같이 123이 갑자기 123456이 되지 않습니다. + */ + }); + + 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 = 'variable' / variableCopy = 'variable' + variable = variableCopy; + expect(variable).to.equal('variable'); + }); + + it('원시 자료형 또는 원시 자료형의 데이터를 함수의 전달인자로 전달할 경우, 값 자체의 복사가 일어납니다.', function () { + let currentYear = 2020; + function afterTenYears(year) { + year = year + 10; + } + afterTenYears(currentYear); + expect(currentYear).to.equal(2020); + function afterTenYears2(currentYear) { + currentYear = currentYear + 10; + return currentYear; + } + let after10 = afterTenYears2(currentYear); + expect(currentYear).to.equal(2020); + expect(after10).to.equal(2030); + // 사실 함수의 전달인자도 변수에 자료(data)를 할당하는 것입니다. + // 함수를 호출하면서 넘긴 전달인자가 호출된 함수의 지역변수로 (매 호출 시마다) 새롭게 선언됩니다. + }); + + /* + 자바스크립트에서 원시 자료형이 아닌 모든 것은 참조 자료형 입니다. 배열([])과 객체({}), 함수(function(){})가 대표적입니다. + + const pi = 3.14 + const arr = ["hello", "world", "java", "spring"]; + + 위 두 가지 코드에서 어떤 차이를 찾으실 수 있나요? 아쉽게도 보기에는 큰 차이가 없습니다. + 하지만 자바스크립트는 보기와는 다르게 작동되는 부분이 있습니다. (under the hood) + 여기서 변수 pi에는 3.14라는 원시 자료형 '값'이 할당되고, arr에는 참조 자료형의 '주소'가 할당됩니다. + 영어 단어 reference 의미와 연결시켜보면 실제 데이터가 저장된 주소를 가리킨다(refer), 즉, 참조(reference)한다로 이해하면 쉽습니다. + + 왜 참조 자료형에서는 '주소'를 할당할 수 밖에 없을까요? + 원시 자료형은 immutable 하다고 말씀 드렸습니다. 참조 자료형은, 그렇지 않습니다. + + 우리가 배열에 요소를 추가 및 삭제하고, 객체에 속성을 추가 및 삭제할 수 있었습니다. + 이것 자체가, 참조 자료형은 이미 immutable하지 않다는 것을 보여주고 있습니다. + 언제든 데이터가 늘어나고 줄어들 수 있죠 (동적으로 변한다.), 그렇기 때문에 특별한 저장공간의 주소를 변수에 할당함으로써 더 잘 관리하고자 합니다. + 이런 저장 공간을 heap이라고 부릅니다. + + 아래와 같이 코드가 작성되어 있다면... + let num = 123; + const msg = "hello"; + let arr = [1, 2, 3]; + const isOdd = true; + + 원시 자료형의 데이터가 저장되는 공간 (stack) + 1 | num | 123 + 2 | msg | "hello" + 3 | arr | heap의 12번부터 3개 // (실제 데이터가 저장되어 있는 주소) + 4 |isOdd| true + ===================================== + Object 자료형의 데이터가 저장되는 공간 (heap) + 10 || + 11 || + 12 || 1 + 13 || 2 + 14 || 3 + 실제 자바스크립트는 변수를 위와 같이 저장할 것입니다. + + * + * 위의 원리가 잘 이해 되셨나요? heap과 stack이라는 용어가 어색하더라도, 위 원리가 잘 이해되었다면 괜찮습니다. + * 이해가 잘 안되시면, 원시 자료형이 할당되는 경우는 값 자체가 할당되고, 참조 자료형은 주소가 할당된다고 암기하셔도 좋습니다. + * + * const hello = "world"; // "world" 그 자체 + * const arr = [1, 2, 3]; // [1, 2, 3] 의 메모리 주소 xxxxxx + * + */ + it('참조 자료형의 데이터는 동적(dynamic)으로 변합니다.', function () { + const arr = [1, 2, 3]; + expect(arr.length).to.equal(3); + arr.push(4, 5, 6); + expect(arr.length).to.equal(6); + arr.pop(); + expect(arr.length).to.equal(5); + + const obj = {}; + expect(Object.keys(obj).length).to.equal(0); + obj['name'] = 'codestates'; + obj.quality = 'best'; + obj.product = ['sw engineering', 'product manager', 'growth marketing', 'data science']; + expect(Object.keys(obj).length).to.equal(3); + delete obj.name; + expect(Object.keys(obj).length).to.equal(2); + }); + + it('참조 자료형을 변수에 할당할 경우, 데이터의 주소가 저장됩니다.', function () { + /* + 참조 자료형의 경우, 값 자체의 복사가 일어나지 않는 이유는 어느 정도 납득할만한 이유가 있습니다. + 배열이 얼마나 많은 데이터를 가지고 있는지가 프로그램의 실행 중 수시로 변경될 수 있기 때문입니다. + 쉽게 생각해서 number 타입 데이터 100만개를 요소로 갖는 배열을 생각해 봅시다. + 따로 명시하지 않는 이상 100만개의 데이터를 일일히 복사하는 것은 상당히 비효율적입니다. + 따라서 일단은 주소만 복사해서 동일한 데이터를 바라보는 게 만드는 것이 효율적입니다. + 배열과 객체의 데이터를 복사하는 방법은 06_Array.js, 07_Object.js에서 다룹니다. + */ + const overTwenty = ['hongsik', 'minchul', 'hoyong']; + let allowedToDrink = overTwenty; + + overTwenty.push('san'); + expect(allowedToDrink).to.deep.equal(['hongsik', 'minchul', 'hoyong', 'san']); + overTwenty[1] = 'chanyoung'; + expect(allowedToDrink[1]).to.deep.equal('chanyoung'); + // .deep.equal은 배열의 요소나 객체의 속성이 서로 같은지 확인하는 matcher입니다. + // .equal아닌 .deep.equal을 사용하는 이유는 아래 테스트 코드를 통해 고민하시기 바랍니다. + + const ages = [22, 23, 27]; + allowedToDrink = ages; + expect(allowedToDrink === ages).to.equal(true); + + const nums1 = [1, 2, 3]; + const nums2 = [1, 2, 3]; + expect(nums1 === nums2).to.equal(false); + + const person = { + son: { + age: 9, + }, + }; + + const boy = person.son; //boy -> {age : 9}; + boy.age = 20; + expect(person.son.age).to.equal(20); + expect(person.son === boy).to.equal(true); + + /* + 아래의 테스트 코드들은 선뜻 받아들이기 힘들 수 있습니다. + const nums1 = [1, 2, 3]; + const nums2 = [1, 2, 3]; + expect(nums1 === nums2).to.equal(FILL_ME_IN); + 배열 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에 저장되어 있습니다. + 이게 비효율적으로 보일수도 있습니다. 굳이 같은 데이터를 왜 한번 더 저장하는 지 이해하기란 쉽지 않습니다. + + 하지만 [1, 2, 3]이 아니라 상당히 큰 데이터(예. length가 100,000인 배열)를 가지고 다시 생각해 봅시다. + const nums1 = [10, 2, 71, ..., 987]; // 길이 100,000개인 배열 + const nums2 = [10, 2, 71, ..., 987]; // 길이 100,000개인 배열 + 이 두 배열이 서로 같아서 두 번 저장할 필요가 없다고 말하려면, 일단 두 배열이 같은지 확인해야 합니다. + 이런 작업을 Object 자료형을 쓸 때마다 한다고 가정해보면, 이것이 얼마나 비효율적인지를 금방 알 수 있습니다. + + 이제 아래와 같이 정리할 수 있습니다. 반드시 기억하시기 바랍니다. + Object 자료형은 데이터는 heap에 저장되고, 변수에 할당을 할 경우 변수에는 주소가 저장된다. + 1) [1, 2, 3]; // [1, 2, 3]이라는 데이터가 heap에 저장되지만 변수 할당이 되지 않아 주소는 어디에도 저장되지 않는다. + 2) const num1 = [1, 2, 3]; // // [1, 2, 3]이라는 데이터가 heap에 저장되고, 그 주소가 변수 num1에 저장된다. + 3) const num2 = [1, 2, 3]; // // [1, 2, 3]이라는 데이터가 heap에 저장되고, 그 주소가 변수 num2에 저장된다. + 1), 2), 3)에서 말하는 주소는 전부 다른 주소입니다. + + 아래의 객체 간 비교도 동일한 논리로 이해하시면 됩니다. + expect(person.son === { age: 20 }).to.equal(FILL_ME_IN); + + 다음 문제를 해결해 보시기 바랍니다. + const num1 = [1, 2, 3]; // [1, 2, 3]이 heap에 저장되고, 그 주소가 변수 num1에 저장된다. + const num2 = num1; // 변수 num1에 저장된 주소가 변수 num2에 저장된다. + // 두 변수 num1, num2는 같은 주소를 저장하고 있습니다. 아래 결과는 어떻게 될까요? + expect(num1 === num2).to.equal(FILL_ME_IN); + */ + }); +}); diff --git a/07_Array.js b/07_Array.js new file mode 100644 index 0000000..67d4996 --- /dev/null +++ b/07_Array.js @@ -0,0 +1,94 @@ +describe('Array에 대해서 학습합니다.', function () { + it('Array의 기본을 확인합니다.', function () { + const emptyArr = []; + expect(typeof emptyArr === 'array').to.equal(false); + expect(emptyArr.length).to.equal(0); + + const multiTypeArr = [ + 0, + 1, + 'two', + function () { + return 3; + }, + { value1: 4, value2: 5 }, + [6, 7], + ]; + expect(multiTypeArr.length).to.equal(6); + expect(multiTypeArr[0]).to.equal(0); + expect(multiTypeArr[2]).to.equal('two'); + expect(multiTypeArr[3]()).to.equal(3); + expect(multiTypeArr[4].value1).to.equal(4); + expect(multiTypeArr[4]['value2']).to.equal(5); + expect(multiTypeArr[5][1]).to.equal(7); + }); + + 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(); // 3, arr = [1, 2] + // const poppedValue = arr.pop(); -> arr.pop(3) -> 뺀 값 Return + expect(poppedValue).to.equal(3); + expect(arr).to.deep.equal([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(['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([]); + + // arr.slice는 arr의 값을 복사하여 새로운 배열을 리턴합니다. + // 아래의 코드는 arr 전체를 복사합니다. 자주 사용되니 기억하시기 바랍니다. + 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']; + + function passedByReference(refArr) { + refArr[1] = 'changed in function'; + } + passedByReference(arr); + // 깊은 복사 + // 1. passedByReference(arr).slice(0); + // 2. passedByReference(...arr); + expect(arr[1]).to.equal('changed in function'); + + const assignedArr = arr; + assignedArr[5] = 'changed in assignedArr'; + expect(arr[5]).to.equal('changed in assignedArr'); + + const copiedArr = arr.slice(); + copiedArr[3] = 'changed in copiedArr'; + expect(arr[3]).to.equal('three'); + }); + + it('Array 메소드 shift와 unshift를 확인합니다.', function () { + const arr = [1, 2]; + + arr.unshift(3); + expect(arr).to.deep.equal([3, 1, 2]); + + const shiftedValue = arr.shift(); + expect(shiftedValue).to.deep.equal(3); + expect(arr).to.deep.equal([1, 2]); + }); +}); diff --git a/08_Object.js b/08_Object.js new file mode 100644 index 0000000..077d893 --- /dev/null +++ b/08_Object.js @@ -0,0 +1,156 @@ +describe('Object에 대해서 학습합니다.', function () { + /* + 이번 과제에서는 객체의 기본적인 내용을 재확인합니다. + 이머시브 과정에서 객체를 보다 자세하게 학습하게 됩니다. (예. prototype) + */ + it('Object의 기본을 확인합니다.', function () { + const emptyObj = {}; + expect(typeof emptyObj === 'object').to.equal(true); + expect(emptyObj.length).to.equal(undefined); // Object는 length 사용 불가 + + const megalomaniac = { + mastermind: 'Joker', + henchwoman: 'Harley', + getMembers: function () { + return [this.mastermind, this.henchwoman]; + }, + relations: ['Anarky', 'Duela Dent', 'Lucy'], + twins: { + 'Jared Leto': 'Suicide Squad', + 'Joaquin Phoenix': 'Joker', + 'Heath Ledger': 'The Dark Knight', + 'Jack Nicholson': 'Tim Burton Batman', + }, + }; + + expect(megalomaniac.length).to.equal(undefined); + expect(megalomaniac.mastermind).to.equal('Joker'); + expect(megalomaniac.henchwoman).to.equal('Harley'); + expect(megalomaniac.henchWoman).to.equal(undefined); + expect(megalomaniac.getMembers()).to.deep.equal(['Joker', 'Harley']); + expect(megalomaniac.relations[2]).to.equal('Lucy'); + expect(megalomaniac.twins['Heath Ledger']).to.deep.equal('The Dark Knight'); + }); + + 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); + }); + + 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; + }, + }; + // this -> new Date() 현재 년도 날짜 시간 분 나옴 + expect(currentYear).to.equal(2024); + expect(megalomaniac.calculateAge(currentYear)).to.equal(54); + + megalomaniac.birthYear = 2000; + expect(megalomaniac.calculateAge(currentYear)).to.equal(24); + + megalomaniac.changeBirthYear(2010); + expect(megalomaniac.calculateAge(currentYear)).to.equal(14); + + /** + * !!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를 찾습니다. + * 그렇기에 화살표 함수를 사용할 때, 이러한 특이점을 생각하고 사용해야 합니다. + * + * 이와 관련하여, this에 대해서 더 깊이 학습하셔도 좋습니다. + * 가이드가 될 만한 학습자료를 첨부합니다. + * https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this + */ + }); + + it('객체의 method를 정의하는 방법을 확인합니다.', function () { + const megalomaniac = { + mastermind: 'Brain', + henchman: 'Pinky', + getFusion: function () { + return this.henchman + this.mastermind; + }, + battleCry(numOfBrains) { + return `They are ${this.henchman} and the` + ` ${this.mastermind}`.repeat(numOfBrains); + }, + }; + + expect(megalomaniac.getFusion()).to.deep.equal('PinkyBrain'); + expect(megalomaniac.battleCry(3)).to.deep.equal('They are Pinky and the Brain Brain Brain'); + }); + + 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'); + + 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 + */ + }); +}); diff --git a/09_SpreadSyntax.js b/09_SpreadSyntax.js new file mode 100644 index 0000000..793ec93 --- /dev/null +++ b/09_SpreadSyntax.js @@ -0,0 +1,122 @@ +describe('Spread syntax에 대해 학습합니다.', function () { + it('전개 문법(spread syntax)을 학습합니다.', function () { + const spread = [1, 2, 3]; + // TODO: 전개 문법을 사용해 테스트 코드를 완성합니다. spread를 지우지 않고 해결할 수 있습니다. + const arr = [0, spread, 4]; + expect(arr).to.deep.equal([0, [1, 2, 3], 4]); + }); + + it('빈 배열에 전개 문법을 사용할 경우, 아무것도 전달되지 않습니다.', function () { + const spread = []; + // TODO: 전개 문법을 사용해 테스트 코드를 완성합니다. spread를 지우지 않고 해결할 수 있습니다. + const arr = [0, spread, 1]; + expect(arr).to.deep.equal([0, [], 1]); + }); + + it('여러 개의 배열을 이어붙일 수 있습니다.', function () { + const arr1 = [0, 1, 2]; + const arr2 = [3, 4, 5]; + const concatenated = [...arr1, ...arr2]; + expect(concatenated).to.deep.equal([0, 1, 2, 3, 4, 5]); + // 아래 코드도 같은 동작을 수행합니다. + // arr1.concat(arr2); + }); + + 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'], + }); + }); + + it('Rest Parameter는 함수의 전달인자를 배열로 다룰 수 있게 합니다.', function () { + // 자바스크립트는 (named parameter를 지원하지 않기 때문에) 함수 호출 시 전달인자의 순서가 중요합니다. + function returnFirstArg(firstArg) { + return firstArg; + } + expect(returnFirstArg('first', 'second', 'third')).to.equal('first'); + + function returnSecondArg(firstArg, secondArg) { + return secondArg; + } + expect(returnSecondArg('only give first arg')).to.equal(undefined); + + // rest parameter는 spread syntax를 통해 간단하게 구현됩니다. + function getAllParamsByRestParameter(...args) { + return args; + } + + // arguments를 통해 '비슷하게' 함수의 전달인자들을 다룰 수 있습니다. (spread syntax 도입 이전) + // arguments는 모든 함수의 실행 시 자동으로 생성되는 '객체'입니다. + function getAllParamsByArgumentsObj() { + return arguments; + } + + const restParams = getAllParamsByRestParameter('first', 'second', 'third'); + const argumentsObj = getAllParamsByArgumentsObj('first', 'second', 'third'); + + expect(restParams).to.deep.equal(['first', 'second', 'third']); + expect(Object.keys(argumentsObj)).to.deep.equal(['0', '1', '2']); + expect(Object.values(argumentsObj)).to.deep.equal(['first', 'second', 'third']); + + // arguments와 rest parameter를 통해 배열로 된 전달인자(args)의 차이를 확인하시기 바랍니다. + expect(restParams === argumentsObj).to.deep.equal(false); + expect(typeof restParams).to.deep.equal('object'); + expect(typeof argumentsObj).to.deep.equal('object'); + expect(Array.isArray(restParams)).to.deep.equal(true); + expect(Array.isArray(argumentsObj)).to.deep.equal(false); + + 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); + }); + + 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); + }); + + it('Rest Parameter는 전달인자의 일부에만 적용할 수도 있습니다.', function () { + // rest parameter는 항상 배열입니다. + function getAllParams(required1, required2, ...args) { + return [required1, required2, args]; + } + expect(getAllParams(123)).to.deep.equal([123, undefined, []]); + + function makePizza(dough, name, ...toppings) { + const order = `You ordered ${name} pizza with ${dough} dough and ${toppings.length} extra toppings!`; + return order; + } + expect(makePizza('original')).to.equal(`You ordered undefined pizza with original dough and 0 extra toppings!`); + expect(makePizza('thin', 'pepperoni')).to.equal(`You ordered pepperoni pizza with thin dough and 0 extra toppings!`); + expect(makePizza('napoli', 'meat', 'extra cheese', 'onion', 'bacon')).to.equal(`You ordered meat pizza with napoli dough and 3 extra toppings!`); + }); +}); diff --git a/10_Destructuring.js b/10_Destructuring.js new file mode 100644 index 0000000..134d5c4 --- /dev/null +++ b/10_Destructuring.js @@ -0,0 +1,137 @@ +describe('구조 분해 할당(Destructuring Assignment)에 관해서', () => { + it('배열을 분해합니다', () => { + const array = ['java', 'spring', 'im', 'course'] + + const [first, second] = array + expect(first).to.eql('java') + expect(second).to.eql('spring') + + const result = [] + function foo([first, second]) { + result.push(second) + result.push(first) + } + + foo(array) + expect(result).to.eql(['spring', 'java']) + }) + + it('rest/spread 문법을 배열 분해에 적용할 수 있습니다', () => { + const array = ['java', 'spring', 'im', 'course'] + const [start, ...rest] = array + expect(start).to.eql('java') + expect(rest).to.eql(['spring', 'im', 'course']) + + // 다음과 같은 문법은 사용할 수 없습니다. 할당하기 전 왼쪽에는, rest 문법 이후에 쉼표가 올 수 없습니다 + // const [first, ...middle, last] = array + }) + + it('객체의 단축(shorthand) 문법을 익힙니다', () => { + const name = '김코딩' + const age = 28 + + const person = { + name, + age, + level: 'Junior', + } + expect(person).to.eql({name: '김코딩', age: 28, level: 'Junior'}) + }) + + it('객체를 분해합니다', () => { + const student = { name: '박해커', major: '물리학과' } + + const { name } = student + expect(name).to.eql('박해커') + }) + + it('rest/spread 문법을 객체 분해에 적용할 수 있습니다 #1', () => { + const student = { name: '최초보', major: '물리학과' } + const { name, ...args } = student + + expect(name).to.eql('최초보') + expect(args).to.eql({major: '물리학과'}) + }) + + it('rest/spread 문법을 객체 분해에 적용할 수 있습니다 #2', () => { + const student = { name: '최초보', major: '물리학과', lesson: '양자역학', grade: 'B+' } + + function getSummary({ name, lesson: course, grade }) { + return `${name}님은 ${grade}의 성적으로 ${course}을 수강했습니다` + } + + expect(getSummary(student)).to.eql('최초보님은 B+의 성적으로 양자역학을 수강했습니다') + }) + + it('rest/spread 문법을 객체 분해에 적용할 수 있습니다 #3', () => { + const user = { + name: '김코딩', + company: { + name: 'Javascript', + department: 'Development', + role: { + name: 'Software Engineer' + } + }, + age: 35 + } +// { +// name: '박해커', +// company: { +// name: 'Javascript', +// department: 'Development', +// role: { +// name: 'Software Engineer' +// } +// }, +// age: 20 +// } + const changedUser = { + ...user, + name: '박해커', + age: 20 + } + + const overwriteChanges = { + name: '박해커', + age: 20, + ...user + } + // const user = { + // name: '김코딩', + // company: { + // name: 'Javascript', + // department: 'Development', + // role: { + // name: 'Software Engineer' + // } + // }, + // age: 35 + // } + + const changedDepartment = { + ...user, + company: { + ...user.company, + department: 'Marketing' + } + } + // const user = { + // name: '김코딩', + // company: { + // name: 'Javascript', + // department: 'Marketing', + // role: { + // name: 'Software Engineer' + // } + // }, + // age: 35 + // } + + expect(changedUser).to.eql(FILL_ME_IN) + + expect(overwriteChanges).to.eql(FILL_ME_IN) + + expect(changedDepartment).to.eql(FILL_ME_IN) + }) +})