[JS] 핵심 개념 및 문법(원시-참조자료형/스코프/클로저)
Categories: javascript
📌 개인적인 공간으로 공부를 기록하고 복습하기 위해 사용하는 블로그입니다.
정확하지 않은 정보가 있을 수 있으니 참고바랍니다 :😸
[틀린 내용은 댓글로 남겨주시면 복받으실거에요]
원시자료형과 참조자료형
원시자료형 VS 참조자료형
JavaScript에서 자료형(type)이란 값(value)의 종류
자료형은 크게 두 가지로 구분
원시 자료형(primitive type)과 참조 자료형(reference type)
- 원시자료형
- Number, boolean, undefined, null, string, symbol이 여기에 해당됨.
- 원시 자료형은 변경 불가능한 값(immutable value) 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값이다.
- stack
-
변수 선언 시 stack에 할당 됨.
-
변수에 값을 할당하면 값이 저장됨. 변수에 할당하면 메모리 공간에 값 자체가 저장
-
원시형 stack에 할당
-
원시형을 재할당 할 경우 아래와 같이 변수와 값은 다른 곳에 저장되고 기존 변수는 사라지고 값만 남게 됨
- 참조할 변수가 없어서 값을 불러낼 수 없음. 사용하지 않아서 나중에 가비지컬렉터에 의해 삭제 됨.
-
원시값 자체 복사
1 2
let num = 20; let copiedNum = num;
- 원시 자료형은 값 자체가 복사됨. 즉, 변수
num
과 변수copiedNum
은 동일하게 20
- 원시 자료형은 값 자체가 복사됨. 즉, 변수
-
- 참조자료형
- 원시 자료형이 아닌 모든 자료형은 참조 자료형
- 여러 데이터를 한 번에 다룰 수 있는 배열, 객체가 대표적인 참조 자료형, 함수도 참조 자료형
- stack
- 참조형인 배열 선언 및 할당
-
참조 자료형을 변수에 할당하면 메모리 공간에 주소값이 저장
-
다양한 참조형
- d.pop()에 의해 배열의 마지막 요소가 삭제되었음.
- 참조 값을 갖는 변수를 다른 변수에 할당하면 주소 값이 복사되어 전달
- 배열의 요소 각각이 하나의 값이기 때문에 하나의 공간에 배열 자체를 저장하는 것은 불가능
- 힙에 참조 자료형을 저장한 후 저장 공간을 참조할 수 있는 주소 값을 변수에 저장, 변수는 stack에 저장됨.
- 참조 자료형은 변경이 가능한 값(mutable value)
-
참조 자료형을 복사할 경우 = 같은 주소 값을 가르킴
1 2
let arr = [0, 1, 2, 3]; let copiedArr = arr;
얕은 복사와 깊은 복사
summary
- 원시 자료형이 할당된 변수를 다른 변수에 할당하면 값 자체의 복사가 일어남, 따라서 원본과 복사본 중 하나를 변경해도 다른 하나에 영향을 미치지 않음
- 참조 자료형이 할당된 변수를 다른 변수에 할당하면 주소가 복사되어 원본과 복사본이 같은 주소를 참조
- 참조 자료형의 주소값을 복사한 변수에 요소를 추가하면 같은 주소를 참조하고 있는 원본에도 영향을 미친다.
- 참조 자료형이 저장된 변수를 다른 변수에 할당할 경우, 두 변수는 같은 주소를 참조하고 있을 뿐 값 자체가 복사되었다고 볼 수 없다. 얕은 복사(shallow copy)
- 절대 =을 통해서 복사하면 안됨!!!!! 참조형은 = 으로 할당 절대 하지말고 새로 만드는 것을 권장.
- 배열 복사 :
slice()
메서드 또는 spread syntax 등의 방법 =얕은복사-
slice()
1 2 3 4
let arr = [0, 1, 2, 3]; let copiedArr = arr.slice(); console.log(copiedArr); // [0, 1, 2, 3] console.log(arr === copiedArr); // false
- 결과
- spread syntax
-
spread syntax는 ES6에서 새롭게 추가된 문법으로, spread라는 단어의 뜻처럼 배열을 펼칠 수 있음
1 2 3
let arr = [0, 1, 2, 3]; console.log(...arr); // 0 1 2 3
-
- for문으로 복사 - 깊은 복사 가능
-
일차원
1 2 3 4 5 6 7 8 9 10 11 12 13
let arr =[1,2,3,4,5,6,7,8,9,10]; let arr2=[]; for(let i=0;i<arr.length;i++){ arr2.push(arr[i]); } console.log("두 배열의 상태 :"); console.log(arr); console.log(arr2); arr.pop(); console.log("arr.pop() 이후 두 배열의 상태 :"); console.log(arr); console.log(arr2);
-
다차원 - 얕은 복사
1 2 3 4 5 6 7 8 9 10 11 12 13 14
let arr =[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]; let arr2=[]; for(let i=0;i<arr.length;i++){ arr2.push(arr[i]); } console.log("두 배열의 상태 :"); console.log(arr); console.log(arr2); arr2[2].pop(); console.log("arr.pop() 이후 두 배열의 상태 :"); console.log(arr); console.log(arr2);
- 다차원 배열은 배열 내 존재하는 배열은 주소값을 참조만 하게 됨.
- 이럴 경우 이중반복문 으로 해결 가능.
-
-
- 객체 복사 :
Object.assign()
또는 spread syntax 등의 방법으로 복사 = 얕은복사-
Object.assign()
1 2 3 4 5
let obj = { firstName: "coding", lastName: "kim" }; let copiedObj = Object.assign({}, obj); console.log(copiedObj) // { firstName: "coding", lastName: "kim" } console.log(obj === copiedObj) // false
-
spread sytax
- spread syntax는 배열뿐만 아니라 객체를 복사할 때도 사용가능
1 2 3 4 5
let obj = { firstName: "coding", lastName: "kim" }; let copiedObj = {...obj}; console.log(copiedObj) // { firstName: "coding", lastName: "kim" } console.log(obj === copiedObj) // false
-
- 깊은 복사 : 대표적인
JSON.stringify()
와JSON.parse()
를 사용하는 방법이 있지만, 예외의 케이스가 존재 ( 참조자료형에 함수가 있는 경우) - 완전한 깊은 복사를 반드시 해야 하는 경우, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 사용하면 됨.
스코프
-
예제1.
-
예제 2
스코프의 정의와 규칙
- 바깥 쪽에 선언한 변수는 안쪽 스코프에서 사용 가능
- 안쪽 스코프에서 선언한 변수는 바깥쪽에서 사용 불가능
- 변수 접근 규칙에 따른 유효범위
- 안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능
- 스코프는 중첩가능
-
가장 바깥의 스코프는 특별히 전역 스코프 (Global scope)라고 부름 ↔ 지역스코프(Local scope)
-
지역변수는 전역변수보다 더 높은 우선순위를 가짐.
//김코딩 , 박해커 , 김코딩
- 스코프의 종류
- block scope : 중괄호로 둘러싼 범위
- fuction scope: 함수로 둘러싼 범위
-
var 키워드는 블록 스코프를 무시하고 함수 스코프만 따름
- var/let/const
- var
- 블록스코프는 무시하고 함수스코프를 따름
- 재선언을 해도 에러내지 않음 = 버그유발
- var로 선언된 전역변수 및 전역함수는 window 객체에 속하게 됨
- 브라우저에는 window라는 객체가 존재
- 브라우저 창을 대표하는 객체지만 브라우저 창과 관계없이 전역항목도 담고 있음
- let
- 블록스코프를 따름
- 재선언 방지
- const
- 블록스코프를 따름
- 값을 새롭게 할당할 일이 없다면 const키워드 사용 권장
- 값을 재할당하는 경우 TypeError를 냄
- var
클로저
클로저 : 외부 함수의 변수에 접근할 수 있는 내부 함수를 클로저 함수
MDN의 클로저 정의
“함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.”
- 클로저는 함수와 그 함수 주변의 상태의 주소 조합이다.
-
클로저의 함수는 어디에서 호출되느냐와 무관하게 선언된 함수 주변 환경에 따라 접근할 수 있는 변수가 정해진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const globalVar = '전역 변수'; function outerFn() { const outerFnVar = 'outer 함수 내의 변수'; const innerFn = function() **{** return 'innerFn은 ' + outerFnVar + '와 ' + globalVar + '에 접근할 수 있습니다.'; **}** return innerFn; } const innerFnOnGlobal =outerFn(); const message = innerFnOnGlobal(); console.log(message) //'innerFn은 outer 함수 내의 변수와 전역 변수에 접근할 수 있습니다.'라는 문자열이 리턴
InnerFn();이 아닌 innerFn;을 리턴하여 실행을 지연시킨후
변수에 함수 자체를 담고 있음 ⇒ 실행가능
결국 안에있는 함수를 밖으로 끌 어 온 것.
그래서 js에서도 캡슐화를 구현하는 것. 외부와 단절시킴.
js에서는 변수에 함수를 담을 수 있고 변수를 실행하면 함수를 실행할 수 있다
- 클로저 활용
-
데이터를 보존하는 함수
1 2 3 4 5 6 7 8 9 10 11
function createFoodRecipe (foodName) { let ingredient1 = '탄산수'; let ingredient2 = '위스키'; const getFoodRecipe = function () { return `${ingredient1} + ${ingredient2} = ${foodName}!`; } return getFoodRecipe; } const recipe = createFoodRecipe('하이볼'); recipe(); // '탄산수 + 위스키 = 하이볼!'
-
highballRecipe
함수는 문자열 ‘하이볼’
을 보존하고 있어서 (=주소값을 보고있어서)
전달인자를 추가로 전달할 필요가 없고, 다양한 하이볼 레시피를 하나의 함수로 제작할 수 있음
-
커링
1) 커링은 여러 전달인자를 가진 함수를 함수를 연속적으로 리턴하는 함수로 변경하는 행위
2) 예제1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
```jsx function sum(a, b) { return a + b; } function currySum(a) { const innerSum = function(b) { return a + b; }; return innerSum; } console.log(sum(10, 20) === currySum(10)(20)) // true ```
3) 예제 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
```jsx function makePancake(powder) { return function (sugar) { return function (pan) { return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`; } } } // 위 함수는 아래 기존 패턴의 함수와 같은 코드입니다. function makePancake(powder) { let innerFn1 = function (sugar) { let innerFn2 = function (pan) { return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`; } return innerFn2; } return innerFn1; } const addSugar = makePancake('팬케이크가루'); const cookPancake = addSugar('백설탕'); const morningPancake = cookPancake('후라이팬'); // 잠깐 낮잠 자고 일어나서 ... const lunchPancake = cookPancake('후라이팬'); ``` ```jsx function makePancakeAtOnce (powder, sugar, pan) { return `팬케이크 완성! 재료: ${powder}, ${sugar} 조리도구: ${pan}`; } const morningPancake = makePancakeAtOnce('팬케이크가루', '백설탕', '후라이팬') // 잠깐 낮잠 자고 일어나서 만든 팬케이크를 표현할 방법이 없다.
comment
클로저랑 커링함수는 대충 뭔가 알것 같은데 실제로 코드를 작성해보라고 하면 못할 것 같다…
프론트 쪽은 수업기간이 짧아서 그런지 할 건 많은데 호다닥 지나가는 것 같아서 이걸 공부를 제대로 혼자라도 알고 넘어가야할지
자바쪽을 더 공부해야할지 고민이 되는데 이렇게 짧은데 복습이라도 조금하면 안될 것 같아서
다른 것 보다 클로저함수는 더 보고 지나가야 할 것 같다..!
Leave a comment