자바스크립트 중급 개념 1 - 변수, 생성자 함수, 객체 메소드, 계산된 프로퍼티, 심볼

혜빈·2024년 9월 20일

JS

목록 보기
19/21

1. 변수

  • let, const, var
  • var, let 비슷함

var

  • 한 번 선언된 변수를 다시 선언할 수 있음
  • 선언 전 사용 가능 : 호이스팅으로 인해 선언이 최상위로 끌어올려지기 때문
  • 선언은 호이스팅 되지만 할당은 호이스팅 되기 않기 때문에 선언 후 할당 전까지는 undefined 상태임

호이스팅
: 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것 처럼 행동

let

  • 재선언 불가
  • let도 호이스팅 되지만 Temporal Dead Zone(TDZ) 때문에 undefined가 아니라 에러가 발생함

TDZ

  • let, const는 TDZ 영역을 갖는데, TDZ 영향을 받기 때문에 할당 하기 전에는 사용할 수 없음
  • 이는 코드를 예측하고 잠재적인 버그를 줄일 수 있음

변수의 생성 과정

  1. 선언 단계
  2. 초기화 단계
  3. 할당 단계

var의 생성 과정

  1. 선언 및 초기화 단계
  2. 할당 단계
  • 선언과 초기화가 동시에 일어남
  • 그래서 할당 전에 호출하면 에러가 아닌 undefined가 나옴

let의 생성 과정

  1. 선언 단계
  2. 초기화 단계
  3. 할당 단계
  • 선언과 초기화가 분리되어 진행됨
  • 그래서 할당 전에 호출하면 에러가 발생함
  • 호이스팅 되면서 선언 단계가 이루어지지만, 초기화 단계는 실제 코드에 도달했을때 이루어지기 때문에 레퍼런스 에러가 발생함

const의 생성 과정

  1. 선언 + 초기화 + 할당 동시에 일어남
const gender;
  • 위 코드처럼 선언하면서 할당을 같이 안해주면 에러가 발생함

스코프

var

  • 함수 스코프 : 함수 내에서 선언된 변수가 지역변수가 됨

let, const

  • 블록 스코프 : 모든 코드 블록에서 선언된 변수는 코드 블록 내에서만 유효하며, 외부에서는 접근할 수 없음
  • 코드 블록 내부에서 선언한 변수 -> 지역변수
  • 코드 블록 : 함수, if문, for문, while문, try-catch문

스코프 예시

const age = 28;

if(age>19){
	var txt = "성인";
}

console.log(txt); // 성인
  • 위 코드에서 var를 사용하면 if문 안에서 선언된 변수도 if문 밖에서 사용 가능 (함수 스코프이기 때문)
  • 하지만 let, const는 블록 내부에서 선언된 변수는 블록 내부에서만 유효하기 때문에 오류 발생 (블록 스코프이기 때문)

함수 스코프 예시

function add(num1, num2){
	var result = num1 + num2;
}
add(2,3);

console.log(result); // 에러
  • var도 함수 밖을 벗어나면 사용할 수 없음
  • 유알하게 벗어날 수 없는 스코프가 함수라고 생각하기

정리

  • var보다 let, const 사용 권장
  • 예측 가능한 결과를 내고 버그를 줄일 수 있기 때문

2. 생성자 함수

객체 리터럴

let user = {
	name: "hyebeen",
  	age: "28",
}
  • 이렇게 객체를 만드는 것을 객체 리터럴이라고 함
  • 비슷한 객체를 여러 개 만드는 상황이 많이 발생함
  • EX. 회원, 상품 등
  • 그럴 때 사용 가능한 것이 생성자 함수

생성자 함수

  • 보통 첫 글자를 대문자로 해서 함수를 만들어줌
function User(name, age){
	this.name = name;
  	this.age = age;
}

let user1 = new User('hyebeen', 28);
let user2 = new User('Jeong', 30);
let user3 = new User('Lee', 20);
  • User라는 함수를 만들어서 이름, 나이를 매개변수로 받은 후 this에 넣어줌
  • new 연산자를 사용해서 함수를 호출함
  • 각각 다른 변수명으로 3개를 연달아 호출함
  • 간편하게 비슷한 객체 3개를 만듦
  • 생성자 함수붕어빵 틀이나 와플 팬이라고 생각하기
  • 필요한 재료들을 넣고 찍어냄
  • 위 코드에서 필요한 재료들은 name, age였음
  • 생성되는 객체들은 와플

생성자 함수 동작 과정

function User(name, age){
	// this = {}
  
  	this.name = name;
  	this.age = age;
  
   // return this;
}

new 함수명();
  • new 함수명()을 실행하면 아래와 같은 과정을 거침
  1. 빈 객체 만들고 this에 할당
  2. 함수 본문을 실행하면서 this에 프로퍼티들을 추가함
  3. 마지막으로 this를 반환함
  • 실제로 this = {} , return this;는 코드에 없음
  • new를 붙여서 실행하는 순간 저 방식으로 알고리즘이 동작함
  • 이렇게 객체를 만들면 하나씩 객체를 쓰는 것 보다 훨씬 바르고, 일관성이 있게 객체 생성 가능
  • 어떤 함수라도 new를 붙여서 실행하면 저 알고리즘이 동작함
  • 그래서 생성자 함수는 첫 글자를 대문자로 하는게 관례임

메소드 추가하기

function User(name, age){

  	this.name = name;
  	this.age = age;
  	this.sayName = function(){
    	console.log(this.name);
    }

}

let user5 = new User("Hee", 50);

user5.sayName() // "Hee"
  • this로 할당된 객체에 sayName 메소드를 추가하고 user를 하나 더 생성함
  • user5.sayName()을 호출했을 때 this는 .앞의 user5가 됨
  • 그래서 this.name은 user5의 name이 됨
  • new를 안붙이면 일반 함수가 되기 때문에 생성자 함수는 꼭 new를 붙여줘야 함

정리

  • 생성자 함수는 비슷한 객체를 여러개 만들 때 유용하게 사용 가능

3. 객체 메소드(Object methods), 계산된 프로퍼티(Computed property)

Computed property

let a = 'age';

const user = {
	name : "hyebeen",
	age : 28
}
  • 위 코드를 아래처럼 age 대신 [a] 이렇게 사용 가능함
let a = 'age';

const user = {
	name : "hyebeen",
	[a] : 28
}
  • 대괄호로 묶어주면 a 라는 문자열이 아니라 변수 a에 할당된 값이 들어감
  • 이를 computed property라고 부름
const user = {
	[1 + 4] : 5,
  	["안녕" + "하세요"] : "Hello"
}

user
// {5: 5, 안녕하세요: "Hello"}
  • 이렇게 식 자체를 넣는 것도 가능

다른 예시

function makeObj(key, val) {
	return {
	[key]: val,
};

}

const obj = makeObj("나이", 28);

console.log(obj);
// {나이: 28}

- **어떤게 key가 될지 모르는 객체를 만들 때 유용함**

#### 객체에서 사용 가능한 Methods

> **Object.assign() : 객체 복제**

```JS
const user = {
	name : "hyebeen",
	age : 28
}
  • 객체를 복제하고 싶다고 아래와 같이 복제하면 안됨
const cloneUser = user;
  • user 변수에는 객체 자체가 들어가 있는게 아니라 객체가 저장된 메모리 주소인 객체에 대한 참조값이 저장됨
  • 그래서 위처럼 복사하면 객체가 복사되면서 들어가는게 아니라 그 참조값만 복사됨
  • 하나의 객체에 두 변수가 접근하고 있는 것임
  • 동일하게 복제하려면 Object.assigh() 메소드를 써야 함
const newUser = Object.assign({}, user);
  • 첫 번째 매개변수인 {} 빈 객체는 초기값이고, 두 번째 매개변수로 들어온 객체들이 초기값에 병합됨
  • 이렇게 하면 빈 객체에 user가 병합되기 때문에 복제가 됨
  • {} + {name : "heybeen", age : 28} = {name : "heybeen", age : 28}
  • 이름을 바꿔도 user는 변함이 없음
  • 같은 객체가 아니기 때문
const newUser = Object.assign({}, user);

newUser.name = 'Tom';

console.log(user.name); // "hyebeen"
const newUser = Object.assign({ gender:'male' }, user);
  • 이렇게 하면 성별값만 있는 객체가 user를 병합하는 것이고,
    총 3개의 프로퍼티를 가지게 됨
  • { gender:'male' } + {name : "hyebeen", age : 28} = {gender: 'male', name : "hyebeen", age : 28}
  • 만약 병합을 하는데 key가 같다면? -> 덮어쓰게 됨
Object.assign({ name: 'Tom' }, user);
  • { name :'Tom' } + {name : "hyebeen", age : 28} = {name : "hyebeen", age : 28}
  • 먼저 있던 name: 'Tom'이 새로 병합된 객체의 name값인 'hyebeen'이 덮혀버림
  • 두 개 이상의 객체도 병합 가능
const user = {
	name : 'Mike'
}
const info1 = {
	age : 30
}
const info2 = {
	gender : 'male'
}

Objcet.assign(user, info1, info2)
  • 이렇게 하면 user 객체에 info1이 먼저 병합되고, 그 다음에 info2가 병합된 객체에 병합됨

Object.keys() : 키 배열 반환

  • 객체 프로퍼티의 key를 배열로 반환함
  • 객체를 Object.keys의 인수로 전달하면 됨
  • 그럼 key들이 배열로 만들어져 반환됨
const user = {
  name : "hyebeen",
  age : 28,
  gender: 'female',
}

Object.keys(user);
// ["name", "age", "gender"]

Object.values() : 값 배열 반환

  • 반대로 값들만 반환하고 싶다면 Object.values() 사용
  • 객체의 value에 해당하는 값들만 배열로 반환됨
const user = {
  name : "hyebeen",
  age : 28,
  gender: 'female',
}

Object.values(user);
// ["hyebeen", 28, female]

Object.entries() : 키/값 배열 반환

  • key, value 모두 배열로 반환 받고 싶을 때 사용
  • 키와 값이 쌍으로 묶여서 배열로 반환됨
  • 배열 안에 값, 키가 들어있는 배열 3개가 들어있음
const user = {
  name : "hyebeen",
  age : 28,
  gender: 'female',
}

Object.entries(user);
// [["name","hyebeen"],["age", 28],["gender", female]]

Object.fromEntries() : 키/값 배열을 객체로

  • Object.entries()와 반대 개념
  • 키,값을 쌍으로 묶은 배열들을 넣어주면 객체로 만들어줌
const arr = [
  ["name","hyebeen"],
  ["age", 28],
  ["gender", female]
];

Object.fromEntries(arr);
// {name : "hyebeen", age : 28, gender: 'female',}

4. 심볼(Symbol)

property key : 문자형

const obj = {
	1: '1입니다.',
  	false: '거짓'
}

Object.keys(obj); // ["1", "false"]

obj['1'] // '1입니다.'
obj['false'] // '거짓'
  • 이렇게 숫자형, boolean형으로 만들어도 Object.keys()로 가져와보면 문자형으로 반환됨
  • 실제로 접근할떄도 문자'1'이나 문자'false'로 접근 가능
  • 이렇게 객체 프로퍼티 키는 문자형으로 가능하고, 문자형 이외에 심볼형도 가능

Symbol

심볼 만드는 법

const a = Symbol(); // new를 붙이지 않음
  • 심볼은 유일한 식별자로 만들 때 사용함
const a = Symbol(); // new를 붙이지 않음
const b = Symbol();

console.log(a)
Symbol()

console.log(b)
Symbol()
  • 생긴건 똑같은데 일치연산자로 확인해보면 false가 나옴
a === b; // false
a == b;  // false

Symbol형 특징

유일성 보장

const id = Symbol('id');
  • 전체 코드 중 딱 하나임
  • 심볼로 만들 때 설명을 붙여줄 수 있음 (디버깅 시 편리함)
  • 방법은 문자열을 전달해주면 되는데, 이 문자열은 심볼 생성에는 어떠한 영향도 주지 않음
const id = Symbol('id');
const id2 = Symbol('id');
  • 이렇게 똑같이 생긴 심볼을 생성해서 보면
    생김새는 같지만 다르다고 나옴
id === id2 // false
id == id2 // false
  • 심볼을 객체의 key로 사용해보기
const id = Symbol('id');
const user = {
	name : "hyebeen",
  	age : 28,
  	[id] : 'myid'
}

user
// {name: "hyebeen", age: 28, Symbol(id): "myid"}

usre[id]
// "myid"

Object.keys(user); // ["name", "age"]
  • Object.keys, Object.values() 와 같은 메소드들은 key가 심볼인 프로퍼티는 건너 뜀
  • for(let a in user){} 처럼 for-in을 써도 심볼은 건너 뜀
  • 그렇다면 심볼은 어디에서 사용할까?

Symbol 사용 상황

  • 특정 객체의 원본 데이터는 건들지 않고 속성 추가 가능
  • 다른 사람들이 만들어 놓은 객체에 자신만의 속성을 추가해서 덮어버리는 경우가 발생하면 안됨

Symbol.for() : 전역 심볼

  • 심볼은 이름이 같더라도 모두 다름
  • 가끔 전역변수처럼 이름이 같으면 같은 객체를 가리켜야 될 때가 있음
  • 그 때 Symbol.for() 사용하면 됨
  • 전역 심볼이라 부르고, 코드 어디에서나 사용 가능
  • Symbol.for()을 사용하면?
  • 하나의 심볼만 보장받을 수 있음
  • 없으면 만들고, 있으면 가져오기 때문
  • Symbol 함수는 매번 다른 Symbol 값을 생성하지만,
    Symbol.for 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유함
const id1 = Symbol.for('id');
const id2 = Symbol.for('id');

id1 === id2; // true

Symbol.keyFor(id1) // 'id'
  • 이름을 얻고 싶다면 Symbol.keyFor을 사용하면 됨
  • 변수를 넣어주면 생성할 때 지었던 이름을 알려줌

description

  • 전역 심볼이 아닌 심볼은 keyFor을 사용할 수 없는 대신description으로 이름을 알 수 있음
const id = Symbol('id 입니다.');
id.description; // 'id 입니다.'

숨겨진 Symbol key 보는 법

  • 심볼은 완전히 숨길 수 있는 방법은 없음
  • Object.getOwnPropertySymbols()를 사용하면 심볼들만 볼 수 있음
const id = Symbol('id');

const user = {
	name : "hyebeen",
  	age : 28,
  	[id] : 'myid'
	}

Object.getOwnPropertySymbols(user); // [Symbol(id)]

Reflect.ownKeys(user); // ["name", "age", Symbol(id)]
  • Reflect.ownKeys() 을 사용하면 심볼형 key를 포함한 객체의 모든 key를 보여줌
  • 하지만 대부분의 라이브러리, 내장함수 들은 이런 메서드들을 사용하지 않음
  • 유일한 프로퍼티를 주고 싶을 때 심볼을 사용하면 됨

참고

profile
최강 개발자를 꿈꾸는 병아리

0개의 댓글