JavaScript 17장 - 개념짚기 (11)
포스트
취소

JavaScript 17장 - 개념짚기 (11)

JavaScript

상속, prototype

  • Java,C언어는 클래스 기반의 객체 지향 프로그래밍 언어이지만, JavaScript는 프로토타입 기반 언어이다.
  • 클래스 기반 객체 지향 언어는 객체 생성 이전에 클래스를 정의하고 이를 통해 객체를 생성하지만, 프로토타입 기반 객체 지향 언어는 클래스 없이도 객체를 생성할 수 있다.
  • 자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있는데, 상속 개념과 같이 부모 객체의 프로퍼티 또는 메서드를 상속받아 사용할 수 있게 한다.
  • 이러한 부모 객체를 prototype객체라고 한다.
1
2
3
let example = { name: "Choi" };

example.hasOwnProperty("name"); // true
  • example이라는 변수에 hasOwnProperty라는 메서드는 없지만, 자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯을 가지며 상속을 구현하는데 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const car = {
  wheels: 4,
  drive() {
    console.log("drive");
  },
};

const bmw = {
  color: "red",
  navigation: 1,
  wheels: 4,
  drive() {
    console.log("drive");
  },
};

const benz = {
  color: "black",
  wheels: 4,
  drive() {
    console.log("drive");
  },
};

const audi = {
  color: "green",
  wheels: 4,
  drive() {
    console.log("drive");
  },
};
  • 위의 코드에서 wheels, drive는 모든 객체에 공통적으로 들어가는 프로퍼티이다.
  • 이렇게 공통된 프로퍼티를 프로토타입에 추가하여 상속시킬 수 있다.
  • 상속은 계속 이어질 수 있는데 이를 프로토 체인이라고 한다.
  • 상속된 키와 값과 관련된 나오지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bmw.__proto__ = car;
benz.__proto__ = car;
audi.__proto__ = car;

bmw; // {color: "red", navigation: 1}
bmw.drive; // drive

const x5 = {
  color: "white",
};

x5.__proto__ = bmw;

for (p in x5) {
  console.log(p);
}

// color
// name
// navigation
// wheels
// drive

생성자 함수 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const car = {
  wheels: 4,
  drive() {
    console.log("drive");
  },
};

const BMW = function (color) {
  // this.wheels = 4;
  this.navigation = 1;
  this.color = color;
  // this.drive(){console.log("drive");};
};

const x5 = new BMW("red");
const z4 = new BMW("yellow");

x5.__proto__ = car;
z4.__proto__ = car;

BMW.prototype.stop = function () {
  console.log("stop");
};

instanceof

  • 생성자 함수를 활용하여 새로운 객체를 생성해낼 때, 생성된 객체는 생성자의 인스턴스라고 불려진다.
  • 인스턴스인지를 확인하기 위해서 사용된다.
  • instanceof를 사용해서 생성자와 인스턴스를 구별할 수 있고, 객체가 생성자로부터 생성된 것인지를 확인하여 boolean값으로 나타낸다.
1
2
z4; // BMW {color: "yellow"}
z4 instanceof BMW; // true

constructor

  • 생성된 객체의 생성자를 가르킨다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
z4.constructor === BMW; // true

// prototype을 줄이기 위해 아래와 같이 코드를 작성하면
// constructor가 사라지기 때문에 프로퍼티를 하나씩 추가하는 것이 좋다.
BMW.prototype = {
  // constructor: BMW
  // 이렇게 수도로 명시해주면 해결할 수 있다.
  wheels: 4,
  drive() {
    console.log("drive");
  },
  navigation: 1,
  stop() {
    console.log("stop");
  },
};

z4.constructor === BMW; // false

클로저 활용

  • 상속시킨 프로퍼티에 직접적인 접근을 방지하기 위해 클로저를 활용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const BMW = function (color) {
  // this.wheels = 4;
  this.navigation = 1;
  this.color = color;
  // this.drive(){console.log("drive");};
};

const z4 = new BMW("yellow");
z4.color = "red"; // BMW{navigation: 1, color: "red"}

// 클로저 활용
const BMW = function (color) {
  // this.wheels = 4;
  this.navigation = 1;
  const c = this.color;
  this.getColor = function () {
    console.log(c);
  };
  // this.drive(){console.log("drive");};
};

Class

  • ES6에 추가된 스펙이다.
  • 객체를 만들어주는 생성자 메서드인 constructor가 있으며, new를 통해 호출한다.
  • 클래스 내에 정의한 메서드는 클래스의 프로토타입에 저장된다.
  • class가 아닌 생성자 함수는 new없이 실행하여도 undefined로 동작하지만, classnew없이 사용할 경우 타입 에러가 발생한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const User = function (name, age) {
  this.name = name;
  this.age = age;
  this.showName = function () {
    console.log(this.name);
  };
};
// {name: name, age: age, showName: function}

class User {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  showName() {
    console.log(this.name);
  }
}
// {name: name, age: age}
// __proto__: {constructor: class User, showName: function}
  • 생성자 함수는 프로토타입으로 상속이 가능하고, classextends를 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Cat {
  constructor(color) {
    this.color = color;
    this.wheels = 4;
  }
  drive() {
    console.log("drive");
  }
  stop() {
    console.log("stop");
  }
}

class BMW extends Car {
  park() {
    console.log("park");
  }
}

const z4 = new BMW("blue");
// {color: "blue", wheels: 4}
// __proto__: Car
//  constructor: class BMW
//  park: function
//  __proto__: class Car
//    drive: function
//    stop: function

메서드 오버라이딩

  • 만약 동일한 메서드가 존재하면 덮어쓰게 되지만 부모의 메서드도 그대로 사용하고 싶다면 super을 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Car {
  constructor(color) {
    this.color = color;
  }
  drive() {
    console.log("drive");
  }
  stop() {
    console.log("stop");
  }
}

class BMW extends Car {
  park() {
    console.log("park");
  }
  stop() {
    console.log("off");
  }
}

const z4 = new BMW("blue");
z4.stop(); // off

class BENZ extends Car {
  park() {
    console.log("park");
  }
  stop() {
    super.stop();
    console.log("off");
  }
}

const e = new BENZ("green");
e.stop(); // stop, off

오버라이딩

  • 상속관계가 있는 부모 클래스에서 이미 정의된 메서드를 자식 클래스에서 다시 정의하는 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Car {
  constructor(color) {
    this.color = color;
  }
  drive() {
    console.log("drive");
  }
  stop() {
    console.log("stop");
  }
}

class BMW extends Car {
  // 만약 새로운 메서드를 정의하고자 constructor을 쓴다면
  constructor() {
    this.navigation = 1;
  }
  park() {
    console.log("park");
  }
}

const z4 = new BMW("blue");
// Must call supter constructor in dervied class before accessing "this"
// constructor에서 this를 사용하기 전에 반드시 super을 사용하여 부모 생성자를 호출해야 한다.
  • 이는 constructor로 빈 객체를 만들고, this를 사용하여 프로퍼티를 만드는데, extends로 만들어진 자식 클래스는 이러한 과정을 건너뛰기 떄문에 super키워드로 부모 클래스의 constructor을 실행해줘야 한다.
  • 또한 자식 클래스의 constructor에 동일한 인수를 받는 작업을 해주어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Car {
  constructor(color) {
    this.color = color;
  }
}

class BMW extends Car {
  constructor() {
    super();
    this.navi = 1;
  }
}

const z4 = new BMW("blue");
// {color: undefined, navi: 1}

class BENZ extends Car {
  constructor(color) {
    super(color);
    this.navi = 1;
  }
}

const e = new BENZ("red");
// {color: "red", navi: 1}
  • 자식 생성자에 constructor가 없을 때, 코드는 없지만 자바스크립트는 부모 생성자를 그대로 받아와 사용한다.
  • 이때 constructorsuper없이 사용할 경우, 빈 contructor로 동작된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 기본 동작
class BMW extends Car {
  // constructor(...agr){
  // super(...agr);
  // }
  park() {
    console.log("parking");
  }
}

// 자식 constructor
class BMW extends Car {
  constructor() {
    this.park = function () {
      console.log("parking");
    };
  }
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.