[JavaScript] DOM, BOM, script, defer, async
프론트엔드 날개달기 : Vuejs, React 배우기 전에 꼭 알아야하는 지식
섹션 0
- DOM 이란 ?
- BOM 이란 ?
- script 태그 defer, async
- this란 무엇인가 ?
JS : html 문서를 제어하기 위해 만들어진 언어
렌더링 엔진
브라우저 내의 엔진으로, html 문서를 한 줄씩 해석하여 객체화함으로서 js가 접근할 수 있도록 함
javascript의 모든 객체는 속성과 메서드를 가지며, 속성은 값을 불러오는 역할, 메서드는 해당 값에 접근하는 역할을 한다. 이 때, 메서드는 값을 얻어오는 get- 과 값을 변경하는 set- 으로 나뉜다.
DOM 이란 ?
DOM (Document Object Model) : 문서 객체 모델
브라우저에서 js로 html을 제어하도록 제공하는 API(브라우저에서 제공하는 기능)
* CSS Object Model : css를 제어하도록 한 것
트리구조
html 문서는 웹의 렌더링 엔진에 의해 해석되어 DOM 트리로 변환된다. 트리는 하나의 부모태그와 n개의 자식태그를 가지는 구조로, 이 경우 각각의 태그는 노드로 구조화할 수 있으며, 이 때 js는 각각의 노드에 접근하여 제어한다.
* document 객체 : dom 트리의 최상위 객체로 문서 자체, 즉 진입점을 나타낸다.
document.getRootNode() -> #document
// chileNodes 를 사용하여 자식노드 접근 가능
BOM 이란 ?
BOM (Browser Object Model) : 브라우저 객체 모델
웹 브라우저를 객체화 한 것으로, 이 때 객체화는 브라우저를 스크립트언어로 제어하기 위한 작업이다.
Browser 객체
Object | Discription |
---|---|
window | 모든 객체가 소속된 객체이며, 브라우저 창을 의미 |
document | 현재 문서(DOM)에 대한 정보를 갖고 있는 객체 |
history | 현재의 브라우저가 접근했던 URL history를 제어 |
location | 문서의 주소와 관련된 객체로, window 객체의 프로퍼티인 동시에 document의 프로퍼티. 이를 이용하여 윈도우의 문서 URL 변경 또는 문서의 위치에 대한 정보 취득 가능 * property : 객체를 구성하는 블록 |
screen | 사용자의 디스플레이 화면에 대한 정보를 지님 |
navigator | 실행 중인 애플리케이션에 대한 정보를 알 수 있으며, 크로스 브라우징 이슈를 해결할때 사용할 수 있다. |
사용 예시
// window 객체
// window. 은 생략 가능
window.open('URL') // URL 의 페이지가 열림
window.close() // 해당 창이 닫힘
// document 객체
// window에 포함된 객체로 window.document로 사용 가능하다 window. 생략
document.querySelector('#id') // 앵커 선택
document.querySelector('#id').textContent = 'hello' // content 변경
// history 객체
history.back() // 뒤로가기
history.forward() // 앞으로 가기
// location 객체
location.host // 현재 페이지의 호스트 반환
loscation.href = 'URL' // 주소를 URL로 변경
location.reload() // 현재 페이지의 리소스를 다시 불러옴
// screen 객체
console.dir(screen) // 현재 디스플레이의 사이즈를 포함한 다양한 정보
// navigator 객체
navigator.geolocation.getCurrentPosition() // 현재 앱에 대한 위치 정보
navigator.appName // 앱(브라우저) 이름
navigator.appVersion // 앱(브라우저) 버전 정보
navigator.userAgent // 서버에 요청할 때 앱에 대한 정보
script 태그 defer, async
<script src=""></script>
<button id = "btn">button</button>
let btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
alert('Hello World!')
;})
위 코드에서 error 가 발생하는 이유
브라우저에서는 html 파일을 만났을 때 순차적으로 코드를 읽기에, script 태그를 만나면 script 를 실행한다. 이 때, #btn이 파싱되기 전에 script 를 삽입하면, document.querySelector(‘#btn’) 의 값은 null이 되기에 error가 발생하는 것이다.
해결 방법
body 태그의 최 하단에서 script 로드
html의 파싱이 완료된 후 script 실행
<button id = "btn">button</button>
<script src=""></script>
load 이벤트 리스너 등록
- window.onload : html 파싱 및 DOM 생성, 외부 컨텐츠 로드 완료 후 발생하는 이벤트
window.onload = function(){ let btn = document.querySelector('#btn'); btn.addEventListener('click', function(){ alert('Hello World!'); }); };
* onload 이벤트는 외부 컨텐츠가 많을 경우 비효율적
- DOMContentLoaded : html 파싱 및 DOM 생성 후 발생하는 이벤트
document.addEventListener('DOMContentLoaded', function(){ let btn = document.querySelector('#btn'); btn.addEventListener('click', function(){ alert('Hello World!'); }); });
* onload보다 더 빠르게 실행됨 : 두 방법을 모두 작성 후 실행하여 확인했을 시, DOMContentLoaded로 작성된 코드가 실행되어 나타나는 것을 볼 수 있음
html 파싱과 동시에 script 태그를 받아올 수는 없을까 ?
DOMContentLoaded 의 경우, html parsing -> Script fetch -> script execution 의 단계로 작업이 수행된다. 이 때, html parsing 단계에서 script fetch를 진행하기 위해, html5에서 defer와 async 가 등장하였다.
- defer
defer 속성은 HTML 파싱과 함께 비동기로 javascript 파일을 불러오며, HTML 파싱 후, 불러온 코드를 실행한다. 따라서, 선택자가 가리키는 요소가 파싱되지 않았더라도, 오류가 발생하지 않는다
<script src="" defer></script>
- async
defer과 같이 HTML 파싱 과정에서 비동기로 javascript 파일을 불러오나, 파싱이 완료되지 않았더라도, 먼저 로딩되는 javascript 파일이 실행된다. 따라서, 선택자가 가리키는 요소가 파싱되지 않은 경우 오류가 발생한다
<script src="" async></script>
this란 무엇인가 ?
this는 호출한 객체를 가리키며, 호출한 객체가 존재하지 않을 경우 기본적으로 window 객체를 가리킨다.
예시 1
let person = {
name: 'psw',
age: 22,
printThis: function(){
console.log(this === person);
},
};
let printThis = person.printThis;
person.printThis(); // true
printThis(); // False, this === window
예시 2 : html에서 javascript를 호출했을 경우
<script src="" defer></script>
<button>button</button>
let btn = document.querySelector('#btn');
btn.addEventListener('click', function () {
console.log(this); // <button>button</button>
console.log(this === btn); // true
});
예시 3 : this 를 설정하고 싶을 때
function printThis(){
console.log(this);
}
let person1 = {
name: 'psw',
};
let printThis1 = printThis.bind(person1);
printThis1(); // {name: 'psw'}
그러나, 아래의 코드를 실행한 결과 또한 위와 동일하게 {name: ‘psw’} 가 되는 것을 볼 수 있는데, 그 이유는 bind는 한 객체에 한 번만 등록할 수 있기 때문이다.
let person2 = {
name: 'bsw',
};
let printThis2 = printThis1.bind(person2);
printThis2(); // {name: 'psw'}
예시 4
let person = {
name = 'psw',
hello: function(){
setTimeout(function(){
console.log(this); // Window { ... }
console.log(this.name); // undefined
}, 1000);
},
};
print.hello();
위 코드의 경우, this.name을 출력하려면 아래와 같이 this를 바인딩 해주어야 한다
let person = {
name = 'psw',
hello: function(){
setTimeout(function(){
console.log(this); // {name: 'psw'}
console.log(this.name); // psw
}.bind(this), 1000); // setTimeout의 this는 person
},
};
print.hello();
예외
화살표 함수 (Arrow Function)
화살표 함수는 자신을 포함하는 외부 스코프에서 this를 계승받는 성질을 지닌다
let person = {
name = 'psw',
hello: function(){
setTimeout(() => {
console.log(this); // {name: 'psw'}
console.log(this.name); // psw
}, 1000);
},
};
print.hello();
예시 4의 첫 코드와 동일한 코드에서 콜백 함수를 화살표 함수로 변경한 결과, 외부 스코프를 계승받아 Window가 아닌, person이 this가 되는 것을 볼 수 있다
화살표 함수를 사용하면 안 되는 경우
객체 메서드를 선언할 때
let person = {
name = 'psw',
printThis: () => {
console.log(this); // window 객체 (person이 지닌 전역 스코프를 계승받음)
}
};
addEventLister 함수의 콜백함수
let btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
console.log(this); // window 객체
console.log(this === btn); // false
});
Strict Mode
strict mode 에서는 호출한 객체가 없을경우, 기본값이 window 가 아닌 undefined가 된다
// non strict mode
function printThis(){
console.log(this);
}
printThis(); // Window
// strict mode
'use strict';
function printThis(){
console.log(this);
}
printThis(); // undefined