💡 Vanilla JS 로 DOM 을 조작하고 사용자 정의 생성자 함수로 컴포넌트를 만드는 법에 대한 내용.
BONUS
생성자 함수 내 this 라는 변수를 참조할 수 잇다.
해당 함수의 프로토타입도 상속 받을 수 있다.
- prototype 을 통해 속성이나 메서드를 추가할 수 있고, new 객체로 할당받은 인스턴스도 상속을 통해 추가된 속성이나, 메소드를 사용할 수 있다.
Person.prototype.getName = function() {
return this.name;
};
console.log(p.getName()); // kim 이 출력된다.
new
와 함께 생성자 함수를 호출할 경우?
new
를 빼먹은 경우에?
var Person = function() {
this.name = "kim";
};
var p = Person('kim');
console.log(p.name); //error 발생
console.log(window.name); //kim 출력된다.
new
없이 동일한 결과를 얻으려면 → 스스로를 호출하는 생성자 패턴으로 해결할 수도 있다.var Person = function(name) {
// new 없이 사용했을 경우를 체크해 내부에서 new 를 통해 생성하고 처리.
if(!(this instanceof Person)) {
return new Person(name);
}
this.name = name;
};
var p = Person('kim');
console.log(p.name); //kim 가 출력
var p1 = new Person('kim');
console.log(p1.name); //kim 가 출력
객체 리터럴 방식 vs 생성자 함수 방식
객체 생성자 new Object()
의 함정
var obj = new Object();
console.log(obj.constructor === Object); //true
// 숫자
var obj = new Object(10);
console.log(obj.constructor === Number) ; //true
obj.toFixed(2); //1.00 이 기록
// 문자열 객체
var obj = new Object("I am a String");
console.log(obj.constructor === String); // true
// 일반적인 객체에는 substring() 이라는 메서드가 없지만 문자열 객체에는 있다.
console.log(typeof obj.substring());
// 불린 객체
var obj= new Object(true);
console.log(obj.constructor === Boolean): // true
this`` 가 어떤 값과 연결되는지는
this` 바인딩을 통해서 확인할 수 있다.this
각 특정 ‘객체' 에 연결되는 것이다.this
의 바인딩은 일반함수 내부, 메서드 내부, 생성자 함수 내부, call, apply, bind 등을 통한 ‘호출 방식' 으로 나눠서 살펴볼 수 있다.console.log(this === window); // true;
a = 30;
console.log(window.a); // 30
function x() {
return this;
}
x() === window; // true
let ryan = {
firstName: "Ryan",
lastName: "Kim",
driveCar() {
console.log(`${this.firstName} drives a car.`)
}
}
ryan.driveCar(); // 'Ryan drives a car.'
function person() {
this.firstName = "Ryan",
this.lastName = "Kim",
this.start = function() {
console.log(`${this.firstName} drives a car.`)};
};
let person1 = new person();
console.log(person1); // person { firstName: 'Ryan', lastName: 'Kim', start: ƒ (), __proto__: person { constructor: ƒ person() } }
Call
을 사용하면, 함수를 실행하고 함수의 첫 번째 인자로 전달하는 값에 this 를 바인딩 한다.function logName(a, b, c) {
console.log(this.name);
console.log(this.nationality);
console.log(a + b + c);
}
const person = {
name: 'Ryan',
nationality: 'South Korea'
}
apply
를 사용하면 함수를 실행하고 함수의 첫번째 인자로 전달하는 값에 this 를 바인딩한다.function logName(a, b, c) {
console.log(this.name);
console.log(this.nationality);
console.log(a + b + c);
}
const person = {
name: 'Ryan',
nationality: 'South Korea'
}
const nums = [1, 2, 3];
logName.apply(person, nums );
// 'Ryan'
// 'South Korea'
// 6
// 요즘에는 apply를 사용하지 않고, spread operator와 call을 활용한다.
logName.call(person, ...nums);
// 'Ryan'
// 'South Korea'
// 6
bind
는 함수의 첫 번째 인자에 this 를 바인딩한다는 점은 같지만function logName(a, b, c) { // 원본 함수
console.log(this.name);
console.log(this.nationality);
console.log(a + b + c);
}
const person = {
name: 'Ryan',
nationality: 'South Korea'
}
const Ryan = logName.bind(person); // 새로운 함수
Ryan(1,2, 3);
// 'Ryan'
// 'South Korea'
// 6
A.
Prototype
은 인스턴스의 private 속성을 통해 인스턴스 간에 하나의 메서드와 값을 공유합니다.this
는 생성자를 호출한 객체에 bind 되어. 인스턴스마다 다른 함수, 값을 가지게 됩니다.따라서, 결론적으로 각 인스턴스마다 달라져야 하는 값들은 this 를 사용하고, 모든 인스턴스들이 공유해도 되는 경우에는 prototype을 사용하는게 메모리 관리차원에서 좋은 방향일 것 같습니다.
this 와 prototype의 차이
Use of 'prototype' vs. 'this' in JavaScript?
하지만 리드 멘토이신 로토님께서는 this 를 주로 사용하신다는 답변을 받았는데, 아마 다음과 같이, 값을 인스턴스끼리 공유할 경우 변경될 수 있다는 점에서 this 를 사용하는 것이 아닐까하는 것이 나의 추측이다..
// x is a method assigned to the object using "this"
var A = function () {
this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
this.x = function() { alert( value ); }
};
var a1 = new A();
var a2 = new A();
a1.x(); // Displays 'A'
a2.x(); // Also displays 'A'
a1.updateX('Z');
a1.x(); // Displays 'Z'
a2.x(); // Still displays 'A'
// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };
B.prototype.updateX = function( value ) {
B.prototype.x = function() { alert( value ); }
}
var b1 = new B();
var b2 = new B();
b1.x(); // Displays 'B'
b2.x(); // Also displays 'B'
b1.updateX('Y');
b1.x(); // Displays 'Y'
b2.x(); // Also displays 'Y' because by using prototype we have changed it for all instances
A. 클로저를 이용한 기법을 많이 사용합니다.
function SomeComponent({ $target }) {
// renderItems는 외부에서 접근 불가능
const renderItems = () => {.... }
// render는 외부에서 접근 가능
this.render = () => {...}
}
클로저를 이용해서 프라이빗 메소드 (private method) 흉내내기
자바와 같이 몇몇 언어는 메소드를 프라이빗으로 선언할 수 있는 기능을 제공하는데, 이는 같은 클래스 내부의 다른 메소드에서만 그 메소드를 호출할 수 있다는 의미이다.
자바스크립트는 태생적으로는 이런 방법을 제공하지는 않지만
클로저를 이용해, 프라이빗 메소드를 흉내내는것이 가능하다
var counter = (function() {
// 클로저
var privateCounter = 0;
// 클로저
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1
위의 예제서 privateCounter
, changeBY
는 둘다 익명함수 외부에서 접근할 수 없다. 따라서, 이 두개 변수와 함수는 익명 함수에서 반환된 세개의 퍼블릭 함수를 통해서만 접근가능하다.
이런 디자인 패턴을 모듈 패턴
이라고 하는데, 위와 같이 클로저나, 익명함수를 사용해 객체를 리턴하고 변수를 함수 안에 선언하여 보호할 수 있다고 한다.
Vaniila JS 함수 컴포넌트 기반으로 SPA 를 구현해보며, 놓지고 있었던 부분을 되돌아 볼 수 있는 매우 유익한 시간이었다..
나는 회사에서 Vanilla JS 클래스형으로 SPA 를 구현해본적도 있는데, 막상 과제 요구사항을 제로베이스에서부터 구현해보려니 생각보다 막막한 부분이 많았다. ㅜ^ㅠ 내가 너무 리액트라는 편리한 프레임워크에 익숙해져 있던 탓이었겠지!