원시자료형(primitive data type) : 고정된 저장공간을 차지하는 데이터로, Method를 가지지 않는다.
- 하나의 변수에 = 하나의 원시 자료형 데이터만 담을 수 있다.
number
,string
,boolean
,undefined
,symbol
,bigint(길이의 제약 없이 정수를 다룰 수 있게 해주는 숫자형)
,(null)
- 기존 데이터의 값(value)을 복사/변경 → 기존 데이터가 변경되지 않음
- but, 변수에 다른 데이터 할당은 가능
💡 `**let`과 `const`의 차이점**참조자료형(reference data type) : 동적인 저장공간을 차지하는 데이터로 → 대량에 데이터를 다루기에 적합
- 하나의 변수에 = 여러 데이터가 담긴다. → 동적인 데이터 보관함을 heap이라고 부른다.
array(배열)[]
,object(객체){}
,funcion(함수)(){}
- 데이터의 주소(reference)를 복사/변경 → 기존데이터가 변경된다.
let word = "hello world!"
word = "hello codestates!"
// let : 키워드 선언 후 → **재할당 가능**
const num1 = 123;
num1 = 123456789; // 에러 발생
// const : 키워드 선언 후 → **재할당 불가능**
: 변수에 접근할 수 있는 범위(변수의 유효범위)
스코프를 나누는 유형
- 중괄호
- 중괄호 안쪽(안쪽 스코프)에 변수가 선언 or 중괄호 바깥(바깥쪽 스코프)에 변수가 선언되었는가
- 함수
- 변수가 함수 안쪽 or 바깥쪽에 선언되었는가
1️⃣ **변수 접근 규칙에 따른 유효범위**스코프의 규칙
스코프의 종류
- 블록 스코프(block Scope) :
**중괄호{}**
를 기준으로 범위가 구분된다.- 함수스코프(function Scope) :
**함수 function**
키워드가 등장하는 함수선언식/함수표현식으로 범위 구분❗️같은 함수여도, 화살표함수를 사용하면 ⇒ 블록 스코프로 취급
let getAge = user => { //화살표 함수 -> 블록스코프 return user.age; }
let
, const
, var
차이점for(___ i = 0 ; i < 5; i++) {
console.log(i); /i가 5번 반복
}
console.log('final i:', i); //결과는 ??
1️⃣ __이 let
일 경우
for(**let** i = 0 ; i < 5; i++) {
console.log(i); /i가 5번 반복
}
console.log('final i:', i); //ReferenceError
2️⃣ __이 var
일 경우
for(**var** i = 0 ; i < 5; i++) {
console.log(i); /i가 5번 반복
}
console.log('final i:', i); //5
👉 **답은 `let` 이다.**그렇다면
let
과var
중 무엇이 더 권장될까?
**let키워드**
가 훨씬 더 예측 가능하다.**let키워드**
는 재선언을 방지 → 버그 실수를 막아준다.3️⃣ const 키워드
: 갑이 변하지 않는 상수(Constant)를 정의할때 사용
**let 키워드**
와 동일)**const 키워드**
사용 권장!let | var | const | |
---|---|---|---|
유효범위 | 블록스코프 함수스코프 | 함수스코프 | 블록스코프 함수스코프 |
값 재할당 | 가능 | 가능 | 불가능 |
재선언 | 불가능 | 가능 | 불가능 |
: 브라우저 창을 대표하는 객체이다. 그러나 브라우저 창과 관계없이 전역 항목도 담고 있다.
var
로 선언된 전역 변수 및 전역 함수 → window객체에 속한다.var myname = '김코딩';
console.log(window.myname); //김코딩
function foo() {
console.log('bar');
}
console.log(foo === window.foo); //true
// -> var로 선언된 전역변수와 전역함수가 window 객체에 속한다.
1️⃣ 전역 변수를 최소화해라
부수효과(side effect)이 발생 하기 때문이다 : 다른 함수, 로직에 의해 의도치않은 변경 발생 가능성
2️⃣ **let
, const
를 주로 사용하자!**
3️⃣ 선언 없는 변수 할당 금지!
선언 없이 변수를 할당 → 해당 변수는 var로 선언한 전역 변수처럼 취급 된다!
‘Use Strict’;
이라고 입력(따옴표 포함): 외부 함수의 변수에 접근할 수 있는 내부함수
💡 **자바스트립트의 특이점**클로저 함수의 특징
- 함수를 리턴하는 함수
const adder = x => y => x=y; // 함수 호출이 두번 발생하는 함수
add(5)(7);
//=위 코드와 동일하게 작동하는 코드
const adder = function(x) { //function(x)는 또 다른 함수인 function (y)를 리턴
return function (y) { //즉 adder 함수는 클로저 함수이다.
return x + y;
}
}
const adder = function(x) {
return function (y) {
return x + y;
}
}
외부함수 function(x) → 내부함수 function(y)에 접근 불가능❌
⇒ 바깥스코프 → 안쪽 스코프 접근 불가 ❌
내부함수 function(y) → 외부함수 function(x) 에 접근 가능 ⭕️
⇒ 안쪽스코프 → 바깥스코프에서 선언한 변수에 접근 가능 ⭕️
클로저의 장점 및 활용
- 데이터를 보존하는 함수
- 외부 함수(adder)의 실행이 끝나도 → 외부 함수 내 변수(x) 사용 가능
const adder = function(x) {
return function(y) {
return x + y ;
}
}
const add5 = adder{5} ;
add5(7) //12
add5(10) //15
//일반적인 함수는 함수 실행이 끝나면 함수 내부의 변수 사용이 불가하다.
//하지만 클로저 함수인 adder 함수의 실행이 끝나도 x 변수에 5 값을 담은 채 남아있다.
// => 이는 클로저가 어휘적 환경을 메모리에 저장하기 때문이다.
const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
const = divMaker = tagMaker('div);
//divMaker 함수는 'div'문자열을 'tag'라는 변수에 담아두고 있다.
divMaker('hello'); // '<div>hello</div>'
divMaker('code');; // '<div>code</div>'
const anchorMaker = tagMaker('a');
//anchorMaker 함수는 'a'라는 문자열을 tag라는 변수에 담아두고 있다.
anchorMaker('go'); // '<a>go</a>'
anchorMaker('you');// '<a>you</a>'
const makecounter = () => {
let value = 0;
return { //makercounter함수는 내부함수 여러개를 포함한 객체 값을 리턴한다.
increase : () => {
value = value + 1
},
decrease : () => {
value = value - 1
},
getvalue : () => value
}
}
const counter1 = makecounter();
counster1 // { increacs : f, decrease : f, getvalue :f }
💡 **클로저의 캡슐화 (정보의 접근 제한)**
let value
변수를 makecounter 함수가 보존하고 있기에 → 스코프 규칙에 의해 value 변수에 새로운 값 할당 불가능❌ ⇒ 따라서 리턴하는 객체가 제공하는 매소드를 통해서만 변경 가능//counter1과 counter2의 value는 서로 영향 끼치지 X
// -> 함수를 완전히 독립적인 부품으로 분리하는 모듈화를 했기 때문에 !!!
const counter1 = makercounter();
counter1.increase(); //1
counter1.increase(); //2
counter1.decrease(); //1
counter1.getvalue(); //1
const counter2 = makercounter();
counter2.decrease(); //-1
counter2.decrease(); //-2
counter2.decrease(); //-2
counter2.getvalue(); //-3
//클로저 함수인 makercounter()함수를 캡슐화 -> 변수 value를 makercounter()함수로 보존 -> 전역 변수로 인한 side effect을 방지한다.
seeYet 함수
가 리턴하고 있는 익명함수seeYet 함수
가 리턴하고 있는 익명함수 : 외부함수 seeYey의 스코프에 선언된 변수 archive에 접근 할 수 있는 클로저이다. let seenYet = function() {
let archive = {};
return function(val) {
if (archive[val]) {
return true;
}
archive[val] = true;
return false;
}
}
let add = function(x) {
let sum = function(y) {
return x + y;
}
return sum;
}
let foo = add(1);
foo(3); // add(1)(3)으로 4가 반환되지만 어떤 변수에도 할당되지않아 total에 영향 X3.
let total = foo(6); // = add(1)(6)과 같으므로 1+6 = 7이 정답
→ 리턴 함수가 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);
var a = 0; //전역변수(글로벌변수)
function foo() {
var b = 0; //지역변수(로컬변수)
return function() {
console.log(++a, ++b);
};
}
var f1 = foo();
var f2 = foo();
f1(); // --> A : 1 1
f1(); // --> B : 2 2
f2(); // --> C : 3 1
f2(); // --> D : 4 2
//해답
//지역변수 b는 함수안에 선언 -> f1(), f2()실행할 때 각각 카운트가 올라감
//전역변수 a는 함수 밖에 선언 -> f1(), f2() 중 어떤것을 실행해도 카운트가 올라감
<!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 () {
// TODO: ③ isShow 변수의 상태를 변경하는 코드를 작성하세요.
};
})();
// ② 이벤트 프로퍼티에 클로저를 할당
toggleBtn.onclick = toggle;
</script>
</body>
</html>
//1. 클로저를 반환하는 함수
return function() {
box.style.display = isShow ? 'block' : 'none' ;
//3. 변수 상태 변경
isShow = !isShow;
};