koan 풀기
koan은 자바스크립트 기본 프로그래밍을 퀴즈 형식의 문제를 풀며 이해하는 방식으로 진행된다.
https://github.com/mrdavidlaing/javascript-koans
mrdavidlaing/javascript-koans
Koans to learn Javascript. Contribute to mrdavidlaing/javascript-koans development by creating an account on GitHub.
github.com
1. Thinking About Expects
등호를 배워보자
10=='10': a와 b의 값이 같다.
10===10: a와b는 값에다가 데이터 타입까지 같다.
2. Thinking About Arrays
배열을 배워보자
하나의 배열에는 여러 타입의 원소가 들어갈 수 있다. ( 값,함수,문자열,객체,배열 등등)
array.push(a): array의 마지막에 새로운 원소로 a를 추가한다.
array.push(a,b): a와 b는 각각 array의 새로운 원소이며 인덱스가 +2 된다.
var emptyArray = new Array(10): 길이가 10인 배열 emptyArray 생성 방법
array.slice(a,b): array의 a번째 인덱스 원소부터 b-1번째 원소까지를 잘라서 새로운 배열로 리턴한다.
array.pop(): array의 마지막 원소를 삭제하고 이를 리턴한다.
array.unshift(a): array의 맨 처음에 a를 넣고 새로운 배열의 길이를 리턴한다.
3. Thinking About Functions
함수를 배워보자
var variable = "top-level";
function parentfunction() {
var variable = "local";
function childfunction() {
return variable;
}
return childfunction();
}
--> 전역변수 variable과 parentfunction함수 내에 지역변수 variable 이 있다.
parentfunction함수 내의 childfunction함수는 variable변수를 리턴하는데 이 값은 지역변수 variable이다.
function returnFirstArg(firstArg) {
return firstArg;
}
expect(returnFirstArg("first", "second", "third")).toBe('first');
function returnSecondArg(firstArg, secondArg) {
return secondArg;
}
expect(returnSecondArg("only give first arg")).toBe(undefined);
-->하나의 인자를 갖는 함수 function firstArg(first)는 firstArg(a,b,c);를 했을 때 a만을 인자로 갖는다.
인자가 정해지지 않은 함수 function returnAllArgs()는 returnAllArgs(a,b,c);를 했을 때 a,b,c 모두 인자로 갖는다.
var appendRules = function (name) {
return name + " rules!";
};
var praiseSinger = { givePraise: appendRules };
expect(praiseSinger.givePraise("John")).toBe('John rules!');
--> 함수를 변수에 저장하면 변수명을 가지고도 이용할 수 있다.
4. Thinking About Objects
객체를 배워보자
var megalomaniac = {
mastermind : "Brain",
henchman: "Pinky",
battleCry: function (noOfBrains) {
return "They are " + this.henchman + " and the" +
Array(noOfBrains + 1).join(" " + this.mastermind);
}
};
var battleCry = megalomaniac.battleCry(4);
expect(battleCry).toMatch('They are Pinky and the Brain');
--> megalomaniac 객체는 mastermind, henchman, battleCry 프로퍼티를 가지고 있고 이중 battleCry는 함수이다.
객체 내 함수는 객체가 가지고 있는 변수를 사용하고 싶을 때 this.변수명으로 사용할 수 있다.
var megalomaniac = { mastermind : "Agent Smith", henchman: "Agent Smith" };
expect("secretary" in megalomaniac).toBe(false);
megalomaniac.secretary = "Agent Smith";
expect("secretary" in megalomaniac).toBe(true);
--> 이미 만들어진 객체에도 프로퍼티를 추가할 수 있다.
function Circle(radius)
{
this.radius = radius;
}
var simpleCircle = new Circle(10);
var colouredCircle = new Circle(5);
colouredCircle.colour = "red";
expect(simpleCircle.colour).toBe(undefined);
expect(colouredCircle.colour).toBe('red');
Circle.prototype.describe = function () {
return "This circle has a radius of: " + this.radius;
}; // 프로토타입에 메서드 넣으면 모든 객체에서 사용가능
expect(simpleCircle.describe()).toBe('This circle has a radius of: 10');
expect(colouredCircle.describe()).toBe('This circle has a radius of: 5');
--> 모든 객체에 적용하고 싶다면 prototype에다가 넣어라.
5. Think About Mutability
var aPerson = {firstname: "John", lastname: "Smith" };
aPerson.firstname = "Alan";
expect(aPerson.firstname).toBe('Alan');
--> 객체의 프로퍼티는 public,mutable 라서 값을 바꿀 수 있다.
function Person(firstname, lastname)
{
var fullName = firstname + " " + lastname;
this.getFirstName = function () { return firstname; };
this.getLastName = function () { return lastname; };
this.getFullName = function () { return fullName; };
}
var aPerson = new Person ("John", "Smith");
aPerson.firstname = "Penny"; // aPerson에 firstname이라는 변수가 들어갔고 인자firstname이랑은 다르다.
aPerson.lastname = "Andrews";
aPerson.fullName = "Penny Andrews";
expect(aPerson.getFirstName()).toBe('John');
expect(aPerson.getLastName()).toBe('Smith');
expect(aPerson.getFullName()).toBe('John Smith');
console.log(aPerson);
aPerson.getFullName = function () {
return aPerson.lastname + ", " + aPerson.firstname;
};
expect(aPerson.getFullName()).toBe('Andrews, Penny');
--> 구조체 내부 변수와 구조체 인자는 private이다. aPerson.fullName과 aPerson.getFullName()의 값은 다르다.
6. Thinking About Higher Order Functions
filter, map, reduce, forEach, all, any, range, flatten, chain, value
1. filter
function () {
var numbers = [1,2,3];
var odd = _(numbers).filter(function (x) { return x % 2 !== 0 });
expect(odd).toEqual([1,3]);
expect(odd.length).toBe(2);
expect(numbers.length).toBe(3);
}
--> numbers배열 안의 모든 원소에 대해서 return값이 true인 원소만을 odd에 저장한다.
2. map
function () {
var numbers = [1, 2, 3];
var numbersPlus1 = _(numbers).map(function(x) { return x + 1 });
expect(numbersPlus1).toEqual([2,3,4]);
expect(numbers).toEqual([1,2,3]);
}
--> numbers배열 안의 모든 원소에 x+1을 적용한 값을 numbersPlus1에 저장한다.
3. reduce
function () {
var numbers = [1, 2, 3];
var reduction = _(numbers).reduce(
function(/* result from last call */ memo, /* current */ x) { return memo + x }, /* initial */ 0); // 배열.reduce((누적값,현재값,인덱스) => {return 결과}, 초기값);
expect(reduction).toBe(6);
expect(numbers).toEqual([1,2,3]);
}
-->reduce는 배열 내의 모든 원소의 합을 구할때 자주 쓰이며, reduce(function(누적값,더할값) {return 누적값+더할값},0) 의 문법을 따른다.0은 누적값의 초기값이다.
4. forEach
function () {
var numbers = [1,2,3];
var msg = "";
var isEven = function (item) {
msg += (item % 2) === 0;
};
_(numbers).forEach(isEven);
expect(msg).toEqual('falsetruefalse');
expect(numbers).toEqual([1,2,3]);
}
--> forEach(function)는 원소 각각에 특정 함수를 적용할때 쓰인다.
7. Thinking About Inheritance
function Muppet(age, hobby) {
this.age = age;
this.hobby = hobby;
this.answerNanny = function(){
return "Everything's cool!";
}
}
function SwedishChef(age, hobby, mood) {
Muppet.call(this, age, hobby);
this.mood = mood;
this.cook = function() {
return "Mmmm soup!";
}
}
SwedishChef.prototype = new Muppet();
function() {
beforeEach(function(){
this.muppet = new Muppet(2, "coding");
this.swedishChef = new SwedishChef(2, "cooking", "chillin");
}
expect(this.swedishChef.cook()).toEqual('Mmmm soup!');
expect(this.swedishChef.answerNanny()).toEqual("Everything's cool!");
--> SwedishChef.prototype = new Muppet();을 통해서 상속이 일어났고 swedishChef는 answerNanny 프로퍼티를 사용할 수 있게 된다.
8. Thinking About Applying What We Have Learnt
지금까지 배운 기본 개념을 써먹어보자.
var sum = 0;
var numArr = _.range(1000).filter(function(x) {if (x%3===0 || x%5===0) {return x; }})
sum = numArr.reduce(function(accum,curr) {return accum+curr},0);
console.log(sum);
expect(233168).toBe(sum);
--> range(1000)으로 0부터 999까지 만든 다음 3이나 5로 나눠지는 수만 numArr에 넣는다. reduce를 이용해서 그 숫ㅈ들을 다 더한다.
var products;
beforeEach(function () {
products = [
{ name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
{ name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
{ name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
{ name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
{ name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
];
}
function () {
var ingredientCount = { "{ingredient name}": 0 };
for (i = 0; i < products.length; i++) {
for (j = 0; j < products[i].ingredients.length; j++) {
ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
}
}
console.log(ingredientCount);
expect(ingredientCount['mushrooms']).toBe(2);
}
--> product 배열은 음식 객체가 원소로 이루어져 있고, 함수는 이 음식에 어떤 재료가 몇번 들어갔는지 알아내는 함수이다.
객체의 길이로 for문을 돌리고 그 안에서 ingredient의 길이로 for문을 돌려서 ingredientCount객체에 성분명과 개수를 삽입하는 과정을 거친다.
배열과 객체를 사용해서 코드 작성하는 법을 배웠는데 아직 어떤 작성법이 더 좋은지를 모르기 때문에 좋은게 맞나 하는 생각이 든다. 객체, 메서드, this가 어려운 것 같아서 여러번 봐야겠다.