230323_TIL

·2023년 3월 23일
0

객체 불변성을 위하여

(동결, 밀봉, 확장 금지)

  1. 오브젝트 동결

    Object.freeze

    수정과 추가, 삭제, 쓰기, 속성 재정의 불가능

const hwuiinn = { name: "호이인" };
const pat = { name: "초코", emoji: "🐶", owner: hwuiinn };

Object.freeze(pat);
pat.name = "왈왈";
console.log(" pat : ", pat);
pat.age = 5;
console.log(" pat : ", pat);
delete pat.name;
console.log(" pat : ", pat);
hwuiinn.name = "호이잉이니"; // 얕은 동결(?) 때문에 pat이 참조하고 있는 hwuiinn 객체는 수정이 가능하다.
console.log(" pat : ", pat);
  1. 오브젝트 밀봉

    Object.seal

    밀봉된 객체는 값의 수정이 가능.
    But, 추가, 삭제, 속성 재정의 불가능.

//  오브젝트 밀봉. Object.seal
//  밀봉된 객체는 값의 수정 가능.
// 추가, 삭제, 속성 재정의 불가능.
const cat = { ...pat };
// Object.assign(cat, pat);
// assign을 이용하면 복사가 가능하다. (cat 객체에 dog 프로퍼티를 모두 복사한다.)
// Object.assign(cat, pat); === const cat = {...pat};
Object.seal(cat);
console.log(" cat : ", cat);
cat.name = "야오옹";
console.log(" cat : ", cat);
delete cat.emoji;
console.log(" cat : ", cat);
console.log(cat.emoji);
  1. 오브젝트 확장 금지

    Object.preventExtensions

    밀봉된 객체는 추가만 불가능.
    But, 추가, 삭제, 속성 재정의 가능.

// 확장 금지 preventExtensions
// 추가만 불가능.
// 삭제, 수정, 속성 재정의 가능
const tiger = { name: "호랑이" };
Object.preventExtensions(tiger);
console.log(" tiger : ", tiger);
console.log(" Object.isExtensible(tiger) : ", Object.isExtensible(tiger));
tiger.name = "호으으응";
console.log(" tiger : ", tiger);
delete tiger.name;
console.log(" tiger : ", tiger);
tiger.age = 1;
console.log(" tiger : ", tiger);
  1. 오브젝트가 동결, 밀봉되었는지 확인할 수 있는 함수
// 동결, 밀봉되었는지 확인할 수 있는 함수?
console.log(" Object.isFrozen(pat) : ", Object.isFrozen(pat));
console.log(" Object.isSealed(cat) : ", Object.isSealed(cat));

Boolean 값으로 알려준다.

객체에서 가질 수 있는 함수 3가지

요약

객체에서 가질 수 있는 함수 세 가지

1. 인스턴스 레벨 함수 / 2. 프로토타입 레벨 함수 / 3. 정적 레벨 함수

모든 인스턴스마다 가지고있을 인스턴스 레벨 함수,
각각의 인스턴스에서 공통적으로 가질 수 있는 프로토타입 레벨 함수,
생성자 함수 이름, 클래스 이름으로만 접근가능한 정적 레벨 함수.

인스턴스 레벨 함수

  • 모든 인스턴스마다 가지고있을 인스턴스 레벨 함수.
  • 메모리 낭비가 될 수 있다.
function Dog(name, emoji) {
  this.name = name;
  this.emoji = emoji;
  // 인스턴스 레벨의 함수 => 메모리 낭비가 될 수 있다.
  this.printName = () => {
     console.log(`${this.name} ${this.emoji}`);
   };
}

프로토타입 레벨 함수

왜 사용하는가? (내 생각)

  • 인스턴스 레벨 함수는 메모리 낭비가 될 수 있다.
  • 프로토타입 레벨의 함수를 만들면, 쓰고싶을 때 사용하면 되기 때문에 메모리 낭비 측면에서 효율적이라고 생각한다.
function Dog(name, emoji) {
  this.name = name;
  this.emoji = emoji;
}

// 프로토타입 레벨의 함수
Dog.prototype.printName = function () {
  console.log(`${this.name}!! ${this.emoji}!!!`);
};

const dog1 = new Dog("몽몽쓰", "🐶");
const dog2 = new Dog("갱갱쓰", "🐈‍");
console.log(" dog1, dog2 : ", dog1, dog2);
dog1.printName();
dog2.printName();

오버라이딩 (프로토타입)

  • 인스턴스 레벨에서(자식) 동일한 이름으로 함수를 재정의 하면,
    프로토타입 레벨의(부모) 함수 프로퍼티는 가져린다. ('섀도잉 된다'. 라고 할 수 있다.)
function Dog(name, emoji) {
  this.name = name;
  this.emoji = emoji;
}

// 프로토타입 레벨의 함수
Dog.prototype.printName = function () {
  console.log(`${this.name}!! ${this.emoji}!!!`);
};

const dog1 = new Dog("몽몽쓰", "🐶");
const dog2 = new Dog("갱갱쓰", "🐈‍");
console.log(" dog1, dog2 : ", dog1, dog2);
dog1.printName();
dog2.printName();

// 오버라이딩
dog1.printName = function () {
  console.log("오버라이딩 된 것");
};
dog1.printName();
// dog1에서 printName()에 접근 할 경우, 콘솔에 console.log("오버라이딩 된 것");이 뜬다.
//반면 dog2에서 접근할 경우, 덮어씌워지지 않은 프로토타입 레벨의 함수값이 출력된다.

정적 레벨 (Static)

  • 정적 레벨 함수는 생성자 네임으로 접근해야 한다.
function Dog(name, emoji) {
  this.name = name;
  this.emoji = emoji;
}

// 정적(Static) 레벨
// 정적 레벨 함수는 생성자 네임으로 접근해야 한다.
Dog.static = () => {
  console.log("Static Level function");
};
// dog1.static(); 접근 불가함.
Dog.static();
Dog.MAX_AGE = 20;
console.log(" Dog : ", Dog);

프로토타입을 이용한 상속

알고 넘어가기

  • 요즘은 최신 자바스크립트, 타입스크립트에서 제공하는 Class를 사용하여 구현한다.
function Animal(name, emoji) {
  this.name = name;
  this.emoji = emoji;
}

Animal.prototype.printName = function () {
  console.log(`${this.name} ${this.emoji}`);
};

function Dog(name, emoji, owner) {
  // super(name, emoji) => 부모 프로퍼티를 사용할 수 있도록 해줌.
  // Static.call() => 생성자 프로퍼티를 사용할 수 있도록 해줌.
  // 즉, super와 동일한 기능이라고 생각하면 된다.
  Animal.call(this, name, emoji);
  this.owner = owner;
}
//Dog의 프로토타입을 object가 아닌 Animal로 변경. (프로토타입 체인을 변경)
// 변경 전 : Dog.prototype = Object.create(Object.prototype);
Dog.prototype = Object.create(Animal.prototype); // 변경 후

Dog.prototype.play = () => {
  console.log("놀아달라몽");
};
const dog1 = new Dog("멍멍쓰", "🐶", "민수");
dog1.play();
dog1.printName();

function Tiger(name, emoji) {
  Animal.call(this, name, emoji);
}
Tiger.prototype = Object.create(Animal.prototype);
Tiger.prototype.hunt = () => {
  console.log("저녁을 찾고 있습니다.");
};
const tiger1 = new Tiger("어어흥흥", "🐯");
tiger1.printName();
tiger1.hunt();

// 어떤 것을 상속하고 있는지 확인하는 방법
console.log(dog1 instanceof Dog); // true
console.log(dog1 instanceof Animal); // true
console.log(dog1 instanceof Tiger); // false

Mixin

오브젝트는 단 하나의 Prototype을 가리킬 수 있다. (== 부모는 단 하나만)
하지만, 다중 상속 방법이 있음(여러 함수를 상속하는). 그게 Mixin이다.

  • 알고 넘어가기
    - 자바스크립트는 본질적으로 프로토타입을 베이스로 한 프로그래밍 언어임.
    Class를 사용해도 실직적으로 프로토타입을 사용한다 볼 수 있다.

    Object.assign(상속받을 생성자 함수.prototype, 상속받을 속성1, 상속받을 속성2 ...);

const play = {
  play: function () {
    console.log(`${this.name} 놀자고!!!!`);
  },
};

const sleep = {
  sleep: function () {
    console.log(`${this.name} 자니까 제발 깨우지 마.`);
  },
};

function Dog(name) {
  this.name = name;
}

// Mixin
Object.assign(Dog.prototype, play, sleep);
const dog = new Dog("왈왈이");
console.log(dog);
dog.play();
dog.sleep();

class Animal {}
class Tiger extends Animal {
  constructor(name) {
    super();
    this.name = name;
  }
}
// 매우 중요
// 자바스크립트는 본질적으로 프로토타입을 베이스로 한 프로그래밍 언어임. 클래스를 사용해도 실직적으로 프로토타입을 사용한다 볼 수 있다.
// 그 증거로 class를 사용하여도 mixin 적용 가능함.
Object.assign(Tiger.prototype, play, sleep);
const tiger1 = new Tiger("호랭이");
console.log(tiger1);
tiger1.play();
tiger1.sleep();

Closures

특정 두 가지 함수와 렉시컬환경 콤비네이션.

내부 함수에서 외부 함수에 있는 상태에 접근할 수 있는 권한을 주는 것.

  • 자신이 생성될 때의 렉시컬 환경을 기억하는 함수다.
  • 클로저에 의해 참조되는 외부함수의 변수를 자유변수라고 부른다.

0개의 댓글