브라우저 객체 모델(BOM)의 핵심이자 브라우저의 인스턴스
브라우저 객체 모델(BOM)이란, 브라우저 창에서 접근하고 조작할 수 있게 하는 인터페이스
브라우저 객체 모델(BOM)은 브라우저 창과 보이는 영역인 window를 기반으로 만들어진다.
window 객체 모든 객체의 최상위에 존재하기 때문이다. 브라우저의 모든 요소들과 자바스크립트 엔진, 변수들은 모두 window 객체에 담겨져있다.
Document of Model의 약자.
javascript에서 doument로 접근 가능. 자식의 최상의 문서는 html
문서 형식 선언으로, 문서 상단에 1회 선언돠며 브라우저 랜더링 또는 유효성 검사시 문서 형식을 확인하는데 쓰인다.
브라우저의 역할은 사용자의 요청을 서버에 전달하고 그 결과를 화면에 나타내는 과정인데 렌더링은 화면에 나타내는 과정을 의미한다.
더 상세하게 알아보면,
- DOM 생성
- CSSOM (CSS Object Model) 생성
- Render Tree 생성
- Render Tree 배치
- Render Tree 그리기 이러한 과정을 통해 브라우저가 서버에 요청한 내용의 노드들을 픽셀화 시키는 것을 브라우저 렌더링 이라고 한다.
자바스크립트의 코드들이 실행되기 위한 환경
전역 컨텍스트, 함수 컨텍스트 2가지 존재
- 전역 컨텍스트 하나 생성 후에 함수 호출할 때마다 함수 컨텍스트가 생성
컨텍스트를 생성시에 변수객체, 스코프 체인, this가 생성됨- 컨텍스트 생성 후 함수가 실행되는데 사용되는 변수들은 변수 객체 안에서 값을 찾고 없다면 스코프체인을 따라 올라가며 찾음
- 함수 실행이 마무리되면 해당 함수 컨텍스트는 사라짐. 페이지가 종료되면 전역 컨텍스트가 사라짐
즉, 자바스크립트의 코드가 실행되기 위해서는 변수객체, 스코프체인, this 정보들을 담고 있는 곳을 실행컨텍스트라고 부른다.
2020년 6월27일 추가
ECMAScript 스펙에 따르면 실행 컨텍스트를 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라고 정의한다. 좀 더 쉽게 말하자면 실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경 이라고 말할 수 있겠다.
물리적으로 가지는 3가지의 객체: Variable Object, Scope Chain, thisValue
Variable Object는 아래의 정보를 담는 객체이고, 실행 컨텍스트의 프로퍼티이기 때문에 값을 갖는데 이 값은 다른 객체를 가리킨다.
전역 컨텍스트의 경우
Variable Object는 유일하며 최상위에 위치하고 모든 전역 변수, 전역 함수 등을 포함하는 전역 객체(Global Object / GO)를 가리킨다. 전역 객체는 전역에 선언된 전역 변수와 전역 함수를 프로퍼티로 소유한다.
함수 컨텍스트의 경우
Variable Object는 Activation Object(AO / 활성 객체)를 가리키며 매개변수와 인수들의 정보를 배열의 형태로 담고 있는 객체인 arguments object가 추가된다.
전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고 있다.
다시 말해, 스코프 체인은 해당 전역 또는 함수가 참조할 수 있는 변수, 함수 선언 등의 정보를 담고 있는 전역 객체(GO) 또는 활성 객체(AO)의 리스트를 가리킨다.
실행 컨텍스트 자세한 내용!
실행 컨텍스트 간단한 내용!
함수 안에 또 다른 함수를 정의 하고 사용하는 것
내부 함수가 자신이 선언 됐을 때의 환경인 스코프를 기억하여 외부 환경에서 호출되어도 그 환경에 접근할 수 있는 함수, 즉 자신이 생성될 때의 환경을 기억하는 함수
var base = 'Hello, ';
function sayHelloTo(name) {
var text = base + name;
return function() {
console.log(text);
};
}
var hello1 = sayHelloTo('승민');
var hello2 = sayHelloTo('흥민');
var hello3 = sayHelloTo('증민');
hello1(); // 'Hello, 승민'
hello2(); // 'Hello, 흥민'
hello3(); // 'Hello, 증민'
출력된 결과를 보면 text 변수가 동적으로 변화하고 있는 것처럼 보인다. 실제로는 text라는 변수 자체가 여러 번 생성된 것이다.
즉, hello1()과 hello2(), hello3()은 서로 다른 환경을 가지고 있고 클로저를 통해 각자의 환경을 기억하고 있다.
사용 하는 이유 :
2020년 6월28일 추가
var counter = function() {
var count = 0;
function changeCount(number) {
count += number;
}
return {
increase: function() {
changeCount(1);
},
decrease: function() {
changeCount(-1);
},
show: function() {
alert(count);
}
}
};
var counterClosure = counter();
counterClosure.increase();
counterClosure.show(); // 1
counterClosure.decrease();
counterClosure.show(); // 0
counter 함수를 호출 할 때, counterClosure 컨텍스트에 counterClosure와 counter가 담긴 scope chain이 생성된다. 그렇게 되면 이제 counterClosure에서 계속 count로 접근할 수 있다. return 안에 들어 있는 함수들은 count 변수나, changeCount 함수 또는 그것들을 포함하고 있는 스코프에 대한 클로저라고 부를 수 있다.
프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거, 즉 코드의 재사용
생성자 함수가 생성할 모든 인스턴스가 공통적으로 사용할 프로퍼티나 메소드를 프로토타입에 미리 구현해 놓음으로써 또 구현하는것이 아니라 상위 객체인 프로토타입의 자산을 공유하여 사용한다.
__proto__
로 자신의 프로토타입, 즉 Prototype 내부슬롯에 접근 할 수 있다.
프로토타입체인 이란?
1. 객체의 프로퍼티에 접근하려고 할때 객체에 접근하려는 프로퍼티가 없으면,__proto__
가 가리키는 링크를 따라 자신의 부모역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색
2. 프로로타입체인의 최상위 객체는 Object.prototype이다. 이 객체의 프로퍼티와 메소드는 모든 객체에게 상속됨
2020년 6월28일 추가
__proto__
는 생성자 함수를 new로 호출할 때, 정의해두었던 prototype을 참조한 객체__proto__
는 new를 호출할 때 prototype을 참조하여 자동으로 만들어짐__proto__
__proto__
는 prototype이 제대로 구현되었는지 확인용으로 사용한다.function Ultra(){}
Ultra.prototype.ultraProp = true;
function Super(){}
Super.prototype = new Ultra();
function Sub(){}
Sub.prototype = new Super();
var o = new Sub();
console.log(o.ultraProp); // true
생성자 Sub를 통해서 만들어진 객체 o가 Ultra의 프로퍼티 ultraProp에 접근 가능한 것은 prototype 체인으로 Sub와 Ultra가 연결되어 있기 때문이다. 내부적으로는 아래와 같은 일이 일어난다.
this가 함수 호출식에 따라 객체를 가르켰다면 call apply bind는 함수가 직접 실행 문맥을 결정한다. 그 중에 call, apply는 함수를 호출해 실행한다. call apply를 사용했을때의 장점은 첫번째 인자가 없더라도 에러없이 실행이 가능하다(자동으로 전역객체를 지정하여 실행) bind는 지정한 객체의 함수를 만든다. 정리하면 call apply는 함수를 실행시켜주는 것이고, bind는 새 함수를 만들어주는 것이다.
const obj = { name: 'Tom'};
const say = function(city) {
console.log(`Hello, my name is ${this.name}, I live in ${city}`);
}
say('seoul'); // Hello, my name is , I live in seoul
say.call(obj, 'seoul'); // Hello, my name is Tom, I live in seoul
say.apply(obj, ['seoul']); // Hello, my name is Tom, I live in seoul
내부 함수에서는 외부 함수의 변수에 접근 가능하지만 외부 함수에서는 내부 함수의 변수에 접근할 수 없다. 그리고 모든 함수들은 전역 객체에 접근할 수 있다.
var name = 'zero';
function outer() {
console.log('외부', name);
function inner() {
var enemy = 'nero';
console.log('내부', name);
}
inner();
}
outer();
console.log(enemy); // undefined
inner 함수는 name 변수를 찾기 위해 먼저 자기 자신의 스코프에서 찾고, 없으면 한 단계 올라가 outer 스코프에서 찾고, 없으면 다시 올라가 결국 전역 스코프에서 찾음
전역 스코프에서 name 변수를 찾아서 'zero'라는 값을 얻음
만약 전역 스코프에도 없다면 변수를 찾지 못하였다는 에러가 발생
이렇게 꼬리를 물고 계속 범위를 넓히면서 찾는 관계를 스코프 체인이라고 부른다.
어떤 특정 함수가 실행을 마친 후에 실행되는 함수
함수는 인자로써 함수를 받을 수 있고, 다른 함수를 리턴할 수 있다. 이러한 특징을 가지는 함수를 고차함수(higher-order functions) 라고 한다.
인자로 전달되는 함수를 콜백 함수라고 부른다.
사용되는 이유: 비동기식 처리를 위해서
바로 처리될 수 없는 함수 (예: setTimeout)의 요청이 있다면 자바스크립트는 비동기 처리를 기다려주지 않는다.
순서를 보장하기 위해 함수의 인자로 콜백함수를 넣고, 비동기 처리가 끝나는 시점에 콜백함수를 실행시켜주는 것.
정리하면 콜백함수는 다른 함수의 실행이 끝날 때까지 특정 코드가 실행되지 않게 기다려주는 방법이다.
C언어와 같은 언어는 소스 파일을 작성한 후, 해당 파일을 컴파일(compile)하여 사용자가 실행할 수 있는 실행 파일(.exe)로 만들어 사용한다.
하지만 인터프리터 언어는 이러한 컴파일 작업을 거치지 않고, 소스 코드를 바로 실행할 수 있는 언어를 의미한다.
자바스크립트는 웹 브라우저에 포함된 자바스크립트 인터프리터가 소스 코드를 직접 해석하여 바로 실행해 준다.
자바스크립트 ES6 특징들
여기에 자세하게 나와있다. 참고해보자^^
Promise를 다루기 위해 생긴 문법
async function f() {
return 1;
}
f().then(alert); // 1
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000);
});
let result = await promise; // promise가 resolve될 때까지 기다립니다 (*)
alert(result); // 끝입니다!
}
f();
싱글쓰레드인 자바스크립트에서 비동기 처리를 위해서 콜백(callback)을 사용하기 때문에 콜백의 중첩으로 인한 복잡도가 증가, 에러/예외 처리의 어려움이 있다.
이 문제를 프로미스로 해결한다.
예외처리 실패 예시:
try {
setTimeout(() => { throw 'Error!'; }, 1000);
} catch(e) {
console.log('에러 캐치 불가능...');
console.log(e);
}
try-catch에서 비동기 함수의 콜백메서드에서 에러를 만들지만 catch하지 못한다.
(setTimeout()함수의 콜백은 이벤트큐에 있다가 콜스택이 비어지면 실행되기 때문)
프로미스 생성, 실행:
//프로미스 생성
const promise1 = function (param) {
return new Promise (function (resolve, reject) {
if (param) {
resolve ("합격");
}
else {
reject ("불합격");
}
});
}
//프로미스 실행
promise1(true).then(function(result) {
console.log(result); // 합격
}, function(err) {
console.log(err); // 불합격
});
결국 비동기함수를 만들어서 사용해야할 때 프로미스 객체를 리턴하게 만들어서 사용하면 콜백 중첩을 방지할 수 있고 에러처리를 수월하게 할 수 있다!
ES6부터 변수 선언에 let과 const가 추가 되면서 var 변수 선언을 권장하지 않는다
var
은 변수 재선언이 가능하다.
// 이미 만들어진 변수이름으로 재선언했는데 아무런 문제가 발생하지 않는다.
var a = 'test'
var a = 'test2'
이는 유연한 변수 선언으로 간단한 테스트에는 편리 할 수 있겠으나, 코드량이 많아 진다면 어디에서 어떻게 사용 될지도 파악하기 힘들뿐더러 값이 바뀔 우려가 있다.
let
과 const
는 변수 재선언이 불가능 하다.
둘의 차이점 여부는 immutable, 불변성 여부이다.
let
은 변수에 재할당이 가능하지만, const
는 변수 재선언, 재할당 모두 불가능하다.
// let
let a = 'test'
let a = 'test2' // Uncaught SyntaxError: Identifier 'a' has already been declared
a = 'test3' // 가능
// const
const b = 'test'
const b = 'test2' // Uncaught SyntaxError: Identifier 'a' has already been declared
b = 'test3' // Uncaught TypeError:Assignment to constant variable.
호이스팅을 알아보자.
자바스크립트는 ES6에서 도입된
let
,const
를 포함하여 모든 선언(var, let, const, function, function*, class)을 호이스팅한다. 호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.
변수는 선언 단계 > 초기화 단계 > 할당 단계
에 걸쳐 생성되는데 var
으로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 따라서 변수 선언문 이전에 변수를 참조하여 undefined
를 반환한다.
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
let
로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.
console.log(foo); // ReferenceError: foo is not defined
let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
var, let, const 차이점 Javascript Hoisting
자세한 내용은 여기에!
이벤트 위임(Event Delegation)은 다수의 자식 요소에 각각 이벤트 핸들러를 바인딩하는 대신 하나의 부모 요소에 이벤트 핸들러를 바인딩하는 방법이다.
DOM 트리에 새로운 요소(예: li)를 추가하더라도 이벤트 처리는 부모 요소(예: ul)에 위임 되기 때문에 새로운 요소에 이벤트를 핸들러를 다시 바인딩할 필요가 없다.
이는 이벤트가 이벤트 흐름에 의해 이벤트를 발생시킨 요소의 부모 요소에도 영향(버블링)을 미치기 때문에 가능한 것이다.
계층적 구조에 포함되어 있는 HTML 요소에 이벤트가 발생할 경우 연쇄적 반응이 일어난다. 즉, 이벤트가 전파(Event Propagation)되는데 전파 방향에 따라 버블링(Event Bubbling)과 캡처링(Event Capturing)으로 구분할 수 있다.
자식 요소에서 발생한 이벤트가 부모 요소로 전파되는 것을 버블링이라 한다.
자바스크립트 이벤트
이벤트의 자세한 내용은 여기에...
16번 문제에서 변수는 선언 단계 > 초기화 단계 > 할당 단계
를 걸쳐 생성 된다고 언급했다.이 세가지의 차이점은 여기서 발생된다.
undeclared: 선언 단계가 이루어지지 않은 상태다.
use strict
명령어를 통해 엄격모드로 진입해야 만날 수 있다. 엄격모드가 아닌 자바스크립트에서 선언되지 않은 변수에 값을 할당하면 자동으로 global 변수로 취급되기 때문이다.
undefined: 선언은 되었으나 초기화 단계가 이루어지지 않은 상태다.
let foo;
console.log(foo); //undefined
null: 선언 되었고 초기화 하였으나, 빈 값을 원할 떄 null
을 할당하여 사용한다.
여기서 주의해야 할 점은 ==
연산자를 사용하여 null
과 undefined
를 비교하면 자동형변환이 이루어지고, 둘 다 빈 값이기 때문에 true
를 반환한다. 따라서 값과 자료형 둘다 비교하는 ===
연산자를 써야 한다.
let foo; // undefined
let boo = null; // null
console.log(foo == boo); // true
console.log(foo === boo); // false
function Person() {}
var person = Person()
var person = new Person()
함수 선언문
이 경우 함수는 호이스팅 되어 해당 스코프의 최상단에 선언한 것 과 동일하게 작동한다.
함수 표현식
이 경우 함수 선언문과 달리 호이스팅 되지 않는다.
new
생성자로 함수 호출
함수를 호출할 때 new
를 붙이면 새로운 객체를 만든 후에 이를 리턴한다.
화살표 함수는 익명 함수로만 사용할 수 있다. 따라서 화살표 함수를 호출하기 위해서는 함수 표현식을 사용한다. 또는 콜백 함수로 사용할 수 있다. 이 경우 일반적인 함수 표현식보다 표현이 간결하다.
화살표 함수를 사용해선 안되는 경우
프론트엔드 인터뷰 핸드북 여기에 프론트엔드에 대한 좋은 인터뷰 문제가 있다. 꼭 읽어 보도록 하자!
8번에서 전역객체와 window(전역)을 다르게 표기한 이유가 있나요?