Skip to content

Commit

Permalink
[4주차: JavaScript 기초 함수] 20장 strict mode (#16)
Browse files Browse the repository at this point in the history
* [2주차:  타입과 실행 컨텍스트] 9. 타입 변환과 단축 평가 (#6)

* 파일 생성

* Update README.md

* 오타 수정

Co-authored-by: sjsjsj1246 <[email protected]>

* 6장 정리 (#9)

* 8장 정리

* 9장 오타수정

* [3주차: JavaScript 기초 함수] 8장 제어문, 10장 객체 리터럴 #11

* [4주차: JavaScript 기초 함수] 20장 strict mode

* 4주차: 빌트인 ror체 strict mode 프로토타입 this

Co-authored-by: sjsjsj1246 <[email protected]>
Co-authored-by: Jeongjin Oh <[email protected]>
  • Loading branch information
3 people authored Dec 2, 2022
1 parent 58972e4 commit daa242e
Show file tree
Hide file tree
Showing 5 changed files with 391 additions and 469 deletions.
148 changes: 148 additions & 0 deletions 20장-strict mode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#20

## 20.1 strict mode란?

- 오타나 문법 지식의 미비로 인한 실수는 언제나 발생한다. 따라서, 오류를 줄여 안정적인 코드를 생산하기 위해서는 좀 더 `근본적인 접근`이 필요하다.
- 다시 말해, 잠재적인 오류를 발생시키기 어려운 개발 환경을 만들고 그 환경에서 개발하는 것이 좀 더 근본적인 해결책이라고 할 수 있다.
- 이를 지원하기 위해 ES5부터 `strict mode`가 추가된 것이다.
- strict mode는 자바스크립트 언어의 문법을 좀 더 엄격히 적용하여 오류를 발생시킬 가능성이 높거나 자바스크립트 엔진의 최적화 작업에 문제를 일으킬 수 있는 코드에 대해 `명시적인 에러를 발생시킨다.`
- ESLint 같은 린트 도구를 사용해도 strict mode와 유사한 효과를 얻을 수 있다.

## 20.2 strict mode의 적용

strict mode를 적용하려면 전역의 선두 또는 함수 몸체의 선두에 ‘use strict’;를 추가한다.

- 전역의 선두에 추가하면 스크립트 전체에 strict mode가 적용된다.
- 함수 몸체 선두에 추가하면 해당 함수와 중첩함수에 strict mode가 적용된다.
- 코드 선두에 ‘use strict’;를 추가하지 않으면 제대로 동작하지 않는다.

## 20.3 전역에 strict mode를 적용하는 것은 피하자

- 전역에 적용한 strict mode는 스크립트 단위로 적용된다.
```jsx
<script>
'use strict';
</script>
<script>
x = 1; //에러가 발생하지 않는다.
</script>
<script>
'use strict';
y = 1; //Refernce Error
</script>

```
- strict mode 스크립트와 non-script mode 스크립트를 혼용하는 것은 오류를 발생시킬 수 있다. 특히 외부 서드파티 라이브러리를 사용하는 경우 라이브러리가 non-strict mode인 경우도 있기 때문에 전역에 strict mode를 적용하는 것은 바람직하지 않다.
- 이러한 경우 즉시 실행 함수로 스크립트 전체를 감싸서 스코프를 구분하고 즉시 실행 함수(선언하자마자 바로 호출되는 함수)의 선두에 strict mode를 적용한다.
```jsx
//즉시 실행 함수의 선두에 strict mode 적용
(function() {
'use strict';
...
}());
```

## 20.4 함수 단위로 strict mode를 적용하는 것도 피하자

- 함수 단위로도 strict mode를 적용할 수 있다.
- 하지만, 함수마다 strict mode를 적용하는 여부를 다르게 하는 것은 바람직하지 않으며 모든 함수에 일일이 적용하는 것은 번거로운 일이다.
- 따라서 strict mode는 즉시 실행 함수로 감싼 스크립트 단위로 적용하는 것이 바람직하다.

## 20.5 strict mode가 발생시키는 에러

### 20.5.1 암묵적 전역

선언하지 않은 변수를 참조하면 `ReferenceError`가 발생한다

```jsx
(function () {
"use strict";
x = 1;
console.log(x); //ReferenceError: x is not defined
})();
```

### 20.5.2 변수, 함수, 매개변수의 삭제

delete 연산자로 변수, 함수, 매개변수를 삭제하면 `SyntaxError`가 발생한다.

```jsx
(function () {
"use strict";

var x = 1;
delete x; //SyntaxError: Delete of an unqualified indentifier in strict mode.

function foo(a) {
delete a; //SyntaxError: Delete of an unqualified indentifier in strict mode.
}
delete foo; //SyntaxError: Delete of an unqualified indentifier in strict mode.
})();
```

### 20.5.3 매개변수 이름의 중복

- 중복된 매개변수 이름을 사용하면 `SyntaxError`가 발생한다.

```jsx
(function () {
"use strict";
//SyntaxError: Duplicate parameter name not allowed in this context
function foo(x, x) {
return x + x;
}
console.log(foo(1, 2));
})();
```

### 20.5.4 with 문의 사용

- with 문을 사용하면 `SyntaxError`가 발생한다. with 문은 전달된 객체를 스코프 체인에 추가한다.
- with문은 동일한 객체의 프로퍼티를 반복해서 사용할 때 객체 이름을 생략할 수 있어서 코드가 간단해지는 효과가 있지만 성능과 가독성이 나빠지는 문제가 있다.

```jsx
(function () {
"use strict";

//SyntaxError: Strict mode code may not include a with statement
with ({ x: 1 }) {
console.log(x);
}
})();
```

## 20.6 strict mode 적용에 의한 변화

### 20.6.1 일반 함수의 this

- strict mode에서 함수를 일반 함수로서 호출하면 this에 undefined가 바인딩된다.
- 생성자 함수가 아닌 일반 함수 내부에서는 this를 사용할 필요가 없기 때문이다.

```jsx
(function () {
'use strict';

function foo() {
console.log(this); //undefined
}
foo();

function Foo() {
console.log(this); //Foo
}
new Foo();
}
```
### 20.6.2 arguments 객체
- arguments 객체는 함수를 호출할 때 넘긴 인자들이 배열 형태로 저장된 객체를 말한다.
- strict mode에서는 매개변수에 전달된 인수를 재할당하여 변경해도 arguments 객체에 반영되지 않는다.
```jsx
(function (a) {
"use strict";
a = 2;
console.log(arguments);
})(1);
```
243 changes: 243 additions & 0 deletions 22장-this/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# 22장 this
```jsx
const circle ={
radius : 5,
getDiameter() {
return 2 * circle.radius;
}
};

console.log(circle.getDiameter()); // 10
```

- 위 예제의 경우 객체 리터럴의 평가가 완료되어 객체가 생성되었고, circle 식별자에 생성된 객체가 할당된 이후이기 때문에 메서드 내에서 circle 식별자를 참조할 수 있다.
- 하지만 재귀적으로 객체를 참조하는 방식은 일반적이지도 않고 바람직하지 않다.

<aside>
💡 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 특수한 식별자가 필요하고, `this`가 바로 그 식별자이다.

</aside>

## 22.1 this 키워드

- 자바스크립트의 일반적인 함수들은 this라는 변수를 가지고 있다.
- this는 해당 함수를 메서드로 가지고 있는 객체 또는 해당 함수가 생성할 객체를 참조하는 변수이다.
- this는 암묵적으로 생성되고, 코드 어디서든 참조 가능하다.
- this는 함수 내에서 쓰일 때 의미가 있지만 전역에서도 참조할 수는 있다.
- this가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.
- this를 전역에서 참조할 경우 다음과 같이 동작한다.
- 브라우저에서 동작할 경우 this는 전역 객체인 window를 참조한다.
- node REPL에서 this는 전역 객체인 global을 참조한다.
- commonJS의 모듈 시스템을 따르는 node 환경에서는 module.exports를 의미한다.
- 브라우저 환경일 때, strict mode라면 undefined가 할당된다.


## 22.2 함수 호출 방식과 this 바인딩

this 바인딩은 함수 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.

주의할 것은 동일한 함수도 다양한 방식으로 호출할 수 있다는 것이다.

### 22.2.1 일반 함수 호출

- 기본적으로 this에는 전역객체가 바인딩 된다.
- 객체를 생성하지 않는 일반함수에서의 this는 의미가 없다.
- strict mode가 적용된 일반함수 내부의 this에는 undefined가 바인딩된다.

```jsx
function foo() {
console.log("foo's this: ", this); //window
function bar() {
console.log("bar's this: ", this); //window
}
bar();
}

foo();
```

- 콜백 함수가 일반 함수로 호출된다면 콜백 함수 내부의 this에도 전역객체가 바인딩된다.
- 하지만 메서드 내에서 정의한 중첩함수 또는 메서드에게 전달한 콜백함수가 일반함수로 호출될 때 그 함수들의 this가 전역 객체를 바인딩하는 것은 문제가 있다.
- 외부 함수인 메서드와 중첩함수 도는 콜백 함수의 this가 일치하지 않는다는 것은 중첩, 콜백 함수를 헬퍼함수로서 동작하기 어렵게 한다.

**해결책**

1. this 바인딩을 다른 변수에 할당

```jsx
var value = 1;

const obj = {
value: 100,
foo() {
const that = this;
setTimeout(function () {
console.log(that.value);
}, 100);
},
};

obj.foo();
```

1. 콜백함수에 명시적으로 this를 바인딩

```jsx
var value = 1;

const obj = {
value: 100,
foo() {
setTimeout(function () {
console.log(this.value);
}.bind(this), 100);
},
};

obj.foo();
```

1. 화살표 함수 이용

화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.

```jsx
var value = 1;

const obj = {
value: 100,
foo() {
setTimeout(()=>console.log(this.value), 100);
}
};

obj.foo();
```

### 22.2.2 메서드 호출

```jsx
const person = {
name: 'Lee',
getName() {
return this.name;
}
};

console.log(person.getName()); //Lee
```

위 예제에 getName 메서드는 person 객체의 메서드로 정의되었다.

- 메서드는 프로퍼티에 바인딩된 함수이기에 getName프로퍼티가 가리키는 함수 객체는 person에 포함된 것이 아닌, 독립적으로 존재하는 별도의 객체이다.
- 따라서 메서드 내부의 this는 프로퍼티로 메서드를 가리키는 객체와 상관 없고, 메서드를 호출한 객체에 바인딩 된다.

![images](images/22_1.png)

- 프로토타입 메서드 내부에서 사용된 this도 일반 메서드와 마찬가지로 해당 메서드를 호출한 객체에 바인딩 된다.

### 22.2.3 생성자 함수 호출

- new 연산자와 함께 호출되는 경우 함수는 생성자로써 호출된다.
- 생성자 함수 내부에서 this는 생성할 객체를 의미한다.
- 생성자 함수의 동작을 간단히 나타내면 다음과 같다.


```jsx
function Circle(radius) {

this.radius = radius;
this.getDiameter = function() {
return 2*this.radius;
};
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); //10
console.log(circle2.getDiameter()); //20

//new 연산자 없이 호출되면 일반적인 함수 호출이다
const circle3 = Circle(15);
console.log(circle3); //undefined

//일반 함수로 호출된 Circle 내부의 this는 전역객체를 가리킨다.
console.log(radius); //15
```


### 22.2.4 Function.prototype.apply/call/bind 메서드에 의한 간접 호출

apply, call,bind 메서드는 Function.prototype의 메서드이다. 즉 모든 함수가 이를 상속받아 사용할 수 있다.

1. **apply, call**
- **apply****call** 메서드는 호출할 함수에 인수를 전달하는 방식만 다를 뿐 동일하게 동작한다.
- **apply** 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.
- **call** 메서드는 호출할 함수의 인수를 쉼표로 구분하는 리스트 형식으로 전달한다.
- 대표적인 용도는 arguments 객체와 같은 유사 배열 객체에 배열 메서드를 사용하는 경우다.
- arguments 객체는 배열이 아니기때문에 slice 메서드같은 배열의 메서드를 사용할 수 없다.

```jsx
function getThisBinding() {
console.log(arguments);
return this;
}
const thisArgs = { a: 1 };
//apply 메서드는 배열 형식
console.log(getThisBinding.apply(thisArgs, [1, 2, 3]));
//call 메서드는 리스트 형식
console.log(getThisBinding.call(thisArgs, 1, 2, 3));
```

2. **bind**
- bind메서드는 apply, call 메서드와 달리 함수를 호출하지 않는다. 다만 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.

```
function getThisBinding() {
return this;
}

const thisArgs = { a: 1 };

console.log(getThisBinding.bind(thisArgs)); //getThisBinding

//bind 메서드는 함수를 호출하지 않으므로 명시적으로 호출해야한다.
console.log(getThisBinding.bind(thisArgs)()); //{ a: 1 }
```

- bind 메서드는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치하는 문제를 해결하기위해 사용된다.

```jsx
const person = {
name : 'Lee',
foo(callback){
setTimeout(callback, 100);
}
};
person.foo(function(){
console.log(`Hi my name is ${this.name}.`); //Hi my name is .
//브라우저 환경 상에서 window.name은 브라우저 창의 이름을 나타내는
//빌트인 프로퍼티이며 기본 값은 ''이다.
})
```

```jsx
const person = {
name : 'Lee',
foo(callback){
//bind 메서드로 콜백함수 내부의 this 바인딩을 전달.
setTimeout(callback.bind(this), 100);
}
};
person.foo(function(){
console.log(`Hi my name is ${this.name}.`); //Hi my name is Lee.
})
```

![images](images/22_2.png)
Binary file added 22장-this/images/22_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 22장-this/images/22_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit daa242e

Please sign in to comment.