객체가 선언되었다면, 콘솔에서 해당 객체에 해당하는 property가 소속되어 있는지(true, false) 확인할 수 있다. 만약 객체에 존재하지 않는 age를 물어본다면 false가 뜰 것이다.
property의 경우, 상속의 개념으로 이전에 선언된 객체를, 이후에 선언된 객체에 상속시킬 수 있다. 하단이미지출처: 코딩앙마
먼저 bmw 객체에 언더바2개 proto = 으로 car가 상속되었고, 같은 방법으로 x5에 bmw가 상속되었다. 콘솔에서 결과를 살펴보자. x5.hasOwnProperty('name')을 콘솔에 입력해보자. 해당 property를 x5가 가지고 있음으로 true가 나온다. 그렇다면 x5.hasOwnProperty('wheels')을 실행해보자. false가 뜬다. 면밀하게 따지면 x5객체 내에는 wheels에 대한 정보가 없지만, proto = 을 통하여 상속받은 값으로 해당 property에 접근하는 것이다. 위의 이미지에서 볼 수 있듯, x5에 navigation property는 없지만 상속받은 bmw로부터 해당 key의 값을 가져왔다. 또한 x5.wheels 역시 x5에 wheels property는 없지만 bmw에 상속된 car로부터 해당 정보를 가져와서 추출하였다. 이를 프로토타입 체인이라고 부른다.
그 결과 for(in) 반복문으로, x5에 기록된 property 전부를 살펴볼 수 있다. 비록 자신의 property에는 존재하지 않더라도, 상속을 통해서 연결되었다면, 전부 자신의 property화 하는 것이다. 그러나 자신의 property에 존재하는지 묻는 hasOwnProperty에서는 상속된 property의 경우 false가 뜬다.
콘솔에서 x5, 또는 x5의 key, value에 접근하더라도 자신의 객체 안에 담고 있지 않은 상속된 내용의 key와 value는 뜨지 않는 것을 볼 수 있다.
const car = {
wheels : 4,
drive() {
console.log('drive..')
}
};
const bmw = {
color : "red",
navigation :1,
};
const x5 = {
color : 'white',
name:'x5',
};
bmw.__proto__ = car;
x5.__proto__ = bmw;
위의 코드에 대하여 콘솔에 아래의 코트로 기록해 보면,
for (p in x5) {
if(x5.hasOwnProperty(p)) {
console.log("o", p);
} else {
console.log("x", p);
}};
x5가 반영하고 있는 property 가운데 자신의 property는 무엇이고(o), 상속되어 가진 property는 무엇인지(x) 확인할 수도 있다.
프로토타입은 생성자함수를 선언하면서 사용할 수 있는데, 그 활용법은 아래와 같다. 변수를 프로토타입 체인으로 연결한 것이 아니라, 생성자함수에 부족한 부분을 prototype 메소드로 직접 추가하는 방법이다.
const Bmw = function (color) {
this.color = color;
};
Bmw.prototype.wheels =4;
Bmw.prototype.drive = function() {console.log("drive...")};
Bmw.prototype.navigation =1;
Bmw.prototype.stop = function() {console.log("STOP!")};
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
콘솔에서 보면 프로토타입체인에서 보았던 것처럼, prototype으로 연결해준 정보들을 가져올 수 있는 것을 볼 수 있다. 그러나 생성자 함수로 생성한 x5객체에 해당 property가 존재하는지 묻는 명령을 실행하 보면, color에 대해서는 true이지만, wheels에 대해선 false가 나오는 것을 볼 수 있다. 또한 위에서 선언했던 조건문을 콘솔에서도 실행해보자. 만약 hasOwnProperty, 즉 객체에 있는 property면 "o"를, 그렇지 않으면 "x"를 기록하라 명령하면 아래와 같이 x5의 각각의 property의 정보를 확인해 볼 수도 있다.
생성자함수를 통해서 쉽게, 객체를 생성하고, 추가된 prototype을 함께 첨가할 수 있다. 여기서 Edwin velog에서 처음 다루는 인스턴스라는 개념이 등장한다. 인스턴스란 쉽게 생성자 함수를 통해서 새로 생성된 객체를 지칭하는 용어이다. 위의 코드에서 살펴보면 x5와 z4는 생성자 함수를 통해서 생성된 각각의 인스턴스이다. 즉 인스턴스란 생성자 함수의 결과물이라고 볼 수 있다.
instanceof
연산자는 인스턴스의 prototype 속성이 객체의 프로토타입 체인 어디에 존재하는지를 판별할 때 사용된다.
구문 : object instanceof constructor
이를 문장으로 기록하면 instanceof 연산자는 object의 프로토타입 체인에 constructor.prototype이 존재하는지 판별한다. 판별의 결과는 true 또는 false로 결과를 가르쳐준다. 또는 .constructor === 함수명
으로 직접 판별하는 것도 가능하다.
Bmw.prototype.wheels =4;
Bmw.prototype.drive = function() {console.log("drive...")};
Bmw.prototype.navigation =1;
Bmw.prototype.stop = function() {console.log("STOP!")};
위에서 prototype
을 여러 줄로 작성했던 부분을 객체로 묶어서 한 묶음으로 기록하는 것도 가능하다.
const Bmw = function (color) {
this.color = color;
};
Bmw.prototype = {
wheels = 4,
drive() {console.log("drive...")},
navigation = 1,
stop() {console.log("STOP!")},
};
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
작동은 동일하게 된다. 그런데 이렇게 리펙토링하면 위의 코드와 문제가 발생된다. 어떤 부분인지 콘솔에서 확인해보자.
x5 instanceof Bmw
는 true를 반영했지만, x5.constructor === Bmw
는 false를 반영했다는 점이다. 코딩앙마의 강의에서는 이러한 문제 때문에 객체로 묶어서 prototype을 정의하지 말고, 앞선 방법과 같이 하나씩 정의할 것을 권한다.
그렇다면 질문이 생긴다. constructor
은 무엇인가? 역시 모질라
를 찾아보자.
constructor는 클래스의 인스턴스 객체를 생성하고 초기화 하는 메소드이다. 자 여기서 클래스란 프로토타입 기반 상속을 사용하여, 주어진 이름의 새로운 클래스를 선언하는 것을 뜻한다. 인스턴스를 형성하는 함수인 Bmw의 초기화에 관여하여 작동되는 것을 의미하는 것 같은데, 별도의 선언은 이를 true로 접근하지만, 객체로 묶여진 선언은 이에 접근하지 못하여 false가 된다는 의미 정도로만 현재는 알고 넘어가자. 만약 이를 해결하고 싶다면 객체 안에 constructor을 수동으로 기록하는 방법도 있다.
Bmw.prototype = {
constructor : Bmw,
wheels = 4,
drive() {console.log("drive...")},
navigation = 1,
stop() {console.log("STOP!")},
};
이렇게 입력하면, constructor : Bmw도 하나의 property가 되어 생성되는 것을 볼 수 있다.
상속과 프로토타입을 다루면서, 클래스를 간략하게 언급하였다. 그러나 충분히 설명하지 못했는데, 여기에서 설명하겠다. 먼저 생성자 함수를 살펴보자.
const User = function(name. age) {
this.name = name;
this.age = age;
this.showName = function() {
console.log(this.name);
};
};
const mike = new User("Mike", 30)
일반적으로 위와 같이 새로운 객체(인스턴스)를 생성했었다. 그러나 이러한 인스턴스 생성은 클래스로도 생성이 가능하도록 설정되었는데, ES6(자바스크립트의 표준, 규격을 나타내는 용어, 2015년 출시) 이후에 추가된 개념이다. 아래를 보자.
class User2 {
constructor(name, age) {
this.name = name;
this.age = age;
}
showName() {
console.log(this.name);
};
};
const tom = new User2("Tom", 19)
인스턴스의 명으로 콘솔에서 호출하면 같은 내용으로 약간의 차이를 볼 수 있다. 바로 showName()
property에 대한 부분이다. Tom에서 showName()
은 prototype에 기록되어 있다. 물론 생성자 함수에서도 이를 실행해 줄 수 있다.
const User = function(name. age) {
this.name = name;
this.age = age;
};
};
User.prototytpe.showName() {
console.log(this.name); };
const mike = new User("Mike", 30);
위와 같이 말이다. 해당 property들은 for(in)문으로 호출하여 그 key들을 볼 수 있었는데, 여기서 생성자함수와 클래스의 차이가 발생한다. 생성자 함수는 prototype의 property들도 보여주지만, 클래스는 그러지 못한다. 또한 새로운 인스턴스를 생성할 때, new를 붙이지 않아도 생성자함수는 오류를 야기할 수 있지만 생성은 한다. 단지 정의가 되지 않아 인자가 매개변수로 전달이 되지 않을 뿐이다. 이또한 콘솔을 통해서 확인할 때 오류를 확인할 수 있다. 그러나 클래스의 경우는 선언을 할 때 부터 new를 붙이지 않으면 "Cannot call a class constructor without |new|"와 같이 개발자에게 해당 부분에 오류가 있음을 말해준다. 즉 보완적 측면으로 보강되었다.
class Car {
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")
위에서 생성된 인스턴스 z4는 bmw 클래스로 인하여 생성되며, color과 wheels의 property를 가지게 되었다. 그러나 park()의 경우에는 Bmw.prototype으로 생성된 property가 되며, drive()같은 경우에는 Bmw에 상속된 Car.prototype으로 생성된 property가 되는 방식으로 작동이 이뤄진다.
class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
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")
Bmw.prototype과 Car.prototype에 동일한 prototype이 존재한다고 할 때 작동은 먼저 탐색이 이뤄지는 Bmw.prototype이 동작된다. 즉 덮어쓰게 된다. 그러나 둘 다 사용하고 싶다면, 상속을 받는 Bmw.prototype의 property에 super.프로퍼티명
을 입력하면 된다.
class Bmw extends Car{
park() {
console.log("PARK");
}
stop() {
super.stop()
console.log("OFF!");}
}
이렇게 입력을 해주고, 콘솔에 z4.stop()을 실행하면, Car.stop()과 Bmw.stop()을 모두 가져올 수 있게 된다. 이런 방식을 오버라이딩이라고 한다.
class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive...");
}
stop() {
console.log("STOP!");
}
}
class Bmw extends Car{
constructor(color) {
super(color);
this.navigation = 1;
}
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue")
부모클래스(Car) 아래 새로 생성한 자녀클래스(Bmw)를 통해서 인스턴스에 직접 property를 생성하고자 할 때는 constructor()와 더불어서, super()를 위와 같이 설정해주면 가능하다. 그런데 먼저 자녀클래스가 인스턴스를 생성하기에, 인자를 받아서 매개변수에 기록하기 위해 (color)를 선언해야 하며, 이러한 작업을 통해서 부모클래스에까지 인자를 매개변수로 전달한다. 이 부분이 생략되면 부모클래스에는 새로운 인스턴스를 실행하며 전달한 인자가 전달되지 못한다.
프로미스는 쉽게 이렇게 이해할 수 있다. 고객이 상점으로부터 물건을 구매할 때, 예약에 대한 전화번호를 남겼다고 하자. 상점은 고객의 물건이 완료되면 전화번호로 고객에게 해당 정보를 전달할 것이다. 이런 과정이 코드 상에서도 가능한데, 이를 가능하게 하는 것이 바로 promise
이다.
구문 : const pr = new Promise((resolve, reject) => {});
성공했을 때 실행되는 과정을 어려운 말로 callback
이라고 부른다. 과정은 아래와 같다.(출처, 코딩앙마)
프로미스를 사용하지 않은 예제를 살펴보자.
const f1 = (callback) => {
setTimeout(function() {
console.log("1번 주문 완료");
callback();
}, 3000);
};
const f2 = (callback) => {
setTimeout(function() {
console.log("2번 주문 완료");
callback();
}, 3000);
};
const f3 = (callback) => {
setTimeout(function() {
console.log("3번 주문 완료");
callback();
}, 3000);
};
console.log("시작")
f1(function() {
f2(function() {
f3(function() {
console.log("끝")
})
})
})
다음은 위의 코드를 프로미스 체이닝으로 실행할 결과이다.
const f1 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("1번 주문 완료");
}, 3000)
});
};
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("2번 주문 완료");
}, 3000)
});
};
const f3 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
res("3번 주문 완료");
}, 3000)
});
};
console.log("시작")
f1()
.then((res) => f2(res))
.then((res) => f3(res))
.then((res) => console.log(res))
.catch(console.log)
.finally(() => {
console.log("끝")});
만약 하나의 정보가 reject 되었었다면 해당 부분을 출력한 후에 promise는 종료된다.
const f2 = (message) => {
console.log(message);
return new Promise((res, rej) => {
setTimeout(() => {
rej("xxx");
}, 3000)
});
};
이후에 PromiseAll이 나왔지만 도통 이해하지 못하겠다. 이후의 과정은 현재 단계에서는 기록은 하되 이해는 못하는 상태인 것 같으므로, 추후에 남겨두려고 한다. 이렇게 함으로 코딩앙마 코치의 강의를 종결하고자 한다. 이후에는 Ajax에 대한 생활코딩의 강의를 이어가려고 한다.
author. EDWIN
date. 23/02/02