항해/TIL

TIL(2/15) / 객체, 변경불가성, 빌트인 객체

yeeendy 2024. 2. 15. 13:42

다음 주 리액트과정 들어가기 전 알아두면 좋은 JS 내용들을 다시 한번 잡고 들어가보자

 

객체

자바스크립트는 객체 기반의 스크립트 언어이며 자바스크립트를 이루고 있는 거의 모든 것이 객체다.

원시 타입을 제외한 나머지 값들(함수, 배열, 정규표현식 등)은 모두 객체이다.

 

자바스크립트의 객체는 으로 구성된 프로퍼티들의 집합이다.

프로퍼티 값으로 함수를 사용할 수도 있으며 프로퍼티 값이 함수일 경우, 일반 함수오 구분하기 위해 메소드라 부른다

 

객체 생성 방법 3가지!

  1. 객체 리터럴
  2. Object 생성자 함수
  3. 생성자 함수
// 1. 객체 리터럴
const emptyObj = {};
console.log(typeof emptyObj) // object

const person = {
	name: 'Lee',
    sayHello: function(){
    	console.log(`Hi! My name is ${this.name}`)
    }
}

// 2. Object 생성자 함수
const person = new Object();

person.name = 'Lee'
person.sayHello = function(){
	console.log(`Hi! My name is ${this.name}`)
}

// 3. 생성자 함수
function Person(name, gender){
	this.name = name;
    this.gender = gender;
    this.sayHello = function(){
    	console.log(`Hi! My name is ${this.name}`)
    }
}
const person1 = new Person('Lee', 'female')
const person2 = new Person('Park', 'male')

 

객체 리터럴 방식으로 생성된 객체는 결국 빌트인(Built-in) 함수인 Object 생성자 함수로 객체를 생성하는 것을 단순화시킨 축약 표현이다.

 

for-in 문

객체의 문자열 키(key)를 순회하기 위한 문법

배열에는 사용하지 않는 것이 좋다

 

이유

  1. 객체의 프로퍼티에는 순서가 없기 때문에 순서가 보장되지 않는다.
    배열은 순서를 보장하는 데이터 구조이지만 객체와 마찬가지로 순서를 보장하지 않는다.
  2. 배열 요소들만을 순회하지 않는다. => 대신 for-of문 사용 추천!!
const arr = ['one', 'two'];
arr.name = 'my array';

for(let idx in arr) {
	console.log(`${idx}: ${arr[idx]}`)
}

/*
0: one
1: two
name: my array
*/


// for-of 문 (ES6)
for(let v of arr) {
	console.log(v)
}

/*
one
two
*/

객체와 변경불가성(Immnutability)

자바스크립트의 원시 타입(primitive data type)은 변경 불가능 한 값(immutable value)이다.

  • Boolean
  • null
  • undefined
  • Number
  • String
  • Symbol

원시 타입 이외의 모든 값은 객체 타입이며 객체 타입은 변경 가능한 값(mutable value)이다.

const user = {
	name: 'Lee',
    address: {
    	city: 'Seoul'
    }
};

let myName = user.name;

user.name = 'Kim';
console.log(myName); // Lee

myName = user.name; // 재할당
console.log(myName); // Kim

let myName=user.name
myName 변수에는 user 객체의 name 속성 값인 Lee가 복사된다.

myName은 복사본이라고 생각하면 간단할 듯!

 

Object.assign

  • 첫 번째 들어오는 인자에 프로퍼티를 복사한다.
  • 얕은 복사가 된다
const user1 = {
  name: 'Lee',
  address: {
    city: 'Seoul'
  }
};

// 새로운 빈 객체에 user1을 copy한다.
const user2 = Object.assign({}, user1);
// user1과 user2는 참조값이 다르다.
console.log(user1 === user2); // false

user2.name = 'Kim';
console.log(user1.name); // Lee
console.log(user2.name); // Kim

// 객체 내부의 객체(Nested Object)는 Shallow copy된다.
console.log(user1.address === user2.address); // true

user1.address.city = 'Busan';
console.log(user1.address.city); // Busan
console.log(user2.address.city); // Busan

Object.freeze

  • 불변 객체로 만들 수 있다.
  • 얕은 복사가 된다.
const user1 = {
  name: 'Lee',
  address: {
    city: 'Seoul'
  }
};

// Object.assign은 완전한 deep copy를 지원하지 않는다.
const user2 = Object.assign({}, user1, {name: 'Kim'});

console.log(user1.name); // Lee
console.log(user2.name); // Kim

Object.freeze(user1);

user1.name = 'Kim'; // 무시된다!

console.log(user1); // { name: 'Lee', address: { city: 'Seoul' } }

console.log(Object.isFrozen(user1)); // true

user1.address.city = 'Busan'; // 변경된다!
console.log(user1); // { name: 'Lee', address: { city: 'Busan' } }

 

내부 객체까지 변경 불가능하게 만들려면 Deep freeze를 해야한다.

깊은 복사하는 방법

function deepFreeze(obj) {
  const keys = Object.getOwnPropertyNames(obj);

  keys.forEach((key) => {
    const value = obj[key];
    if(typeof value === 'object' && value !== null) {
      deepFreeze(value);
    }
  });
  return Object.freeze(obj);
}

const user = {
  name: 'Lee',
  address: {
    city: 'Seoul'
  }
};

deepFreeze(user);

user.name = 'Kim';           // 무시된다
user.address.city = 'Busan'; // 무시된다

console.log(user); // { name: 'Lee', address: { city: 'Seoul' } }

 

value !== null 조건을 넣은 이유

  • value !== null 조건을 넣어주지 않으면
    const user = {(...), address: null}을 deepFreeze(user)했을 때
    typeof user.address => object가 출력된다.

빌트인 객체

 

원시 타입인 문자열(str)이 왜 메서드를 호출할 수 있는지를 이해하기 위해서는 JavaScript의 내부 동작 방식을 이해해야 합니다.

 

원시 타입과 래퍼 객체:

  • JavaScript에서는 문자열, 숫자, 불리언 등의 원시 타입에 대해 해당하는 래퍼(wrapper) 객체를 가지고 있습니다.
    이러한 래퍼 객체들은 각각의 내장 객체인 String
    , Number, Boolean 등입니다.
  • 래퍼 객체는 원시 타입의 값을 감싸는 역할을 합니다.

메서드 호출:

  • JavaScript 엔진은 원시 타입에 메서드를 호출할 때, 해당 원시 값에 대한 래퍼 객체를 임시로 생성합니다.
  • 이 래퍼 객체는 해당 원시 값에 대한 메서드를 가지고 있으므로, 메서드 호출이 가능해집니다.

프로토타입 체인:

  • 각 래퍼 객체들은 자신의 프로토타입 체인을 가지고 있습니다. 예를 들어, String 래퍼 객체는 String.prototype을 상속받습니다.
  • 이러한 프로토타입 체인을 통해 래퍼 객체들은 자신의 프로토타입에 정의된 메서드를 사용할 수 있습니다.

메서드 호출 과정:

  • str.toUpperCase()와 같은 메서드 호출이 발생하면, 엔진은 str이라는 문자열 원시 타입을 래퍼 객체로 변환합니다.
  • 래퍼 객체에는 toUpperCase()와 같은 메서드가 존재하므로, 이를 호출할 수 있습니다.
  • 호출된 메서드는 래퍼 객체의 프로토타입 체인을 따라 올라가서 해당 메서드가 있는지 찾습니다.
  • 만약 해당 메서드가 래퍼 객체의 프로토타입 체인에 존재한다면, 해당 메서드가 호출됩니다.

이와 같은 과정을 통해 JavaScript는 원시 타입에 메서드를 호출할 수 있도록 하고, 프로토타입 체인을 활용하여 메서드를 찾아가게 됩니다.

 

'항해 > TIL' 카테고리의 다른 글

TIL(2/19) / 프로토타입  (0) 2024.02.19
TIL(2/17) / JS 이벤트  (0) 2024.02.17
TIL(2/13) / JS 메서드  (0) 2024.02.13
TIL(2/10) / DOM, 클래스, 클로저  (0) 2024.02.10
TIL(2/8) / JS 4 (콜백함수, 동기/비동기)  (0) 2024.02.08