TIL 21.10.06 Javascript

서재환·2021년 10월 6일
0

TIL

목록 보기
32/37

✏️ 객체란?

원시타입은 값에 단 하나의 유형을 갖지만 객체는 여러 유형의 값을 갖는 자료구조이다. 자바스크립트 함수는 일급 객체이므로 프로퍼티에 대한 값으로 사용할 수 있다. 프로퍼티 값으로 함수가 사용될 경우 이를 메서드(프로퍼티 key + 프로퍼티 값)라고 부른다. 따라서 객체는 상태와 동작을 가진 하나의 단위로 구조화 할 수 있어 용이하다.
프로퍼티키가 자바스크립트 네이밍규칙을 준수하지 않을 경우 따옴표를 붙여야 한다. 준수할 경우 안붙여도 무방하다. 네이밍규칙으로는 변수나 함수에는 카멜케이스, 생성자 함수 또는 클래스 이름에는 파스칼 케이스를 사용한다.

OOP에선 객체엔 클래스와 인스턴스의 개념이 같이 들어있다.

원시타입
객체를 제외한 여섯가지 자료형(String, Number, null, undefined, Symbol, boolean) 을 일컫는다.

일급객체
1. 런타임 때 생성이 가능하다.
2. 함수의 매개변수로 전달할 수 있다.
3. 함수의 반환값으로 사용할 수 있다.
4. 변수나 자료구조(객체, 배열)에 저장할 수 있다.

인스턴스
클래스에 의해 생성되어 메모리에 저장된 실체

// 객체

const person = {
  name: 'john', // name: 'john' // 프로퍼티키: name 프로퍼티값: john
  age: 20, // age: 20 -> 프로퍼티 
  
  // 통틀어 method
  hobby: function () { 
    console.log("hello nice to meet you");
  }
}

객체 리터럴에 의한 객체 생성

리터럴
값을 생성하기 위해 미리 약속한 표기법. 사람이 이해할 수 있는 문자(아라비아 숫자, 알파벳, 한글) 등을 사용하여 값을 생성한다.

평가
식을 해석해서 값을 생성하거나 참조하는 것

표현식
값으로 평가될 수 있는 문(statement). 즉 값을 생성하는 하나의 식으로 간주 할 수 있다.


표현식이 평가되어 생성된 결과

객체 리터럴에 의한 생성이라는 것은 말 그대로 리터럴을 이용해 값을 생성하는 표기법을 의미한다. 리터럴 외의 객체 생성 방식은 모두 함수를 사용해 객체를 생성하므로 함수에 대해서 잘 알아야 한다.

// 객체 리터럴 생성 방법

let person = {
  name: 'park',
  age: 20,
  hobby: function() {
    console.log('play soccer')
  }
};

자바스크립트 객체의 관리 방식

자바스크립트 객체는 프로퍼티 키를 인덱스로 사용하는 해시테이블이라고 생각할 수 있다. 자바스크립트는 C++, JAVA와는 다르게 클래스 없이 객체를 생성할 수 있고 객체가 생성된 이후 동적으로 프로퍼티와 메서드를 추가할 수 있다. 프로퍼티와 메서드를 추가할 수 있는 장점이 있지만 성능적으로 클래스 기반 OOP 언어의 객체 생성과 프로퍼티 접근에 비용이 더 든다는 단점이 있다.

그런데 v8엔진에서는 프로퍼티 접근을 위해 히든클래스 방식을 사용해 C++객체 프로퍼티 접근하는 정도의 성능을 보장한다.

원시타입 문자열이 가지는 특성, 유사 배열 객체

배열처럼 인덱스로 프로퍼티에 접근할 수 있고 length 프로퍼티를 갖는 객체를 의미한다. 문자열은 위와 같은 특징으로 인해 원시타입이지만 객체처럼 사용할 수 있다.

원시타입

원시타입은 값을 변경할 수 없다. 값을 변경할 수 없다는 의미는 변수에 새로운 값을 할당하기 위해선 값 할당시 변수가 새로운 메모리 주소를 가리켜야 한다는 것을 의미한다. 원시타입이 값을 변경할 수 있다면 같은 메모리 공간에서 개발자가 변수에 의도한 값을 추출해내한다는 의미가 된다.

값에 의한 전달(공유에 의한 전달)

아래와 같은 예제가 있을 때 콘솔에 찍히는 num2의 값은 무엇일까? 정답은 10이다. 변수에 원시값을 갖는 변수를 할당 할 경우 num1에 있는 값이 복사돼서 새로운 메모리 공간에 10이 할당되는 특징으로 인해 num1과 num2는 서로 다른 메모리 공간을 갖게 되어 num1에 다른 값의 할당이 이루어지는 경우 num2의 값은 변경되지 않는다.

var num1 = 10;
var num2 = num1;

num1 = 20;

console.log(num2); // ?

javascript는 위와 같이 동작하는데 python의 경우 값에 의한 전달이 발생했을 때 num2가 같은 메모리 주소를 가리키고 있다가 새로운 값이 할당될 때 그 때 비로소 새로운 메모리 공간을 가리키는 방식으로 동작한다.

그런데 엄밀히 얘기하면 변수가 할당 연산자에 의해 값을 갖을 수 있는 것은 할당 시 메모리 주소를 변수에 전달해주기 때문이다. 따라서 값에 의한 전달(표현을 그렇게 하므로)은 할당 연산자에 의해 메모리 주소를 변수에 전달해 주는 것이고 자바스크립트의 경우 변수에 값을 갖는 변수 할당시 새로운 메모리 주소가 생성되어 해당 메모리 주소를 전달해 주는 것이고 파이썬의 경우 같은 메모리 주소를 참조하고 있다가 어느 변수에 새로운 값 할당시 그 값이 있는 메모리 주소를 해당 변수에 전달해 주고 변수에 값이 저장되는 것이다. 두가지 경우 모두 서로의 값에 영향을 끼치지 않는다.

참조에 의한 전달

값에 의한 전달과 다르게 변수에 객체를 할당 할 경우 할당문에서 변수에 메모리 주소를 전달해주는 것까지는 동일한데 변수가 가지는 값은 값에 의한 전달에서는 값이었다면 객체를 할당했을 때에는(참조에 의한 전달이 일어났을 때에는) 메모리 주소를 갖는다.

앞서 객체의 구조적 특성에 대해서 알아보았다. 객체는 여러 프로퍼티를 갖을 수 있어 크기가 일정하지 않고 프로퍼티 값으로 객체를 갖을 수 있어 복사해서 생성하는 비용이 원시타입에 비해 크다. 이는 메모리의 소비와 연결되는 문제로 메모리 사용의 효율성을 위해 자바스크립트 객체는 변경 가능한 값으로 설계되었다.

원시타입과 비교했을 때 메모리 효율이 좋지 않지만 여러 자료를 저장할 수 있다는 장점을 이용하기 위해 자체적으로 수정가능하도록 설계된 것이다.

이러한 설계적 특징으로 여러개의 변수는 하나의 변수를 참조하게 되는 것이다.

얕은복사

얕은 복사가 일어났을 때 프로퍼티 값으로 객체를 갖고 있을 때 객체가 아닌 프로퍼티에 대해선 두 객체가 서로 다른 메모리 공간을 가지고 있지만 객체에 대해선 서로 같은 주소를 참조하고 있다. 아래의 경우 person2에 얕은 복사가 이루어진 후 객체 내부의 설정을 바꾸었을 때 person1의 객체 내부에도 변화가 일어나 값이 변경된 것을 볼 수 있다.

let person1 = {
    name: 'sjh',
    age: 29,
    hobby: {
        soccer: true,
        running: false,
    }
}

let person2 = {...person1}

person2.name = 'sonic';
console.log(person1.name); //sjh

person2.hobby.soccer = "힘들지만 살 잘빠짐";
console.log(person1.hobby.soccer); //힘들지만 살 잘빠짐

깊은복사

깊은 복사의 경우 객체 내 프로퍼티로 객체를 갖는 모든 것에 대해서 복사가 이루어지는 것을 의미한다. 따라서 객체 내부에 있는 객체에 새로운 값을 할당 한다고 해도 본래 객체의 값이 바뀌지 않는다. person4 객체 내부 객체의 프로퍼티 값을 변경해주었음에도 불구하고 두 객체가 각각의 값을 갖는 것을 알 수 있다.

let person3 = {
    name: 'sjh',
    age: 29,
    hobby: {
        soccer: true,
        running: {
            sonic: 'man'
        }
        
    }
}

let person4 = JSON.parse(JSON.stringify(person3))

person4.hobby.running.sonic = 'woman';
console.log(person3.hobby.running.sonic); //man

얕은복사 깊은복사 정리

얕은복사 또는 깊은복사에 의해 생성된 객체는 서로 다른객체이다. 다만 복사의 깊이 차이에 따라 재할당 발생시 원래 객체에 영향을 미치거나 그렇지 않은 경우로 나뉜다. 얕은 복사의 경우 프로퍼티 값으로 객체가 아닌 원시타입을 갖는 경우에 대해선 두 객체가 서로 다른 메모리 공간을 갖지만 프로퍼티 값으로 객체를 갖는 경우 두 객체가 서로 같은 메모리 주소를 가리킨다.

깊은복사의 경우 아예 서로 다른 메모리 공간을 갖는 객체를 변수가 참조한며 객체 내 재할당이 이루어져도 서로 영향을 주지 않는다.

일치비교연산자(===)를 통한 객체비교

얕은복사 또는 깊은복사가 이루어져 객체 참조가 이루어진 변수 간의 일치비교연산자를 통해 비교 할 경우 서로 다른 값을 참조하고 있기 때문에 false가 뜬다.

let person1 = {
    name: 'sjh',
    age: 29,
    hobby: {
        soccer: true,
        running: false,
    }
}

let person2 = {...person1}

console.log(person1===person2); //false


let person3 = {
    name: 'sjh',
    age: 29,
    hobby: {
        soccer: true,
        running: {
            sonic: 'man'
        }
        
    }
}

let person4 = JSON.parse(JSON.stringify(person3))

console.log(person3===person4) //false

let person5 = person1
console.log(person1===person5); //true

객체를 참조하고 있는 변수를 할당한 변수간 일치비교연산자를 통한 비교가 이루어질 경우 true가 뜬다. 해당 할당은 같은 주소를 참조하는 할당이기 때문이다.

0개의 댓글