(function () {
'use strict';
x = 1;
console.log(x); // ReferenceError: x is not defined
}());
(function () {
'use strict';
var x = 1;
delete x;
// SyntaxError: Delete of an unqualified identifier in strict mode.
function foo(a) {
delete a;
// SyntaxError: Delete of an unqualified identifier in strict mode.
}
delete foo;
// SyntaxError: Delete of an unqualified identifier in strict mode.
}());
(function () {
'use strict';
//SyntaxError: Duplicate parameter name not allowed in this context
function foo(x, x) {
return x + x;
}
console.log(foo(1, 2));
}());
with 문의 사용
(function () {
'use strict';
// SyntaxError: Strict mode code may not include a with statement
with({ x: 1 }) {
console.log(x);
}
}());
with는 뭐하는 함수일까?
with (document.getElementById("myDiv").style) {
background = "yellow";
color = "red";
border = "1px solid black";
}
// 또는
var r = 10, a, x, y;
with (Math) {
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
with를 사용하면 왜 안될까?
function doSomething(value, obj) {
with (obj) {
value = "which scope is this?";
console.log(value);
}
}
일반 함수의 this
(function () {
'use strict';
function foo() {
console.log(this); // undefined
}
foo();
function Foo() {
console.log(this); // Foo
}
new Foo();
}());
``~
function square(number) {
console.log(arguments);
console.log(this); //크롬브라우저에서 실행 시 window
return number * number;
}
square(2);
자바스크립트의 경우 함수 호출 방식에 의해 this에 바인딩할 어떤 객체가 동적으로 결정된다.
함수 호출
전역객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미하며 일반적으로 Browser-side에서는 window, Server-side(Node.js)에서는 global 객체를 의미한다.
// in browser console
this === window // true
// in Terminal
node
this === global // true
전역객체는 전역 스코프(Global Scope)를 갖는 전역변수(Global variable)를 프로퍼티로 소유한다. 글로벌 영역에 선언한 함수는 전역객체의 프로퍼티로 접근할 수 있는 전역 변수의 메소드이다.
기본적으로 this는 전역객체(Global object)에 바인딩된다. 전역함수는 물론이고 심지어 내부함수의 경우도 this는 외부함수가 아닌 전역객체에 바인딩된다.
function foo() {
console.log("foo's this: ", this); // window
function bar() {
console.log("bar's this: ", this); // window
}
bar();
}
foo();
var value = 1;
var obj = {
value: 100,
foo: function() {
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar() {
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
}
bar();
}
};
obj.foo();
var value = 1;
var obj = {
value: 100,
foo: function() {
setTimeout(function() {
console.log("callback's this: ", this); // window
console.log("callback's this.value: ", this.value); // 1
}, 100);
}
};
obj.foo();
var value = 1;
var obj = {
value: 100,
foo: function() {
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar(a, b) {
console.log("bar's this: ", this); // obj
console.log("bar's this.value: ", this.value); // 100
console.log("bar's arguments: ", arguments);
}
bar.apply(obj, [1, 2]);
bar.call(obj, 1, 2);
bar.bind(obj)(1, 2);
}
};
obj.foo();
메소드 호출
var obj1 = {
name: 'Lee',
sayName: function() {
console.log(this.name);
}
}
var obj2 = {
name: 'Kim'
}
obj2.sayName = obj1.sayName;
obj1.sayName();
obj2.sayName();
생성자 함수 호출
function Person(name) {
// 생성자 함수 코드 실행 전 -------- 1
this.name = name; // --------- 2
// 생성된 함수 반환 -------------- 3
}
var me = new Person('Lee');
console.log(me.name);
생성자 함수에 new 연산자를 붙이지 않고 호출 하는 것을 방지하기 위한 패턴
Scope-Save Constructor
callee는 arguments 객체의 프로퍼티로서 함수 바디 내에서 현재 실행 중인 함수를 참조할 때 사용한다. 다시 말해, 함수 바디 내에서 현재 실행 중인 함수의 이름을 반환한다.
// Scope-Safe Constructor Pattern
function A(arg) {
// 생성자 함수가 new 연산자와 함께 호출되면 함수의 선두에서 빈객체를 생성하고 this에 바인딩한다.
/*
this가 호출된 함수(arguments.callee, 본 예제의 경우 A)의 인스턴스가 아니면 new 연산자를 사용하지 않은 것이므로 이 경우 new와 함께 생성자 함수를 호출하여 인스턴스를 반환한다.
arguments.callee는 호출된 함수의 이름을 나타낸다. 이 예제의 경우 A로 표기하여도 문제없이 동작하지만 특정함수의 이름과 의존성을 없애기 위해서 arguments.callee를 사용하는 것이 좋다.
*/
if (!(this instanceof arguments.callee)) {
return new arguments.callee(arg);
}
// 프로퍼티 생성과 값의 할당
this.value = arg ? arg : 0;
}
var a = new A(100);
var b = A(10);
console.log(a.value);
console.log(b.value);
apply/call/bind 호출
this를 특정 객체에 명시적으로 바인딩하는 방법도 제공된다.
Function.prototype.apply
func.apply(thisArg, [argsArray])
// thisArg: 함수 내부의 this에 바인딩할 객체
// argsArray: 함수에 전달할 argument의 배열
apply() 메소드를 호출하는 주체는 함수이며 apply()메소드의 본질적인 기능은 함수 호출이 다.
apply() 메소드의 대표적인 용도는 arguments 객체와 같은 유사 배열 객체에 배열 메소드를 사용하는 경우이다.
function convertArgsToArray() {
console.log(arguments);
// arguments 객체를 배열로 변환
// slice: 배열의 특정 부분에 대한 복사본을 생성한다.
var arr = Array.prototype.slice.apply(arguments); // arguments.slice
// var arr = [].slice.apply(arguments);
console.log(arr);
return arr;
}
convertArgsToArray(1, 2, 3);
Function.prototype.call
call() 메소드의 경우, apply()와 기능은 같지만 apply()의 두번째 인자에서 배열 형태로 넘긴 것을 각각 하나의 인자로 넘긴다.
```
Person.apply(foo, [1, 2, 3]);
Person.call(foo, 1, 2, 3);
apply()와 call() 메소드는 콜백 함수의 this를 위해서 사용되기도 한다.
Function.prototype.bind
ES5에 추가되었다.
함수에 인자로 전달한 this가 바인딩된 새로운 함수를 리턴한다.
function Person(name) {
this.name = name;
}
Person.prototype.doSomething = function (callback) {
if (typeof callback == 'function') {
// callback.call(this);
// this가 바인딩된 새로운 함수를 호출
callback.bind(this)();
}
};
function foo() {
console.log('#', this.name);
}
var p = new Person('Lee');
p.doSomething(foo); // 'Lee'
실행 가능한 코드가 실행되기 위해 필요한 환경
실행 가능한 코드
실행에 필요한 여러가지 정보란 아래와 같은 것들이 있다.
실행에 필요한 정보를 형상화하고 구분하기 위해 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체 의 형태로 관리한다.
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이지만 물리적으로는 객체의 형태를 가지며 3가지 프로퍼티를 소유한다.
전역 코드에의 진입
전역 객체(Global Object)가 생성된다.
스코프 체인의 생성과 초기화
Variable Instantiation(변수 객체화) 실행
함수의 선언 처리
변수의 선언 처리
this value 결정
this value가 결정되기 이전에 this는 전역 객체를 가리키고 있다가 함수 호출 패턴에 의해 this에 할당되는 값이 결정된다. 전역 코드의 경우, this는 전역 객체를 가리킨다.
전역 코드의 실행
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
위 예제를 보며 전역 코드의 실행을 살펴보자
변수 값의 할당
전역 변수 x에 문자열 ‘xxx’를 할당할 때, 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 Variable Object를 선두(0)부터 검색하여 변수명에 해당하는 프로퍼티가 발견되면 값(‘xxx’)을 할당한다.
함수 foo의 실행
스코프 체인의 생성과 초기화
Activation Object에 대한 레퍼런스를 스코프 체인의 선두에 설정
Activation Object는 우선 arguments 프로퍼티의 초기화를 실행하고 그 후, Variable Instantiation가 실행된다.
Activation Object는 스펙 상의 개념으로 프로그램이 Activation Object에 직접 접근할 수 없다. (Activation Object의 프로퍼티로의 접근은 가능하다)
그 후, Caller(전역 컨텍스트)의 Scope Chain이 참조하고 있는 객체가 스코프 체인에 push된다. 이 경우 함수 foo를 실행한 직후 실행 컨텍스트의 스코프 체인은 Activation Object(함수 foo의 실행으로 만들어진 AO-1)과 전역 객체를 순차적으로 참조하게 된다.
Variable Instantiation 실행
Function Code의 경우, 스코프 체인의 생성과 초기화에서 생성된 Activation Object를 Variable Object로서 Variable Instantiation가 실행된다. 이것을 제외하면 전역 코드의 경우와 같은 처리가 실행된다.
this value 결정
변수 선언 처리가 끝나면 다음은 this value가 결정된다. this에 할당되는 값은 함수 호출 패턴에 의해 결정된다.
foo 함수 코드의 실행
이제 함수 foo의 코드 블록 내 구문이 실행된다. 위 예제를 보면 변수 y에 문자열 ‘yyy’의 할당과 함수 bar가 실행된다.
변수 값의 할당
지역 변수 y에 문자열 ‘yyy’를 할당할 때, 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 Variable Object를 선두(0)부터 검색하여 변수명에 해당하는 프로퍼티가 발견되면 값 ‘yyy’를 할당한다.
함수 bar의 실행
함수 bar가 실행되기 시작하면 새로운 실행 컨텍스트이 생성된다. 이전 함수 foo의 실행 과정과 동일하게 1. 스코프 체인의 생성과 초기화, 2. Variable Instantiation 실행, 3. this value 결정이 순차적으로 실행된다.