원시 자료형(primitive data type) : 객체가 아니면서 메서드를 가지지 않는 타입. 각 변수 간 데이터 값을 복사하기 때문에 기존 데이터에는 영향이 가지 않는다. (string, number, bigint, boolean, undefined, symbol, null)
참조 자료형(reference data type) : 원시 자료형이 아닌 모든 것은 참조 자료형. 값 대신 저장소의 주소를 복사하여 참조하기 때문에 기존 데이터에도 영향이 간다. (배열, 객체, 함수)
===
(strict equality) : 참조 자료형에서 ===는 주소값이 같은지를 확인한다.console.log('hi' === 'hi'); // true
console.log(3.14 === 3.14); // true
console.log([1,2,3] === [1,2,3]); // false
console.log({ name: 'hazel' } === { name: 'hazel' }); // false
변수가 많아질 때, 찾고 삭제하는 활동은 비효율적이다.
➡️ 힙이라는 빈 공간을 두어 변수에 값 대신 힙 영역의 주소를 넣어 관리한다. (원소가 많아져도 하나의 주소지에서 삽입, 삭제 등의 연산을 할 수 있다.)
배틀 그라운드에서 목표물을 정확히 조준하기 위한 아이템인 스코프가 떠오른다!
// 바깥쪽 스코프
let username = 'hazel';
if (username) { // 안쪽 스코프
let message = `Hello, ${username}!`;
console.log(message); // Hello hazel!
}
console.log(message); // ReferenceError
let name = 'hazel';
function showName() {
let name = 'daisy'; // 지역 변수
console.log(name); // 두 번째 출력
}
console.log(name); // hazel
showName(); // daisy
console.log(name); // hazel
// 블록 스코프
let getAge = user => {
return user.age;
}
// 함수 스코프
let getAge = function (user) {
return user.age;
}
var
: 블록 스코프를 무시하고 함수 스코프만 따르며 재선언을 해도 에러가 나지 않음. (선언 키워드 없으면 var로 취급, 화살표 함수의 블록 스코프는 무시하지 않음)let
: 블록 단위로 스코프를 구분했을 때, 훨씬 예측 가능한 코드 작성이 가능하며 재선언을 방지해서 권장되는 키워드이다.const
: 블록 스코프를 따르며 값이 변하지 않는 상수를 정의할 때 쓰는 키워드로 재할당이 불가능하다. (의도하지 않은 값의 변경을 방지하며, 재할당 시 TypeError 발생)'use strict';
: Strict Mode는 브라우저를 엄격하게 작동하도록 만들어 에러를 알려준다. // var의 특성
for(var i = 0; i < 5; i++) {
console.log(i); // 5번 출력
}
console.log('final i: ', i); // 5
// var, let 재선언 비교
var myName = 'hazel';
var myName = 'hazel'; // 아무 에러가 나지 않음
let myName = 'hazel';
let myName = 'hazel'; // SyntaxError 발생
// const 재할당
const pi = 3.14;
pi = 3.1415; // TypeError 발생
let | const | var | |
---|---|---|---|
유효 범위 | 블록 스코프 및 함수 스코프 | 블록 스코프 및 함수 스코프 | 함수 스코프 |
값 재할당 | 가능 | 불가능 | 가능 |
재선언 | 불가능 | 불가능 | 가능 |
var myName = 'hazel';
console.log(window.myName); // hazel
function foo() {
console.log('bar');
}
console.log(foo === window.foo); // true
정의 : 함수와 함수가 선언된 어휘적(lexical) 환경(변수 및 함수 선언의 형태)의 조합, 외부 함수의 변수에 접근할 수 있는 내부 함수, 자신이 선언될 때의 맥락을 기억하는 함수
특징 :
// 1. 화살표 함수를 이용한 덧셈 함수
const add = (x, y) => x + y;
add(5, 7);
// 2. 함수 호출이 두 번 발생하게 구현
const adder = x => y => x + y;
adder(5)(7); // 12
typeof adder(5); // 'function'
adder(5); // y => x + y
// 3. 클로저 함수의 기본 형태: 함수를 리턴하는 함수 (2번과 동일하게 작동)
const adder = function (x) { // 외부 함수
return function (y) { // 내부 함수, 리턴값이 함수의 형태
return x + y;
}
}
// 4. 데이터를 보존하는 함수: 함수 실행이 끝나도 어휘적 환경이 저장되어 계속해서 변수 사용 가능
const adder = function (x) {
return function (y) {
return x + y;
}
}
const add5 = adder(5);
add5(7); // 12
add5(10); // 15
// 5. HTML 문자열 생성기 (실용적인 예제)
const tagMaker = tag => content => `<${tag}>${content}</${tag}>`;
const divMaker = tagMaker('div');
divMaker('hello'); //'<div>hello</div>
const anchorMaker = tagMaker('a');
anchorMaker('nice'); //'<a>nice</a>'
// 6. 클로저 모듈 패턴 (정보의 접근 제한)
const makeCounter = () => {
let value = 0;
return {
increase: () => { // 내부 함수를 여러 개 만들 수 있음
value = value + 1
},
decrease: () => {
value = value - 1
},
getValue: () => value // 객체에 담아 여러 개의 내부 함수 리턴 가능
}
}
// 모듈화: 독립적인 부품 형태로 분리하여 함수 재사용성 극대화
const counter1 = makeCounter(); // { increase: f, decrease: f, getValue: f }를 리턴하여 counter1은 함수 여러개를 포함 한 객체이다.
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue(); // 1
const counter2 = makeCounter(); // 독립적으로 value를 가지게 됨 (서로에게 영향을 끼치지 않음)
counter1.decrease();
counter2.decrease();
counter1.decrease();
counter2.getValue(); // -3
// 1번
let x = 10;
function outer () {
x = 20;
function inner () {
let x // undefined
x = x + 20; // undefined + 20 = NaN
return x;
}
inner();
}
outer();
let result = x;
// 2번: seenYet이 리턴하고 있는 익명 함수가 클로저로 간주된다.
let seenYet = function() {
let archive = {};
return function(val) {
if (archive[val]) {
return true;
}
archive[val] = true;
return false;
}
}
// 3번
let add = function(x) {
let sum = function(y) {
return x + y;
}
return sum;
}
let foo = add(1); // 1 + y
foo(3); // 할당이 되지 않아 아무런 영향을 끼치지 않음
let total = foo(6); // 1 + y
// 4번: 리턴 함수가 x에 접근할 수 있기 때문에 multiplyByX만 클로저를 사용하고 있다고 볼 수 있다.
let multiplyByX = function(x) {
return function(y) {
return x * y;
}
}
let multiplyBy5;
multiplyBy5 = multiplyByX(5);
multiplyBy5(4);
/*******************************/
let multiplyByFive = function() {
return function(y) {
return 5 * y;
}
}
let multiplyBy5
multiplyBy5 = multiplyByFive();
multiplyBy5(4);
// 5번
var a = 0;
function foo() {
var b = 0;
return function() {
console.log(++a, ++b);
};
}
var f1 = foo();
var f2 = foo();
f1(); // --> 1 1
f1(); // --> 2 2
f2(); // --> 3 1
f2(); // --> 4 2
// 6번
<!DOCTYPE html>
<html>
<body>
<button class="toggle">toggle</button>
<div class="box" style="width: 100px; height: 100px; background: red;"></div>
<script>
var box = document.querySelector('.box');
var toggleBtn = document.querySelector('.toggle');
var toggle = (function () {
var isShow = false;
// TODO: ① 클로저를 반환하는 함수를 작성하세요.
return function () {
box.style.display = isShow ? 'block' : 'none';
// TODO: ③ isShow 변수의 상태를 변경하는 코드를 작성하세요.
isShow = !isShow;
};
})();
// ② 이벤트 프로퍼티에 클로저를 할당
toggleBtn.onclick = toggle;
</script>
</body>
</html>
...
로 사용하며, 배열이나 문자열과 같이 반복 가능한 문자를 인수나 요소로 확장하여 키-값 쌍의 객체로 확장시킬 수 있다. 기존 배열은 변경하지 않는다. (immutable)function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
sum(...numbers) // 6
// rest 문법
function sum(...theArgs) {
// reduce(accumulator, currentValue)
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
sum(1,2,3) // 6
sum(1,2,3,4) // 10
// 배열 합치기
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2]; // spread 문법은 기존 배열을 변경하지 않으므로(immutable), arr1의 값을 바꾸려면 새롭게 할당해야 함
// 배열 복사
let arr = [1, 2, 3];
let arr2 = [...arr]; // arr.slice()와 유사
arr2.push(4); // [1, 2, 3, 4]
// 객체 합치기
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
let clonedObj = { ...obj1 }; // {foo: 'bar', x: 42}
let mergedObj = { ...obj1, ...obj2 }; // {foo: 'baz', x: 42, y: 13}
// 함수에서 나머지 매개변수 받아오기
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a);
console.log("b", b);
console.log("manyMoreArgs", manyMoreArgs);
}
myFun("one", "two", "three", "four", "five", "six");
// 퀴즈 1번
let arr = [10, 30, 40, 20]
let value = Math.max(arr) // NaN (배열로 전달했기 때문)
let arr = [10, 30, 40, 20]
let value = Math.max(...arr) // 40 (요소들을 펼쳐서 실행)
// 퀴즈 2번
let arr = ['code', 'states']
let value = [
...arr,
'pre',
...['course', 'student']
]
// ['code', 'states', 'pre', 'course', 'student']
// 배열
const [a, b, ...rest] = [10, 20, 30, 40, 50];
// {0: 10, 1: 20, 2: 30, 3: 40, 4: 50}
// 객체
const {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
// {a: 10, b: 20, c: 30, d: 40}
// 함수에서 객체 분해
function whois({displayName: displayName, fullName: {firstName: name}}){
console.log(displayName + " is " + name);
}
let user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
whois(user) // jdoe is John
정의 : 인터프리터가 변수와 함수의 메모리 공간을 선언하기 전에 미리 할당하는 것을 의미한다. (변수의 선언과 초기화를 분리한 후 선언만 코드의 최상단으로 옮기는 행위)
// 함수 선언식은 호이스팅에 영향을 받지만, 함수 표현식은 호이스팅에 영향을 받지 않음.
let funcExpressed = 'to be a function';
expect(typeof funcDeclared).to.equal('function'); // 함수 선언식은 호이스팅으로 인해 'function'으로 리턴
expect(typeof funcExpressed).to.equal('string'); // 함수 표현식은 영향을 받지 않아 '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; // const 재할당은 불가하지만, Object에 key-value 추가 및 변경은 가능 (바인딩 되는 값이 아니기 때문)
expect(funcContainer.func()).to.equal('this is a function declaration');
함수 표현식을 사용하는 것을 권장한다. 호이스팅이 일어나는 상황을 만들지 마라. - 더글라스 크락포드
const obj1 = { a: 1, b: 2};
const obj2 = obj1;
console.log( obj1 === obj2 ); // true
obj2.a = 100;
console.log( obj1.a ); // 100
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); // 객체를 함수의 인수로 전달
obj.henchwoman; // 'Adam West'
const assignedObj = obj;
assignedObj['relations'] = [1, 2, 3];
obj['relations']; // [1, 2, 3]
const copiedObj = Object.assign({}, obj);
copiedObj.mastermind = 'James Wood';
obj.mastermind; // 'Joker'
obj.henchwoman = 'Harley';
copiedObj.henchwoman; // 'Adam West'
delete obj.twins['Jared Leto'];
expect('Jared Leto' in copiedObj.twins).to.equal(false);
페어분과 함께 네비게이터와 드라이버를 번갈아 수행하였는데, 빈칸에 들어갈 것으로 옳은 정답이 무엇인지 서로 토론하고 테스트를 통과하는 과정들이 마치 퀴즈 맞추는 게임을 하는 것 같아서 재미있었다! 또한, 디테일하게 알지 못했던 부분을 JavaScript Koans를 작성하는 것을 통해 알게 되어 뜻깊은 시간이었다.
반복 학습이 매우 중요한데, 나는 지나간 것에 관심을 쏟기가 어려운 것 같다. 기본기 중요하니까 관심을 조금이라도 가지고 공부하자...젭알