객체가 아니면서 메소드가 없는 string, number, boolean, symbol, bigint, null, undefined 7가지 데이터 타입
(단, typeof null === 'object')
-cf) symbol 유형 : 병결불가능한 원시 타입의 값 -> 이름이 충돌할 위험이 없는 객체의 유일한 프로퍼티키를 만들기 위해 사용!
stack!
heap
라고도 부름, 참조(reference) 타입임.Example 1) 참조자료형의 비교
[] === [] // false
{} === {} // false
왜 false가 뜰까? 참조자료형에서 비교가 되는것은 배열의 내용이 아니라 개체 참조이기 때문! -> 동일한 개체 인트던스 아님!
Example 2) 참조자료형을 원시자료형의 형태를 써서 바꿀 수 있을까?
let x = [1, 2, 3, 4];
let y = x;
y = 2 //
console.log(x) // 고대로 [1, 2, 3, 4]
원시자료형으로 접근해봤자 영향 XXXX
def) 식별자가 유효한 유효범위(우효~)
쉐도잉
Example 3) let이 있고 없고의 차이가 이렇게 큽니다!
let name = 'a';
function() {
let name = 'b';
console.log(name);
}
console.log(name) // 'a'
function() // 'b' 바꿨네
console.log(name) // 'a' 안쪽 지역변수가 바깥쪽에 영향 x
let name = 'a';
function() {
name = 'b'; // ***
console.log(name);
}
console.log(name) // 'a'
function() // 'b' 바꿨네
console.log(name) // 'b' 계속 바뀌네? 밑에는 ***식에 let이 없음. 즉 전역변수를 계속 사용,수정할거
let이 없으면 전역변수를 그대로 가져다 쓴다!
cf) 화살표함수는 블록스코프
임!
Example 4) user에서 세번째 줄 } 까지 블록 스코프!
let getAge = user => {
return user.age;
}
var : 블록스코프를 무시하고 함수스코프만 따름.
단, 화살표함수의 블록스코프는 무시하지 않음.
함수스코프의 최상단에 선언
const : 값이 변하지 않는 상주정의할때 씀. 블록스코프 따름.
ex) var console; 이렇게 함으로써 console.log 작동 오류
intro) JS에는 클로저라는 개념이 있음!
def) by mdn ... 함수와 그 함수가 선언된 렉시컬 환경과의 조합
이다. 뭔 개소리?
우선 렉시컬이 뭔지 보자
Example 5) 렉시컬 스코프(정적 스코프)와 동적 스코프의 차이
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // ?
bar(); // ?
위의 결과는 놀랍게도 1을 두 번 호출하는데 이는 JS가 렉시컬 스코프를 따르기 때문이다
동적 스코프
이다.정적 스코프
)이다.bar함수는 어쨋든 전역에서 정의된 함수이므로 전역 스코프를 기억함.
함수가 호출될 때마다 함수의 상위 스코프를 참조할 필요가 있기 때문. -> 즉 상위 스코프를 기억
Example 6) 클로저 그게 뭔데
const x = 1;
// 1.
function outer() {
const x = 10;
const inner = function () { console.log(x); }; //2.
return inner;
}
//outer 함수를 호출하면 중첩 함수 inner를 반환한다.
//그리고 outer 함수의 실행 컨텍스트는 실행 컨테스트 스택에서 팝되어 제거된다.
const innerFunc = outer(); // 3.
innerFunc(); // 4. 10 not a 1
외부 함수(outer)보다 중첩 함수(inner)가 더 오래 유지되는 경우
이러한 중첩 함수를 클로저라고 부른다.
Q. 아니근데 왜 이런현상이 일어날까요??
A. 그냥 함수안에 함수가 있다고 클로저가 아님! 상위 스코프의 식별자를 참조
(const x = 10)해야 그대는 진정한 클로저!
Example 7) 클로저 특
const adder = function (x) {
return function (y) {
return x + y;
}
}
console.log(adder(5)(7)); //12
외부함수는 y에 접근 불가하지만 내부함수는 선언된 변수x에 접근 가능!
함수내부의 변수를 재 사용!.
일반적인 함수는 실행이 끝나면 함수내부의 변수사용이 불가하다.
Example 8-1) 내부변수 사용 ㄴㄴ
function whereIsX() {
let x = 3;
return x;
}
console.log(x); // x is not defined
Example 8-2) 클로저는 다릅니다!
const adder = function (x) {
return function (y) {
return x + y;
}
}
const add5 = adder(5); // 5라는 값을 x에 계속 담은채로 있음!
Example 9-1) 이건 좀 아니지 않나요?
let num = 0;
const increase = function () {
return ++num;
};
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
저렇게 해도 되지만 오류가 발생할 수 있음. 왜냐?
카운트 상태(num 변수 값) 는 increase 함수가 호출되기 전까지 변경되지 않고 유지되어야 한다.
이를 위해 카운트 상태는 increase 함수만이 변경할 수 있어야함!
Q. num을 조금 더 안전하게 보관할 수 있으면 좋겠다!
Example 9-2) 클로저를 이용해 조금 더 세이프티하게 저장!
const increase = (
function () {
let num = 0;
return function () { //요게 클로저
return ++ num; // 상위 스코프의 식별자 num 사용!
};
}
());
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
Example 10) HTML 문자열 생성기
const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
const divMaker = tagMaker('div'); // tag 변수에 div 저장함!
console.log(divMaker('hello')); // <div>hello</div>
응용해보셈!
Example 11-1) 클로저 모듈 패턴
const makeCounter = () => {
let value = 0;
return {
increase: () => {
value += 1;
},
decrease: () => {
value -= 1;
},
getValue: () => value
}
}
const counter1 = makeCounter();
counter1 // {increase:f, decrease:f, getValue:f} 함수여러개를 포함한 객체가 됨.
Example 11-2) 클로저 모듈 패턴의 활용(11-1 예제에 이어서 작성)
counter1.increase();
counter1.increase();
counter1.increase();
console.log(counter1.getValue()); // 3이 됨
const counter2 = makeCounter();
counter2.decrease();
counter2.getValue(); // -1이 됨
counter1의 value와 counter2의 value가 서로 영향 안 줌!