관련 Post
예전에 JS 관련해서 포스팅한 내용과 세션 내용을 기반으로 간단하게 정리하고자 합니다. 세부적인 사항이 궁금하신 분들은 Post를 참조해 주세요.
실행할 코드 : 전역 / 함수 / (eval)
하나의 자바스크립트 파일을 실행할 때, 필요한 환경 정보를 모아 실행 컨텍스트를 구성합니다. 그리고, call stack에 쌓아 올려지게 되고, 가장 위의 컨텍스트와 관련있는 코드를 실행하는 식으로 전체 코드의 환경과 순서를 보장합니다. call stack 이란 현재 실행 중인 작업 관한 정보를 저장하는 Stack 자료구조(LIFO,후입 선출)라 이해하면 좋습니다.
변수, 매개변수, 인자, 함수 선언 등의 정보
Environment record와 Outer로 구성되어 있음
function book(){ var point = 123; function show(){ var title = "JS책"; }; function getPoint() { return point; }; show(); }; book();
show 실행 콘텍스트(EC): { 렉시컬 환경 컴포넌트(LEC): { 환경 레코드(ER): { 선언적 환경 레코드(DER): { title: "JS책" }, 오브젝트 환경 레코드(OER): {} }, 외부 렉시컬 환경 참조(OLER): { point: 123, getPoint: function(){} } }, 변수 환경 컴포넌트(VEC): {}, this 바인딩 컴포넌트(TBC): { 글로벌 오브젝트(window) } }
즉, 실행 콘텍스트 안에 함수에서 구할 수 있는 값의 덩어리(정적 환경)를 만들어두면서, 함수가 메모리에 올라가 실행 될 때 메모리를 빠져나가거나 이동하거나 하지않고(scope 고정) 안에서 처리할 수 있게 됩니다. 따라서 환경에 맞춰서 코딩을 하게 되었을 경우 효율성이 올라갑니다.
자바스크립트는 함수 선언문 해석 → 변수 초기화 → 코드 실행 순으로 함수 코드를 해석합니다. 따라서 선언부는 끌어올리고, 할당은 코드가 실행되는 시점에 진행됩니다.
function a() {
// 로직
}
→ function 정의만 존재하고 별도의 할당 명령이 없다.
표현식(expression)이란 자바스크립트 인터프리터가 계산하여 값을 구할 수 있는 자바스크립트 구절을 말한다. 이러한 값을 표현하는 것을 리터럴이라고 한다.
var a = function() {
// 로직
}
var b = function success() {
// 로직
}
→ function 키워드로 정의한 함수를 변수에 할당하는 것을 말한다.
function book(){
var getBook = function(){
return "책1";
};
console.log(getBook());
function getBook(){
return "책2";
};
};
book();
catName("Chloe");
function catName(name) {
console.log("My cat's name is " + name);
}
var의 경우 변수의 선언이 끌어올려 집니다. ES6에서 등장한 let과 const의 경우와는 차이가 있습니다. let, const 역시 마찬가지로 LexicalEnvironment에 변수 정보를 미리 수집합니다(hoisting의 개념으로 알고 있는 동작). 하지만, let, const는 실행되기 전까지 액세스 할 수 없고, 사이 공간을 TDZ(Temporal Dead Zone)라고 함
// var를 사용한 호이스팅의 예시, 선언에 대하여 할당은 되지 않고 읽혀진다.
var x = 1;
console.log(x + " " + y); // '1 undefined'
var y = 2;
// let을 사용한 호이스팅의 예시, 선언에 대하여 실행 콘텍스트에 수집은 되지만, 실행 이전까지 TDZ으로 에러가 발생한다.
x = 3; // Uncaught ReferenceError: Cannot access 'x' before initialization
let x = 1;
두 가지 모두 LexicalEnvironment에 변수 정보를 미리 수집은 되나 동작에서 차이가 있다.
등록
하고 검색
한다는 두 가지 목적var x = 'global';
function foo () {
var x = 'function scope';
console.log(x); // function scope
}
foo();
console.log(x); // global
window
, Node.js에서는 global
var value = 100;
function book() {
return value;
};
book()
JavaScipt는 다른 언어와 달리 scope의 범위가 함수 블록 내이다. ({} 블록과 상관이 없다)
= 함수에 의해서만 scope가 생성된다(function 오브젝트를 만날 때 스코프가 결정)
if (true) {
var x = 5;
}
console.log(x); // 5
→ if 문 안에 있더라도, 일반 블록이므로 x는 전역변수가 된다.
(function () {
var x = 5;
})();
console.log(x); // Uncaught ReferenceError: x is not defined
→ 오로지 함수만 scope가 생성!
function 오브젝트를 만날 때 스코프가 결정
⚠️ JavaScipt에서 let, const로 변수를 선언할 때의 scope은 블록({}) 단위이다.
{
let x = 1;
}
console.log(x); //Uncaught ReferenceError: x is not defined
→ var
vs let
vs const
프로퍼티 이름을 구조적으로 결속된 상태로 만드는 것 입니다. 스코프를 설정하여 식별자 해결하는 것을 목적으로 하며, 바인딩 시점에 따라 정적 바인딩과 동적바인딩으로 구분합니다.
function book() {
var point = 100;
function add(param) {
point += param;
};
var get = function() {
return point;
};
add(200);
console.log(get());
};
book();
// 300
<식별자 해결>
- 마지막 줄에서 book() 함수 호출
- 초기화 단계에서 함수와 변수 이름을 선언적 환경 레코드에 바인딩
- function add(param) {...}
- function 오브젝트 생성
- add 함수가 속한 스코프를 add 오브젝트의
[[Scope]]
에 설정- add 이름을 레코드에 바인딩
- var point = 100;
- point 이름을 레코드에 바인딩
- var get = function() {...}
- get 이름을 레코드에 바인딩
- 바인딩으로 함수와 변수의 식별자 해결 완료
<코드실행>
- var point = 100;
- point 변수에 100 할당
- var get = function() {...}
- function 오브젝트 생성, get에 할당
- get 함수가 속한 스코프를 get 오브젝트의
[[Scope]]
에 설정<add() 함수 호출>
- add(200) 함수를 호출
- point += param;
- 먼저 선언적 환경 레코드에서 point 이름 검색
- point가 없기에 add 오브젝트의 [[Scope]]를 스코프로 사용하여 다시 검색
- book 오브젝트가 스코프이며 point가 존재하므로 값을 산술
<get() 함수 호출>
- get() 함수를 호출
- return point;
- 선언적 환경 레코드에서 point 이름 검색
- point가 없기에 get 오브젝트의 [[Scope]]를 스코프로 사용하여 다시 검색
- book 오브젝트가 스코프이며 point가 존재하므로 값을 반환
실행 콘텍스트: { 렉시컬 환경 컴포넌트: { 환경 레코드: { 선언적 환경 레코드: {}, 오브젝트 환경 레코드: {} }, 외부 렉시컬 환경 참조: {} } }
→ 외부 렉시컬 환경 참조에 선언된 변수, 함수를 내 것처럼 사용하는 것
var A = function() {
var a = 1;
var B = function() {
return ++a;
};
return B;
};
var outer = A();
console.log(outer()); // 2
console.log(outer()); // 3
**var outer = A()
** 에서 함수 B자체를 반환A()
실행했으므로 A의 실행컨텍스트는 종료됨outer
변수는 이제 B함수
를 바라보고 있음outer()
outer를 호출하면, 즉! B를 호출. a값은 계속 증가함:: 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우, A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상
function 키워드
로 함수를 정의한 것을 의미합니다.객체
의 프로퍼티
로 함수가 정의되어야 합니다.let user = {
name: 'kim',
underTwenty: function(age) {
return age < 20;
}
}
user.underTwenty(30); // 메서드
const under20 = user.underTwenty;
under20(15); // 객체 안에 정의된 함수라도, 이것은 메서드가 아닌 함수
Host 오브젝트 개념
을 적용하기 때문입니다. (DOM 오브젝트도 Host 오브젝트입니다)var a = 1;
console.log(a);
console.log(window.a);
console.log(this.a);
var name = 'lee';
var user = {
name: 'kim',
getName: function() {
console.log(this.name);
},
age: 50,
child: {
age: 15,
underTwenty: function() {
console.log(this.age);
return this.age < 20
}
}
}
user.getName(); // kim | getName 메서드는 user 객체를 바라봄
user.child.underTwenty(); // 15 | underTwenty 메서드는 child 객체를 바라봄
user.parentUnderTwenty = user.child.underTwenty;
user.parentUnderTwenty(); // 50 | parentUnderTwenty 메서드는 user 객체를 바라봄
인스턴스의 목적
인스턴스마다 고유 값을 유지
인스턴스에서 this의 목적
this로 인스턴스를 참조하여 this.name형태로 프로퍼티에 접근할 수 있습니다.
__proto__ 프로퍼티 접근
⇒ 일관된 환경에서 값만 다르게 가져가는 데이터 중심의 처리
var book = {}; book.Point = function(point) { this.point = point; }; book.Point.prototype.getPoint = function() { console.log(this.point); }; var obj = new book.Point(100); obj.getPoint(); // 100
함수를 호출할 때, 원하는 대상의 객체를 인자로 넘겨준다.
var user = {
name: 'kim',
getName: function() {
console.log(this.name);
},
age: 50,
child: {
age: 15,
underTwenty: function() {
console.log(this.age);
return this.age < 20
}
}
}
user.child.underTwenty.call(user);
call과 비슷하지만, 바로 호출하는 것이 아니라 대상을 묶어놓기(binding)만 하는 것
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button **onClick={this.handleClick}**>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
event listener는 내부적으로 this를 이벤트가 일어난 엘리먼트로 대상을 잡아놓기 때문에
handleClick 내부의 this가 Class를 가르키는 것이 아닌, 엘리먼트를 가르치게 됨
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
}
handleClick = () => {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}