expect(테스트하는 값).기대하는 조건
expect() 함수를 이용해서 테스트 케이스를 만들 수 있다. 테스트하는 값(실제값)과 기대하는 값과 기대하는 조건(matcher)을 구성해서 테스트 통과 기준을 만들어낼 수 있다.
Q. 테스트하는 값, 기대하는 값, 실제 값, 특정 값 이런 용어들이 헷갈리긴 한다. 어쨌든 표현식이나 함수의 실행값을 넣어 어떤 특정값과 같도록 기대하는 조건을 사용하는 식으로 문제 테스트 케이스를 만들어낼 수 있다는 것.
function () {
let input = ['Mars', 'Wayne', 'Mary'];
transformFirstAndLast(input);
expect(input).to.deep.equal(['Mars', 'Wayne', 'Mary']);
}
==을 통한 느슨한 연산은 실행 중 타입 변환이 일어난다. 느슨함을 보여주는 여러 예시가 있다. 느슨한 동치 연산을 하면 아래 테스트 케이스를 모두 통과하게 된다. 이런 예외 상황을 외우지 말고, 엄격한 동치 연산===을 사용하라.
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;
// expect('2' == 2).to.be.true;
자바스크립트의 별난 부분들 기괴하다. 이런 상황들을 모두 외우려 하지는 말라. 최대한 같은 타입끼리 연산을 하고, 엄격한 동치 연산을 사용하고, 조건문에 비교 연산을 명시하는 코딩 습관을 길러라.
1 + '1' // '11'
123 - '1' // 122
1 + true // 2
'1' + true // '1true'
const로 선언된 변수에는 재할당이 금지된다.
const로 선언된 배열의 경우 새로운 요소를 추가/삭제할 수 있다.
const로 선언된 객체의 경우 속성을 추가/삭제할 수 있다.
하지만, 여전히 재할당은 안 된다.
위와 아래가 상충된다고 느끼나? 원래 정말 이런 질문을 했었다. 참조 자료형에서 새로운 요소와 속성을 추가/삭제하는 것은 재할당과 같을까? 그렇지 않다.
새로운 요소나 속성을 추가/삭제하는 것은 새로운 주소를 부여하는 재할당이 아니고, 주소는 그대로인 집에 가구만 뺐다 넣었다 하는 거라고 보면 된다.
const 키워드로 생성한 배열이나 객체에 새롭게 [], {} 값을 재할당하면 이건 결국, 새로운 주소값을 주는 것과 같다.
코딩을 할 때, 변수가 의도적으로 재할당되지 않아야 하는 경우가 많다. 변수의 값이 바뀌지 않기를 원할 때, const 키워드가 추천된다.
스코프는 변수에 담긴 값을 찾을 때 확인하는 곳이다.
호이스팅 : 함수 안에 있는 변수의 선언들을 모두 끌어올려서 해당 함수의 유효범위(스코프)의 최상단에 선언을 해주는 것을 일컫는다.
함수 선언식은 호이스팅의 대상이다. 스코프 내에서 어떤 위치에서 함수 선언을 하든지 호출할 수 있습니다. 표현식은 호이스팅의 대상이 아니다.
sayName();
function sayName(){
console.log('surisrui');
}
====비교====
sayName();
var sayName = function(){
console.log('surisuri');
}
// var 호이스팅에 의해 위의 코드는 아래와 같이 작동한다.
var sayName;
sayName(); // TypeError 발생
sayName = function(){
console.log('surisuri')
}
let 키워드를 사용해 함수 표현식을 작성할 때와 var 키워드를 사용해 함수 표현식을 작성할 때 던지는 에러도 다르다.


객체의 키 값에 함수가 들어 있을 때, 키 값을 조회하는 방식에서 새로운 부분이 있었다.
function funcDeclared() {
return 'this is a function declaration';
}
let funcExpressed = function () {
return 'this is a function expression';
};
const funcContainer = { func: funcExpressed };
funcContainer.func(); // 'this is a function expression'
값을 함수로 가지는 키 이름에 () 호출하는 표시를 붙이면 실행값을 가져오는 걸 알 수 있었다.
function shadowParameter(message) {
message = 'Do not use parameters like this!';
return message;
}
이렇게 매개변수에 실행 코드에서 값을 할당해버린 함수에 대해서 어떻게 생각하는지? 이 안에는 어떤 인자를 전달해도 결국 같은 메세지만 반복할 뿐이다! 그래서 메세지 또한 이런 식으로 사용하지 말라고 나오는 것 같다.
클로저는 함수와 함수가 선언된 어휘적 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
클로저 유즈 케이스 :
Q. 함수 안에 있는 변수는.. 그 함수가 실행되고 있다고 가정하고 그대로 따라가면 되는건가? 선언부 부분.. 실행문 부분. 그에 따른 값의 변화들. 조금 헷갈리지만 대충은 알 것 같다. (sprint 4번)
화살표 함수는 함수 표현식에서 function 키워드를 생략하고 화살표 =>를 붙인다.
const add = function (x, y) {
return x + y
}
=====function 키워드 생략하고 =>를 붙인다=====
const add = (x, y) => {
return x + y
}
=====중괄호와 return이 삭제 가능한 경우=====
const add = (x, y) => x + y
// 여기에서 파라미터가 하나인 경우 소괄호도 생략이 가능하다.
화살표 함수로 클로저 표현하기
const adder = x => {
return y => {
return x + y
}
}
adder(50)(10); // 60
======비교======
const subtractor = x => y => {
return x - y
}
const subtractor2 = subtractor(3);
subtractor2(1); // 2
const htmlMaker = tag => textContent => `<${tag}>${textContent}</${tag}>`
const liMaker = htmlMaker('li')
liMaker('hi');
// 외부함수에 인자 'li'가 전달된 채로 실행되고 종료가 되었다. 그리고 그 실행값(리턴값)인 내부 함수를 다른 변수에 담는다. 이 내부 함수는 외부 함수의 변수 tag에 접근할 수 있고 그 tag는 'li' 인자를 기억하고 있는 상태가 된다. 이렇게 생각하면 될까? limaker(); 이렇게 할 수 있는 건.. 생각해보면 위에 식은 함수 표현식이라고 볼 수도 있지 않을까?
원시 자료형은 값 자체에 대한 변경이 불가능하다. 배열이나 객체와 달리 원본을 수정할 수 없다는 뜻인 것 같다. 재할당은 가능해도!
변수에 값을 재할당하는 것과 값 자체의 변경은 다른 것이다. 값 자체를 변경할 수는 없어도 새로운 값으로 재할당은 가능하다.
let str = 'hello'
str.toUpperCase(); // 'HELLO'
console.log(str) // 'hello'
원시 자료형을 변수에 할당할 경우, 값 자체의 복사가 일어난다.
원시 자료형 또는 원시 자료형의 데이터를 함수의 인자로 전달할 경우, 값 자체의 복사가 일어난다. 이 말은 복사가 일어나므로 그 변수의 값 자체는 변경이 되지 않는다는 뜻...
let currentYear = 2020;
function afterTenYears(year) {
year = year + 10;
}
afterTenYears(currentYear);
function afterTenYears2(currentYear) {
currentYear = currentYear + 10;
return currentYear;
}
let after10 = afterTenYears2(currentYear);
자바스크립트에서 원시 자료형이 아닌 모든 것은 참조 자료형 입니다. 배열([])과 객체({}), 함수(function(){})가 대표적이다.
let number = 123;
const word = "hello";
let arr = [1, 2, 3];
const isEven = true;
[1, 2, 3]; // 이 데이터가 heap에 저장되지만 변수 할당이 되지 않아 주소는 어디에도 저장되지 않는다.
원시 자료형의 데이터가 저장되는 공간 (stack)
1 | number | 123
2 | word | "hello"
3 | arr | heap의 12번부터 3개 // (실제 데이터가 저장되어 있는 주소)
4 |isEven| true
=====================================
Object 자료형의 데이터가 저장되는 공간 (heap)
10 ||
11 ||
12 || 1
13 || 2
14 || 3
실제 자바스크립트는 변수를 위와 같이 저장한다.
const multiTypeArr = [
0,
1,
function () {
return 10
},
{ value1: 1, value2: 2},
[6, 7],
];
console.log(multiTypeArr[2]()) // 낯선 표기! 하지만 10
const arr = [1, 2, 3]
const poppedValue = arr.pop(); // 이 의미는 arr.pop(); 메소드를 실행하고 그 실행값을 변수에 담는다는 뜻이다.
console.log(poppedValue) // 3
console.log(arr) // [1, 2]
arr.slice()
slice() 메서드는 배열의 값을 복사해서 새로운 배열을 리턴한다.
const copiedArr = arr.slice(); // arr 배열을 복사해서 새로운 배열을 리턴한다. 여기에서도 데이터는 heap에 저장되고, heap의 주소를 copiedArr에 할당하는 거겠지.
const shiftedValue = arr.shift(); // shift() 메서드는 원본 배열을 수정하는 것이고, 실행값은 제거된 값이다.
const newArrLen = arr.unshift(3); // unshift() 메서드는 원본 배열을 수정하는 것이고, 실행값은 변경된 배열의 길이다. 따라서 arr.length + 1 값이 나올 것이다.
배열을 함수의 인자로 전달한 경우, 참조 주소가 전달된다.
let obj = {
greeting: function () {
return 'hello'
}
}
obj.name = 'suri' // 객체에 속성 추가
'name' in obj // true
obj.name = 'masuri' // 객체에 속성 변경
delete obj.name //객체에 속성 제거
obj.greeting() 이렇게 객체의 키를 불러올 수 있다. obj.greeting과의 차이는? 전자는 실행값을 불러오고, 후자는 함수 그 자체를 불러온다. 메서드는 어떤 객체의 속성으로 정의된 함수를 말한다. 위의 예에서 greeting은 obj 객체의 속성으로 정의된 함수인 메서드라고 할 수 있다. obj.greeting() 같은 형태로 사용할 수 있다.
this는 이 어떤 객체를 가리키는 키워드다. 


객체의 메서드를 정의하는 두 가지 형식
const members = {
manager : 'suri',
programmer: 'masuri',
showName: function () {
return this.manager + this.programmer;
},
greeting(number) {
return `There are ${this.manager} and` + ` ${this.programmer}`.repeat(number);
},
};
객체를 함수의 인자로 전달할 경우, 주소값(reference)이 전달된다. 따라서 함수 안에서 객체의 속성값을 변경할 경우, 원본 객체도 수정이 된다.
function test() {
'use strict';
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
// 깊은 복사
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
}
test();

const arr = [1, 2, [1, 2]];
const copied = arr.slice();
arr[2].push(3);
console.log(copied[2][2]) // 3
=======비교=======
arr[2] = [1, 2, 3, 4];
// 이렇게 한 경우에는 copied의 값이 바뀔까?
* reference site : 깊은 복사와 얕은 복사에 대하여
전개문법을 통해 배열과 객체를 풀어서 각각의 요소로 넣거나 병합할 수 있다.
const arr1 = [0, 1, 2]
const arr2 = [3, 4, 5]
const concat = [...arr1, ...arr2] // [0, 1, 2, 3, 4, 5]
const lover = {
name: 'teo',
age: 30,
};
const me = {
name: 'suri loves teo'
status: 'sleepy',
todos: ['study', 'sleep'],
};
const merged = { ...lover, ...me };
// {name: 'suri loves teo', age: 30, status: 'sleepy', todos: Array(2)}
me.todos[0] = 'hang out with friends';
me.todos[0] === merged.todos[0] // true
Rest Parameter는 함수의 인자를 배열로 다룰 수 있게 한다.

function getAllParamsByRestParameter(...args) {
return args;
}
function getAllParamsByArgumentsObj() {
return arguments;
}
========위의 두 함수를 비교========
const restParams = getAllParamsByRestParameter('first', 'second', 'third');
const argumentsObj = getAllParamsByArgumentsObj('first', 'second', 'third');
// 두 함수에 똑같은 인자를 전달해주었다. 실행값이 어떻게 달라지는지 비교해보자.
restParams // ['first', 'second', 'third']
Array.isArray(restParams) // true
======================
argumentsObj
// Arguments(3) ['first', 'second', 'third', callee: ƒ, Symbol(Symbol.iterator): ƒ]
typeof argumentsObj // 'object' (배열은 아님)
Object.keys(argumentsObj) // ['0', '1', '2']
// 키의 이름이 인덱싱으로 되어있다. 스트링 처리 안해준 것 주의!
Object.values(argumentsObj) // ['first', 'second', 'third']
======================
restParams === argumentsObj // false
const argsArr = Array.from(argumentsObj); // Array.from 메서드는 새로운 Array 객체를 만든다.
Array.isArray(argsArr) // true
argsArr // ['first', 'second', 'third']
argsArr === restParams // false
전개 문법으로 함수에 인자를 전달해주면 배열의 형태로 다룰 수 있게 한다는 건 이해가 된다.
그런데 return arguments는 배열이 아닌 객체로 읽히는데, 키의 이름이 인덱스로 되어 있고 배열 형태로 보여준다. 이게 유사 배열 객체인 것 같다.
Array.from() 메서드는 유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕게 복사해 새로운 Array 객체를 만든다.
true랑 false에 대한 deep equal은 의미가 없는 거 같다, 맨 뒤에서 이상하게 해석해서 문제를 틀린 것 같다.
'Rest Parameter는 인자의 수가 정해져 있지 않은 경우에도 유용하다.
function sum(...num) {
let sum = 0;
for (let i = 0; i < num.length; i++) {
sum += num[i];
}
return sum;
//return num;
// sum(1, 2, 3); => [1, 2, 3] 배열로 출력이 된다.
}
'Rest Parameter는 인자의 일부에만 적용 가능하다.
function getAllParams(arg1, arg2, ...args) {
return [arg1, arg2, args];
}
getAllParams(123); // [123, undefined, []];
rest/spread 문법을 이용해 배열을 분해한다.
const arr = ['hello', 'beautiful', 'world']
const [el1, el2] = arr;
console.log(el1) // 'hello'
console.log(el2) // 'beautiful'
====함수의 인자로 받을 때도 분해해서 받을 수 있다====
const newArr = [];
function sayHello([arg1, arg2]) {
newArr.push(arg1);
newArr.push(arg2);
return newArr;
}
sayHello(arr); // ['hello', 'beautiful']
객체의 단축 문법을 사용해 이미 할당된 변수를 그대로 가져와 값을 생략하고 쓸 수도 있다.
rest/spread 문법을 이용해 객체를 분해할 수 있다.
const student = { name: 'suri', major: 'education' }
const { name } = student // 배열을 분해하는 것과 같은 패턴임을 알 수 있다.
console.log(name) // suri
const me = { age : 30, address : 'mola'}
const {age, ...rest} = me
console.log(rest) // {address: 'mola'}