[JavaScript] 변수, 생성자함수, 객체 메소드, 심볼

JiHyun·2023년 2월 1일
0

JavaScript

목록 보기
2/8
post-thumbnail

변수 var, let, const

앞서 기초강좌에서는 실질적으로 사용하는 let, const만 다뤘었다.
하지만 이번 강좌에서는 var까지 다뤄본다.

var name = 'Mike';
console.log(name);

var name = 'Jane';
console.log(name);

let name = 'Mike';
console.log(name);

let name = 'Jane'; // error
console.log(name);

var는 한번 선언된 변수를 재선언할 수 있다. 이는 유연한 변수로써 사용하기 좋을 거 같지만 코드량이 많아진다면 값이 바뀔 우려가 있다
그에비해 let과 const는 변수를 재선언할 수 없다.

그럼 let과 const의 차이는 무엇일까?
let은 재할당이 가능하다는 점이다.(const는 재할당 안됨)

let name = 'Mike';
console.log(name);

name = 'Jane';
console.log(name);

컴퓨터가 코드를 읽는 방식은 위에서부터 읽는 것인데 var는 해당 함수 밑에서 선언해도 사용가능하다는 점이 있다.

이렇게 콘솔전에 실행되는걸 '호이스팅(hoisting)'이라고 한다
호이스팅이란 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것 처럼 행동하는걸 뜻한다
(하지만 name의 변수선언은 호이스팅되도 'Mike'의 값은 할당되기 전이라 이 코드는 결국 undefined가 뜬다)

그럼 let과 const는 호이스팅되지 않을까?
결론은 된다.
다만 'TDZ'로 인해 함수 선언전에는 작동이 되지 않는다

TDZ란 'Temporal Dead Zone'의 약자로 함수가 선언되기 전의 영역을 의미한다. 이는 코드를 예측가능하게 하고 잠재적인 버그를 낮춰준다

let과 const는 TDZ의 영향을 받는다.

또 변수생성과정에서도 차이가 있다

  • var
    1) 선언 및 초기화 2) 할당
  • let
    1) 선언 2) 초기화 3) 할당
  • const
    1) 선언 + 초기화 + 할당

생성자 함수

회원이나 상품처럼 객체를 여러개 만들어야 하는 경우가 있다.
각각의 객체를 따로 만들 필요없이 생성자 함수를 사용하면 편하다.

function User(name, age){
  this.name = name;
  this.age = age;
}
let user1 = new User('ji', 26);
let user2 = new User('hyun', 26);
let user3 = new User("yun", 26);

이처럼 첫글자는 항상 대문자로 적어줘야한다.
new연산자를 사용해서 함수를 호출한다
실제로 함수 실행방식은 new 함수명();을 실행시키면 this라는 빈 객체를 만들고 name의 값과 age의 값을 객체에 할당한다. 그다음 this를 리턴한다.
생성자 함수를 사용하면 훨씬 빠르고 일관성있게 객체를 만들 수 있다.

function Item(title, price){
  this.title = title;
  this.price = price;
  this.showPrice = function(){
    console.log(`${title}의 가격은 ${price}원 입니다.`)
  }
}
const item1 = new Item("인형", 3000)
const item2 = new Item("가방", 4000)
const item3 = new Item("지갑", 9000)

item3.showPrice();
> "지갑의 가격은 9000원 입니다."

객체 메소드(Object methods)

1) 계산된 프로퍼티(Computed property)

let a = "age"

const user = {
  name: "jihyun",
  [a] : 30,
}

객체의 key값은 이런식으로도 넣을 수 있다. a의 변수를 미리 선언하고 key값에 a를 할당하는 것이다.
변수 뿐만 아니라 문자나 숫자도 넣을 수 있다.

const user = {
  [1 + 4]: 5,
  ["안녕하세요"]: "Hello"
}
> user {5: 5, 안녕하세요: "Hello"}

그럼 객체에서 사용할 수 있는 methods를 더 알아보자

2) Object.assign() 객체 복제

const user = {
  name: "jihyun",
  age : 30,
}

user라는 객체가 있고 이것을 cloneUser라는 이름으로 복제를 하고싶다면 어떻게 해야할까?

const cloneUser = user;

단순히 이런 코드로 복제가 될까?
cloneUser의 name값을 "jihyun"에서 "yun"으로 바꿔보자

cloneUser.name = "yun"
console.log(user);
> {name : "yun", age: 30}
console.log(cloneUser);
> {name : "yun", age: 30}

이렇게 cloneUser의 name의 값만 바꾸고 싶지만 변수명이 2개가 된것일뿐 복제는 이루어지지 않아서 user의 name 값까지 변경된 것이다.
복제가 제대로 이루어지지 않은 이유는 user변수에는 객체 자체가 들어가있는게 아니라 객체에 대한 참조값이 저장되어있기 때문이다.
'제대로 된'복제를 하려면 Object.assign()를 사용하면 된다

const newUser = Object.assign({}, user)

newUser.name = "yun"
console.log(user);
> {name : "jihyun", age: 30}
console.log(newUser);
> {name : "yun", age: 30}

여기서 빈 객체{}는 초기값이다 두번째 매개변수로부터 들어온값(user)이 초기값에 병합된다. 이렇게 newUser라는 이름으로 복제가 잘 이루어져서 name의 값을 바꿨을때 user객체에는 영향을 주지 않는다.
또한 객체를 복제할 뿐만 아니라 새로운 key, value도 넣을 수 있다

Object.assign({gender: "female"}, user)
{
  gender: "female",
  name: "jihyun",
  age: 30
}

그런데 만약 key값이 같다면 어떻게 될까? 초기값은 지워지고 병합된 key값만 남게된다.

Object.assign({name: "yun"}, user)
{
  name: "yun",   <<이부분은 지워짐
  name: "jihyun",
  age: 30
}

2개 이상의 객체도 합칠 수 있다

const user = {
  name : "jihyun"
}
const info1 = {
  age : 22
}
const info2 = {
  gender : "female"
}
Object.assign(user, info1, info2)
console.log(user)
{
  "name": "jihyun",
  "age": 22,
  "gender": "female"
 }

여기서 객체 자체는 깊은 복사가 수행되지만 2차원 이상의 객체는 얕은 복사가 수행된다.

3) Object.Keys() 키 배열 반환

key값만 가져오고 싶을때 사용한다.

const user = {
  name : "jihyun",
  age : 22,
  gender : "female",
}
Object.Keys(user);
> ["name", "age", "gender"]

4) Object.values() 값 배열 반환

반대로 value 값을 반환하고싶다면 Object.values()를 쓰면 된다

const user = {
  name : "jihyun",
  age : 22,
  gender : "female",
}
Object.values(user);
> ["jihyun", 22, "female"]

5) Object.entries() 키, 값 배열 반환

key, value값을 둘다 반환해준다.

const user = {
  name : "jihyun",
  age : 22,
  gender : "female",
}
Object.entries(user)
> [
  ["name", "jihyun"], 
  ["age", 30], 
  ["gender", "female"]
]

다만 이건 리스트형태로 반환된다.

6) Object.fromEntries() 키, 값 배열을 객체로

앞서 key, value값을 반환했는데 이걸 객체로 만들어주고 싶을때 사용할 수 있는 방법이다.

const arr = [
  ["name", "jihyun"], 
  ["age", 30], 
  ["gender", "female"]
]
Object.fromEntries(arr)
> {
  name : "jihyun",
  age : 22,
  gender : "female",
}

심볼(Symbol) 자료형

심볼은 코드내 유일한 식별자를 만들때 사용한다.

const a = Symbol()

심볼을 만들때 설명을 적어줄 수도 있는데 설명을 덧붙이면 디버깅할때 유용하다고 한다. 게다가 이 문자열은 심볼생성할때 어떠한 영향도 주지 않는다.
심볼형은 객체의 key로도 사용이 가능하다.

const id = Symbol('id') 
const id2 = Symbol('id') 

const user = {
  name : "jihyun",
  age : 22,
  [id] : "myid",
  [id2] : "myid",
}
id === id2
> false
id == id2
> false

이 코드를 해석해보면 id라는 변수에 심볼형 'id'값을, id2 변수에 심볼형 'id'값을 넣고 이걸 user라는 객체의 key로 사용했다. 심볼형의 이름이 같으니 같은 값일까? 아니다. 앞에서 언급했듯 이 문자열은 심볼생성에 어떠한 영향도 주지 않으며 심볼은 코드내 유일한 식별자를 만들기때문에 두 값은 같을 수 없다.
심볼에 설정한 이름을 알고싶다면 .description을 사용하면 된다

id2.description
> "id"

그럼 앞에서 배웠던 Object.key도 잘 나올까?

Object.key(user)
> ["name", "age"]

이 처럼 Object.key(user), Object.values(user), Object.entries(user) 이런 객체 메소드들은 심볼형을 건너뛴다 마찬가지로 for~in구문도 심볼형을 건너뛴다.
하지만 숨겨진 Symbol key를 보는 방법도 있다.

// 심볼형만 보는 방법
Object.getOwnPropertySymbols(user)
> [Symbol(id)]

// 심볼형을 포함한 모든 key를 보는 방법
Reflect.ownKeys(user)
> ["name", "age", Symbol(id)]

Symbol.for() 전역 심볼

앞서 말했듯 심볼은 이름이 같더라도 각각 다르게 취급된다. 그런데 이름이 같은 심볼이 같은 개체를 가리키길 원하는 경우도 있다. Symbol 함수는 매번 다른 Symbol값을 생성하지만, Symbol.for 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유한다.

const id = Symbol.for('id') 
const id2 = Symbol.for('id') 

id === id2
> true
id == id2
> true

전역 심볼에 넣은 이름을 알고싶다면 Symbol.keyFor()을 쓰면 된다.

Symbol.keyFor(id2)
> "id"
profile
비전공자의 개발일기📝

0개의 댓글