
Koans란
- 불교에서 유래된 단어로, 결론을 내리기 전에 이게 왜 맞는지 깊게 고민한다는 의미를 가지고 있다고 합니다. 답이 미리 제시되어 있기 때문에 고민 없이 풀면, 큰 어려움 없이 전부 다 풀 수 있습니다. 하지만 그게 왜 정답인지 깊게 고민해 보는 시간을 갖지 않는다면 충분히 성장하기 어려울 것입니다. Koans 문제 중에는 학습하지 않은 개념도 의도적으로 포함되어 있습니다. 페어와 함께 깊게 고민하고, 정답을 설명할 수 있도록 연습해 보는것.
Introduction
describe("expect에 대해서 학습합니다.", function () {
it("테스트하는 값(expect의 전달인자)이 true인지의 여부를 검사합니다.", function () {
expect(true).to.be.true;
});
it("테스트하는 값(expect의 전달인자)이 falsy 여부를 검사합니다.", function () {
expect(false).to.be.false;
});
it("'테스트하는 값'을 '기대하는 값'과 비교한 결과가 참 인지 확인합니다.", function () {
let actualValue = 1 + 1;
let expectedValue = 2;
expect(actualValue === expectedValue).to.be.true;
});
it("Matcher .equal 의 사용법을 학습합니다.", function () {
let expectedValue = 2;
expect(1 + 1).to.equal(expectedValue);
});
it("Matcher .equal의 사용법을 학습합니다.", function () {
let actualValue = (1 + 1).toString();
expect(actualValue).to.equal("2");
});
});
Types-part1
describe("type에 대해서 학습합니다.", function () {
it("비교연산자 '=='는 두 값의 일치 여부를 느슨하게 검사(loose equality)합니다.", function () {
let actualValue = 1 + 1;
let expectedValue = 2;
expect(actualValue == expectedValue).to.be.true;
});
it("비교연산자 '==='는 두 값의 일치 여부를 엄격하게 검사(strict equality)합니다.", function () {
let actualValue = 1 + 1;
let expectedValue = 2;
expect(actualValue === expectedValue).to.be.true;
});
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");
});
});
LetConst
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);
});
it("'const'로 선언된 객체의 경우, 속성을 추가하거나 삭제할 수 있습니다.", function () {
const obj = { x: 1 };
expect(obj.x).to.equal(1);
delete obj.x;
expect(obj.x).to.equal(undefined);
obj.occupation = "SW Engineer";
expect(obj["occupation"]).to.equal("SW Engineer");
});
});
Scope
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 () {
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) {
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 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);
});
});
ArrowFuntcion
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;
};
};
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}</${tag}>`;
expect(htmlMaker("div")("code states")).to.eql(
`<${"div"}>${"code states"}</${"div"}>`
);
const liMaker = htmlMaker("li");
expect(liMaker("1st item")).to.eql(`<${"li"}>${"1st item"}</${"li"}>`);
expect(liMaker("2nd item")).to.eql(`<${"li"}>${"2nd item"}</${"li"}>`);
});
});
Types-part2
describe("primitive data type과 reference data type에 대해서 학습합니다.", function () {
it("원시 자료형은 값 자체에 대한 변경이 불가능(immutable)합니다.", function () {
let name = "codestates";
expect(name).to.equal("codestates");
expect(name.toUpperCase()).to.equal("CODESTATES");
expect(name).to.equal("codestates");
name = name.toUpperCase();
expect(name).to.equal("CODESTATES");
});
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;
}
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);
});
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 () {
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");
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);
const person = {
son: {
age: 9,
},
};
const boy = person.son;
boy.age = 20;
expect(person.son.age).to.equal(20);
expect(person.son === boy).to.equal(true);
expect(person.son === { age: 9 }).to.equal(false);
expect(person.son === { age: 20 }).to.equal(false);
});
});
Array
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();
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([]);
expect(arr.slice(0)).to.deep.equal(["peanut", "butter", "and", "jelly"]);
});
it("Array를 함수의 전달인자로 전달할 경우, reference가 전달됩니다.", function () {
const arr = ["zero", "one", "two", "three", "four", "five"];
function passedByReference(refArr) {
refArr[1] = "changed in function";
}
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]);
});
});
Object
describe("Object에 대해서 학습합니다.", function () {
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"],
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;
},
};
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);
});
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);
});
});
SpreadSyntax
describe("Spread syntax에 대해 학습합니다.", function () {
it("전개 문법(spread syntax)을 학습합니다.", function () {
const spread = [1, 2, 3];
const arr = [0, ...spread, 4];
expect(arr).to.deep.equal([0, 1, 2, 3, 4]);
});
it("빈 배열에 전개 문법을 사용할 경우, 아무것도 전달되지 않습니다.", function () {
const 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]);
});
it("여러 개의 객체를 병합할 수 있습니다.", function () {
const fullPre = {
cohort: 7,
duration: 4,
mentor: "hongsik",
};
const me = {
time: "0156",
status: "sleepy",
todos: ["coplit", "koans"],
};
const merged = { ...fullPre, ...me };
expect(merged).to.deep.equal({
cohort: 7,
duration: 4,
mentor: "hongsik",
time: "0156",
status: "sleepy",
todos: ["coplit", "koans"],
});
});
it("Rest Parameter는 함수의 전달인자를 배열로 다룰 수 있게 합니다.", function () {
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);
function getAllParamsByRestParameter(...args) {
return args;
}
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",
]);
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 () {
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!`
);
});
});
Destructuring
describe("구조 분해 할당(Destructing Assignment)에 관해서", () => {
it("배열을 분해합니다", () => {
const array = ["code", "states", "im", "course"];
const [first, second] = array;
expect(first).to.eql("code");
expect(second).to.eql("states");
const result = [];
function foo([first, second]) {
result.push(second);
result.push(first);
}
foo(array);
expect(result).to.eql(["states", "code"]);
});
it("rest/spread 문법을 배열 분해에 적용할 수 있습니다", () => {
const array = ["code", "states", "im", "course"];
const [start, ...rest] = array;
expect(start).to.eql("code");
expect(rest).to.eql(["states", "im", "course"]);
});
it("객체의 단축(shorthand) 문법을 익힙니다", () => {
const name = "김코딩";
const age = 28;
const person = {
name,
age,
level: "Junior",
};
expect(person).to.eql({ name, age, 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: "Code States",
department: "Development",
role: {
name: "Software Engineer",
},
},
age: 35,
};
const changedUser = {
...user,
name: "박해커",
age: 20,
};
const overwriteChanges = {
name: "박해커",
age: 20,
...user,
};
const changedDepartment = {
...user,
company: {
...user.company,
department: "Marketing",
},
};
expect(changedUser).to.eql({
name: "박해커",
company: {
name: "Code States",
department: "Development",
role: { name: "Software Engineer" },
},
age: 20,
});
expect(overwriteChanges).to.eql({
name: "김코딩",
age: 35,
company: {
name: "Code States",
department: "Development",
role: { name: "Software Engineer" },
},
});
expect(changedDepartment).to.eql({
name: "김코딩",
company: {
name: "Code States",
department: "Marketing",
role: { name: "Software Engineer" },
},
age: 35,
});
});
});