**expect(테스트하는 값). 기대하는 조건**
expect(isEven(3)).to be.true
expect(1+2).to.equal(3)
equal
은 두 값의 타입까지 엄격하게 같은지 검사한다(Strict equality)==
과 ====
==
(loose equality) : 두 값의 일치여부를 느슨하게 검사
**===**
(strcit equality) : 두 값의 일치여부를 엄격하게 검사
1 + '1' ; // '11' -> 숫자 + 문자 : 모두 문자로 취급
123 - '1' ; // 122 -> 문자 - 숫자 : 모두 숫자로 취급
1 + true ; // 2 -> 숫자 + boolean : true는 1, false는 0으로 숫자로 취급
'1' + true ; // '1true' -> 문자 + boolean : 모두 문자로 취급
const
로 선언된 변수엔 재할당(reassingment)이 금지된다.const constNum = 0;
constNum = 1; //불가
const
로 선언된 배열은 새로운 요소의 추가/삭제가 가능하다.const arr = []; //const로 선언된 배열
const number = 2;
arr.push(number);
arr[0] === 2
arr = [2] //새로운 요소의 추가,
arr.pop();
arr =[]; //삭제가 가능하다.
const
로 선언된 객체는 속성을 추가/삭제 할 수 있다.const obj = {x :1}
delet.obj.x;
obj === {}
obj = {x : 123} //const로 선언했기 때문에 -> 재할당은 불가(추가/삭제만 가능)
🛠️ **왜 재할당도 안되는 `const` 키워드를 굳이 써야 할까?**
🛠️ [**구글 자바스크립트 코딩 스타일 가이드**](https://google.github.io/styleguide/jsguide.html#features-use-const-and-let)
scope
: 변수의 값(변수에 담긴 값)을 찾을 때 확인하는 곳// 함수선언문
function foo() {
console.log('hello')' }
//함수표현식
var foo2 = function() {
console.log('hello2') }
}
let funcExpressed = 'to be a function';
function funcDeclared() {return 'this is a function declaration'}; //함수선언식
typeof funcExpressed === 'string'
typeof funcDeclared === 'function'
//
const funcContainer = { func: funcExpressed }; //함수표현식
funcContainer.func() === 'this is a function expression'
funcContainer.func = funcDeclared; //funcDeclared() : 함수선언식
funcContainer.func() === 'this is a function declaration'
🛠️ **함수선언과 함수표현의 차이**
var
let
const
function
let
cosnt
**var
로 선언한 변수 :** 호이스팅 시 undefined 로 변수를 초기화 (호이스팅 발생)//변수 호이스팅 : 변수 선언보다 호출이 먼저 됐을때 -> var
console.log(vari) //error가 아닌 undefined가 할당된다(초기화)
var vari = 'vari'
console.log(vari) //'vari'
**let
const
로 선언한 변수, 함수표현식 :** 호이스팅 시 변수 초기화되지 않고 에러 발생
→호이스팅이 일어나긴 하지만, TDZ(일시적 사각지대)에 머물게 되어 → 참조에러가 발생한다.
//변수 호이스팅 : 변수 선언보다 호출이 먼저 됐을때 -> let, const
console.log(vari) //error 발생
let(const) vari = 'vari'
변수 호이스팅의 결론
- 호이스팅의 대상은 선언문 상단에서 참조, 할당이 가능하다.
- 모든 변수 선언은 호이스팅이 발생한다.
- 그러나 var키워드로 선언한 변수만 → 호이스팅이 발생하는 것처럼 보인다.
**function**
함수선언문으로 정의한 함수 : 함수 호이스팅이 일어난다.참고자료
⇒ 하지만 호이스팅은 절대 좋은게 아니다 !!!
→ 왜나면 호출을 안했는데 호출이 되니까….
→ 따라서 <자바스크립트 학심 가이드>의 저자는 표현식을 권장한다….(JSON 만든 분)
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;
}
//문제
getMessage(); // 'outer'
shadowGlobal(); // 'Inner'
shadowGlobal2('Parameter'); // 'Parameter'
shadowParameter('Parameter'); // 'Do not use parameters like this!'
message; // 'outer'
function () {
function increaseBy(increaseByAmount) {
return function (numberToIncrease) {
return numberToIncrease + increaseByAmount;
}; }
const increaseBy3 = increaseBy(3);
const increaseBy5 = increaseBy(5);
//문제
increaseBy3(10); // increaseBy3(3)(10) = 13
increaseBy5(10); //increaseBy(5)(10) = 15
increaseBy(8)(6) + increaseBy(5)(9); // 14+14 = 28
참고자료
❌ 사실 잘 모르겠다 ㅠㅠㅠㅠ
function () { //외부 익명함수 안에 outerFn(),innerFn()내부 함수 존재
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;
} //여기까지가 outerFn 함수'
innerFn();
//문제1)
return age; // 26
return name; //'Jimin' outerFn()가 인접함수 임으로 여기서 선언된 값 가져옴
return innerFn;
}
const innerFn = outerFn(); // -> function innerFn()에 명시된 내용이 없어짐
});
});
//const innerFn = outerFn()됨에 따라 -> 함수가 아래와 같이 정리된다.
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()
//문제2)
age; // 27
name; // 'Jimin'
innerFn(); // 178
🛠️ **클로저란 ?**
클로저의 조건
함수가 → 함수를 리턴하는 형식이 나온다면 → 일단 클로저라고 의심!
그리고 리턴되고 있는 함수가 외부함수의 변수를 참조한다? → 이게 바로 클로저 !
클로저를 사용하는 이유
→ 변수를 안전하게 보호하기 위해(은닉)
→ 리턴된 함수를 사용하지 않으면 다른 요인으로 변수 못 바꾼다.
function 키워드를 생략하고 화살표 =>
를 붙인다.
리턴을 생략할 수 있다.
const subtract = (x, y) => x - y
const divideBy10 = x => x / 10 //소괄호 생략(파라미터 1개)
const multiply = (x, y) => (x * y) //소괄호 붙임(파라미터 2개)
const subtractor = x => y => { //외부x함수 안에 x-y값을 리턴하는 내부y함수(클로저)
return x - y } //리턴은 생략가능하다.
subtractor(50)(10); // 40
const htmlMaker = tag => textContent => `<${tag}>${textContent}</${tag}>
htmlMaker('div')('code states'); // '<div>code states</div>'
const liMaker = htmlMaker('li')
liMaker('1st item'); //'<li>1st item</li>'
**객체**
가 아니면서 method
를 가지지 않는 6가지 데이터를 말한다.string
, number
, bigint
, boolean
, undefined
, symbol
, (null)
Stack
: 원시자료형 데이터가 저장되는 공간let name = 'codestates';
name; // 'codestates'
name.toUpperCase(); // 'CODESTATES'
name; // 'codestates' -> 값이 변경되지 않는다.
name = name.toUpperCase();
name; // 'CODESTATES'
let overTwenty = true;
let allowedToDrink = overTwenty; // allowedToDrink = true;
overTwenty = false;
overTwenty; //false
allowedToDrink; //true -> allowedToDrink는 'overTwenty = true' 의 값 자체를 복사했기 때문에 재 할당해도 변하지 않는다.
let variable = 'variable';
let variableCopy = 'variableCopy';
variableCopy = variable;
variable = variableCopy;
variable; // 'variable'
let currentYear = 2020;
function afterTenYears(year) {
year = year + 10;
}
afterTenYears(currentYear); //2030
//문제1)
currentYear; // 2020
function afterTenYears2(currentYear) {
currentYear = currentYear + 10;
return currentYear; }
let after10 = afterTenYears2(currentYear);
//문제2)
currentYear; //2020
after10; //2030
});
배열[]
, 객체{}
, 함수function() {}
‘주소(refer)’를 할당하며, mutable(동적)이다.
Stack
: 원시자료형 데이터가 저장되는 공간
=================================
**heap
: Object 자료형 데이터가 저장되는 공간**
const obj = {};
Object.keys(obj).length; //0이다
obj['name'] = 'codestates';
obj.quality = 'best';
obj.product = ['sw engineering', 'product manager', 'growth marketing', 'data science'];
Object.keys(obj).length; //3이 된다.
delete obj.name;
Object.keys(obj).length //그리고 2로 변한다.
const overTwenty = ['hongsik', 'minchul', 'hoyong'];
let allowedToDrink = overTwenty; //참조자료형을 변수로 할당할 경우-> 참조자료형의 주소
overTwenty.push('san'); //가 저장된다.
allowedToDrink; // ['hongsik', 'minchul', 'hoyong', 'san']
overTwenty[1] = 'chanyoung';
expect(allowedToDrink[1]).to.deep.equal('chanyoung');
const ages = [22, 23, 27];
allowedToDrink = ages;
expect(allowedToDrink === ages); //true
expect(allowedToDrink === [22, 23, 27]); //false -> 주소가 다르다.
const person = {
son: {
age: 9, }, };
const boy = person.son;
boy.age = 20; //person.son.age = 20
expect(person.son.age); // 20
expect(person.son === boy); // true -> 주소가 동일하다.
expect(person.son === { age: 9 }); // false -> { age: 20 }이다.
expect(person.son === { age: 20 }); // 답은 맞지만 두 값의 주소가 다르다!
🛠️ `**.equal` 과 `.deep.equal`의 차이점은 ?**
const multiTypeArr = [
0,
1,
'two',
function () {
return 3;
},
{ value1: 4, value2: 5 },
[6, 7],
];
multiTypeArr[4].value1; //4
multiTypeArr[4].value2; //5
multiTypeArr[5][0]; //6
multiTypeArr[5][1]; //7
**.pop()
.push()
.shift()
.unshift()
.splice()
**.**slice()
.concat()
**arr = [1,2,3];
const poppedValue = arr.pop();
expect(poppedValue); // 3 -> arr.pop() 자체는 삭제된 요소 3이다
expect(arr); // [1, 2]
//.pop()은 원본 배열을 직접 변경하는 mutable method이다.
.**slice()
: immutable method****.slice( a )**
: ****a부터 끝까지 추출**.slice( a, b )
: a부터 b앞까지** 추출const arr = ['peanut', 'butter', 'and', 'jelly'];
expect(arr.slice(1)) // ['butter', 'and', 'jelly']
1)추출 시작점 = 추출 종료점이거나
expect(arr.slice(2, 2) // []
2)추출종료점이 0이거나
expect(arr.slice(3, 0)) // []
3)추출시작점이 엘리먼트보다 큰 경우
expect(arr.slice(5, 1) // // []
=> 빈배열을 반환한다.
4)엘리먼트가 0일 경우 -> arr 전체를 복사한다.
expect(arr.slice(0) // ['peanut', 'butter', 'and', 'jelly'];
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');
// arr = ['zero', 'changed in function', 'two', 'three', 'four', 'five']
const assignedArr = arr;
assignedArr[5] = 'changed in assignedArr';
expect(arr[5]).to.equal('changed in assignedArr');
//arr = ['zero', 'changed in function', 'two', 'three', 'four', 'changed in assignedArr']
const copiedArr = arr.slice();
//slice에 인자가 없으면 arr 전체를 반환 + slice는 immutable method로 원본 배열을 변경하지 않고 새로운 배열을 반환함.
copiedArr[3] = 'changed in copiedArr';
expect(arr[3]).to.equal('three'); //slice는 원본 배열을 변경하지 않기 때문에
//=> 원본arr는 변경되지 않는다.
// arr = ['zero', 'changed in function', 'two', 'three', 'four', 'changed in assignedArr']
const emptyObj = {};
emptyObj.length // undefined -> *0이 아님*
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);
//*왜 5가 아닌 undefined일까 ??? 찾아보기 !!
expect(megalomaniac.mastermind).to.equal('Joker');
expect(megalomaniac.henchwoman).to.equal('Harley'); //w가 소문자
expect(megalomaniac.henchWoman).to.equal(undefined); //w가 대문자 -> 선언된게 없다
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');
const megalomaniac = { mastermind: 'Agent Smith', henchman: 'Agent Smith' };
expect('mastermind' in megalomaniac); //true;
expect('secretary' in megalomaniac) // false;
this
는 method가 호출되는 시점에 결정된다.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(new Date().getFullYear());
expect(megalomaniac.calculateAge(currentYear)).to.equal(52);
//2022 - 1970
megalomaniac.birthYear = 2000;
expect(megalomaniac.calculateAge(currentYear)).to.equal(22);
//2022 - 2000
megalomaniac.changeBirthYear(2010);
expect(megalomaniac.calculateAge(currentYear)).to.equal(12);
//this.birthYear = 2010 => 2022 - 2010
🛠️ `**this` 란 ?**
this
의 값은 함수를 호출하는 방법에 의해 결정된다. 또 엄격모드와 비엄격모드에도 차이가 있다.this
가 없다. → 따라서 화살표 함수 바로 바깥범위의 this
를 찾는다this
는 자신을 감싼 정적 범위(lexical context)이다.🛠️ `**apply` , `call` ,`bind`란?** 🛠️ **일반 변수 조회 규칙이란 ?**참고자료 : MDN 정의
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]);
//*함수의 전달인자로 object가 전달되어 값이 아닌 -> 주소가 전달되었다. 즉 같은 주소를 공유하게 된다(주소 복사)
//이때문에 함수를 통해 객체의 value 값을 바꿀 경우 -> 원본 value의 값도 변한다(같은 주소를 공유하고 있기 때문이다)
.**assign
을 통한 복사(주소가 아닌 새로운 객체로 복사)**
**object.assign(목표객체, 출처객체)**
: 출처객체의 속성을 복사해 →목표객체에 반환한다.
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); // *하지만 assign()은 딱 1depth까지만 깊은 복사가 된다.
.assign()
깊은복사의 한계.assign()
을 통해 주소복사 가 아닌 → 새로운 객체로 복사가 가능하다.
하지만 .assign()
의 한계는 딱 1 depth 까지만 복사 = 즉, 중첩함수는 복사가 되지 않는다.
delete obj.twins['Jared Leto'];
expect('Jared Leto' in copiedObj.twins).to.equal(false);
//1)전달인자가 많이 주어졌다면,
function returnFirstArg(firstArg) {
return firstArg;
}
}
expect(returnFirstArg('first', 'second', 'third')).to.equal('first');
//함수 주어진 파라미터만큼만 리턴한다.
step1. arguments와 rest parameter를 통해 배열 만들기
//1)**rest parameter를 통해 함수의 전달인자를 배열로 만들어 준다.-> parameter는 배열이다.**
// ->rest Parameter는 spread syntax를 통해 간단하게 구현된다.
function getAllParamsByRestParameter(...args) {
return args }
**//2)** arguments객체를 통해 배열 만들기 **-> arguments객체는 유사배열객체이다**
function getAllParamsByArgumentsObj() {
return arguments;
}
const restParams = getAllParamsByRestParameter('first', 'second', 'third');
const argumentsObj = getAllParamsByArgumentsObj('first', 'second', 'third');
step2. 배열 메서드로 활용하기
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']);
대표적인 배열의 메서드
Object.keys(obj)
: 객체의 키만 담은 배열을 반환한다.
**Object.values(obj)**
: 객체의 값만 담은 배열을 반환한다.
**Object.entries(obj)**
: 객체의 [키:값] 쌍을 담은 배열을 반환한다.
step3. 진짜배열과 유사배열이 된 전달인자(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); //**parameter**로 만든 배열은 **진짜배열이다.**
expect(Array.isArray(argumentsObj)).to.deep.equal(false) /**/argumen**t로 만든 배열은 **유사배열객체**이다.
**//1)직접 배열 리터럴로 선언**
let array = [1,2,3]
**//2)다른 방법으로 배열 만들기**
let nods = document.querySelecorAll('div')
let els = document.body.children;
function arraylike (..arr) {
return arr
}
//3)Array.isArray로 확인하기
Array.isArray(array) ;//true
Array.isArray(nods) ;//false
Array.isArray(els) ;//false
Array.isArray(arraylike) ;//false
**array.from**
을 활용하자**Array.from()**
: 유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)을 얕게 복사해 새로운 Array 객체를 만든다.
방법 : 유사 배열 객체로부터 배열을 만들고 싶다면 → value만 복사해 만든다.
const argsArr = Array.from(argumentsObj) //Array.from을 통해 유사배열객체를 새로운 배열 객체로 만들어준다.
expect(Array.isArray(argsArr)).to.deep.equal(true); //그럼 새롭게 만든 배열은 배열type으로 취급된다.
expect(argsArr).to.deep.equal(['first', 'second', 'third']);
expect(argsArr === restParams).to.deep.equal(false); //Array.from을 통해 새로운 배열이 되었기 때문에 기존 주소와 일치하지 않는다.
//1)기본 변수 할당
var foo = ["one", "two", "three"];
var [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
//2)선언에서 분리한 할당 : 변수의 선언이 분리되어도 구조 분해를 통해 값을 할당할 수 있다.
var a, b;
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
//3. 기본값 : 변수에 기본값을 할당하면, 분해한 값이 undefined일 때 -> 그 값을 대신 사용한다.
var a, b;
[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7
💡 const [first, ...middle, last] = array -> 즉 rest 문법은 마지막 인수로만 올 수 있다.