이번주차 과제에서 헷갈렸던 개념과 문법.. object 개념에 대해서 객체리터럴
자바스크립트의 객체는 키(key)
과 값(value)
으로 구성된 프로퍼티(Property)
들의 집합이다. 프로퍼티의 값
으로 자바스크립트에서 사용할 수 있는 "모든 값"
을 사용할 수 있고, 프로퍼티 값이 함수일 경우, 일반 함수와 구분하기 위해 메서드라 부른다.
👉 아래와 같이 객체 프로퍼티에 할당된 함수를 메서드(method) 라고 한다.
var obj = {
name: "sujin",
age: function foo() { // -> 메서드
// blah blah.. code..
}
}
객체 리터럴 안에 메서드를 선언할 때 단축 문법을 사용할 수 있다.
// 아래 두 객체는 동일하게 동작합니다.
user = {
sayHi: function() {
alert("Hello");
}
};
// 단축 구문을 사용하니 더 깔끔해 보이네요.
user = {
sayHi() { // "sayHi: function()"과 동일합니다.
alert("Hello");
}
};
위처럼 function
을 생략해도 메서드를 정의할 수 있다.
다만, 일반적인 방법과 단축 구문을 사용한 방법이 완전히 동일하진 않다.
과제를 진행하면서 내가 헷갈렸던 부분과 이해가 되지 않았던 부분
정말 간단한 개념이면서 예제이지만 나는 이조차 제대로 이해를 하지 못하였었다.
아래 코드에서 예를 들면,
const CanolaUI = {
/*
* 👉🏻 create 메소드입니다.
* CanolaError에 대해서 살펴보신 후, componentFactory로 이동하세요.
*/
create(options) {
validateOptions(options);
return componentFactory(options.template, options.events);
},
};
export default CanolaUI;
여기서 사용된 create()
메소드가 Object.create()
와 같은 것인 줄로만 알았다.
⛔ 하지만 둘은 연관성이 없으며, 위에서 사용된 create()
메소드는 객체 안에서의 자체 메소드이다.
Object.create()
에서 말하는 Object
는 객체를 뜻하는 네임을 쓰는 것이라고 생각했는데 그렇지 않고 저 문법 그대로 사용하여야 해당 메소드를 사용할 수 있는 것이다. 때문에 이 개념과 많은 혼동이 일어나 코드를 제대로 이해하지 못하였다.
위에 구문에서 create()
는 메서드 단축 구문을 이용한 객체안에서의 함수 값이다.
위에서도 말했듯 이를 메서드라고 부른다.
const CanolaUI = {
greeting: "hello",
create(options) {
return componentFactory(options.template);
}
}
console.log(CanolaUI.greeting); // "hello"
const Clock = CanolaUI.create({ ... });
create()
는 객체 안에서 정의된 함수 값이므로 CanolaUI
객체의 키 값으로 정의된 greeting을 불러오면 그 것의 값인 "hello"가 찍히는 것과 같이, CanolaUI.create({ ... });
는 말그대로 CanolaUI
객체의 값이 create
를 변수 Clock에 담은 것이다.
또, CanolaUI
에서 create()
는 인자로 options
을 받고 있기 때문에 이 options
의 값이 바로 CanolaUI.create({ ... });
에서 {...}
값이 인자로 전달되는 것이다.
이 다음으로 중요한 것이 그렇다면, CanolaUI.create()
가 리턴하는 값이 무엇이냐 이다.
로직을 따라가보면 리턴하는 값은 componentFactory(options.template);
임을 알 수 있고, 이 값이 무엇을 또 리턴하는지 알면 된다.
export default function componentFactory(generateTemplate) {
function Component(options) { ... }
Component.prototype.render = function () { ... };
Component.prototype.destroy = function () { ... };
return Component;
}
-> 피드백 내용)
내부 로직을 축소해서 보면 위와같은 형태로 함수가 만들어져있습니다. 리턴값을 한번 확인해보세요.
바로 Component
함수 즉, Pascal Case로 네이밍이 되어있기 때문에 Component
라는 생성자 함수를 리턴하는 것을 알 수 있습니다.
이쯤되면 이런 의문이 드실 수 있습니다. 왜 이렇게 복잡하게 생성자 함수를 리턴하는거야?
그 답은 배우셨던 prototype
과 closure
그리고 같은 함수 Context
로 묶기 위함에 있습니다.
componentFactory
가 인자값으로 받는 generateTemplate
가 이 내부에서 어떻게 쓰이고 있는지 확인해보세요.
이해하게 되신다면 이 구조를 파악하게 되실 수 있을 것 같습니다.
먼저 component는 요소, 부품이라는 뜻을 가지고 있다.
import componentFactory from "./Component";
import CanolaError from "./CanolaError";
이번주차 과제에서 이렇게 코드가 짜여져있어 처음에는 해당하는 파일 내에서 저 값들만 가져올수 있는거구나 했는데 아니었다.
export
를 이용하여 내보낸 값을 기준으로 다른 모듈에서 import
를 사용하여 내보낸 값을 복사해서 쓸 수 있는 것이다.
따라서 저 name명은 그냥 해당하는 모듈에서 export
로 내보낸 값을 쓰기 위한 네임명이라는 것이다.
export
로 어떤 값을 내보냈는지에 따라 네임명을 설정해주면 더 좋기때문에 저렇게 적은 것 같다.
MDN 인용 글)
import
문은 다른 모듈에서 내보낸 바인딩을 가져올 때 사용합니다.
가져오는 모듈은 "use strict"의 존재 유무와 상관없이 무조건 엄격 모드입니다. HTML 안에 작성한 스크립트에서는 import를 사용할 수 없습니다.
함수형 구문을 가진 동적 import()도 있으며, type="module"을 필요로 하지 않습니다.
<script>
태그의 nomodule 속성을 사용해 하위호환성을 유지할 수 있습니다.
✅ name
파라미터는 "export
되는 멤버를 받을 오브젝트의 이름"입니다. member
파라미터는 각각의 멤버를 지정하지만, name
파라미터는 모두를 가져옵니다. 모듈에서 name
은 멤버 대신 하나의 default
파라미터를 통해 export
하는 경우에도 동작할 수 있습니다. 다음의 명확한 예제 문법을 살펴봅시다.
아래 예시에서,
모듈 전체를 가져옵니다. export
한 모든 것들을 현재 범위(스크립트 파일 하나로 구분되는 모듈 범위) 내에 myModule 로 바인딩되어 들어갑니다.
import * as myModule from "my-module.js";
export
문은 JavaScript 모듈에서 함수, 객체, 원시 값을 내보낼 때 사용합니다. 내보낸 값은 다른 프로그램에서 import
문으로 가져가 사용할 수 있습니다.
내보내는 모듈은 "use strict"의 존재 유무와 상관없이 무조건 엄격 모드입니다. export
문은 HTML 안에 작성한 스크립트에서는 사용할 수 없습니다.
const html = generateTemplate.call(this).trim();
strict mode
에서 일반적인 호출방법으로 함수를 실행하면 this
의 값은 undefined
입니다.
generateTemplate
함수는 this
를 참조하고 있는 string
값이 return
되고 있고,
this
, 즉 undefined
는 어떠한 property
도 가지고 있지 않으므로 오류에서 "Cannot read property 'backgroundColor' of undefined" 라고 안내해주고 있습니다.
혹시 위의 내용이 이해가 정확하게 안간다면 this
에 대해서 조금 더 꼼꼼하게 검색해보시길 추천드립니다.
그리고 call
은 apply
, bind
와 함께 항상 세트처럼 설명되는 개념입니다.
3가지를 함께 비교해보고 어떤 차이점들이 있는지 알아보시길 추천합니다.
this, prototype 을 공부할 때 개인적으로 가장 도움이 되었던 자료입니다. 참고해주세요~
setInterval
은 clearInterval
로 사용중지를 하지 않으면 메모리 누수로 연결된다는 점을 유의해주셔야합니다.
과제의 흐름에 대해 간단하게 설명
일단 프로젝트의 가장 상위인 app.js
에서 출발해볼게요.
// in app.js
const Clock = CanolaUI.create({ ... });
Clock
변수에는 CanolaUI.create
가 실행되면서 리턴된 값이 할당되어있습니다.
// in /Canola/index.js
const CanolaUI = {
create(options) {
...
return componentFactory(options.template);
},
};
이를 확인하기 위해 CanolaUI
를 확인해보면,
CanolaUI
의 create method
는 componentFactory
의 리턴값을 다시 리턴합니다.
// in /Canola/Component.js
export default function componentFactory(generateTemplate) {
function Component(options) { ... }
Component.prototype.render = function () { ... } // 생성자함수의 prototype 객체에 render라는 method가 추가되었습니다.
Component.prototype.destroy = function () { ... } // 생성자함수의 prototype 객체에 destroy라는 method가 추가되었습니다.
return Component;
}
이를 확인하기 위해 componentFactory
를 확인해보면,
생성자함수 Component
를 리턴하고 있습니다. (생성자 함수는 보통 대문자로 시작합니다.)
돌고 돌았지만 간단히 정리해보면, Clock
변수에는 생성자함수 Component
가 담겨있다는 것을 알 수 있습니다.\
Clock
변수 -> CanolaUI.create
리턴값 -> componentFactory
리턴값 -> 생성자함수 Component
const myClock = new Clock({ ... });
myClock.render();
⭐ 이제 myClock
변수을 살펴보면,
myClock
변수에는 Clock
이라는 생성자함수를 new
와 함께 호출한 instance
가 할당됩니다.
myClock
이라는 instance
안에는 render
라는 method
가 없지만,
myClock.render
를 실행할 수 있는 이유는
프로토타입 체인이 동작하여 생성자함수 Component
의 prototype
객체에 추가되어있는 render method
를 사용할 수 있기 때문입니다.
render method
에서는 this.$el
처럼 this
가 등장합니다.
⛔ 여기서 this
는 render
가 실행되는 순간 결정되는데,
myClock.render()
, 즉 "dot notation" 방식으로 시행되었으므로, dot(.) 앞의 myClock
이 this
입니다.
과제와 관련되어 가장 기본적인 흐름이라고 생각되는 부분만 말씀드렸습니다.