20장 strict mode
20.1 strict mode란?
암묵적 전역
function foo() {
x = 10;
}
foo();
console.log(x); // 참조에러가 일어나지 않고 10이 출력된다.
선언하지 않은 변수 x에 10이라는 값을 할당하면?
스코프 체인에서 변수 x를 검색한다. 그러나 스코프 체인의 최상위인 전역 스코프에서도 x를 찾을 수 없으므로 값을 할당하지 못하고 ReferenceError가 발생할 것이다. 그런데 자바스크립트 엔진이 x를 전역 객체에 프로퍼티로 동적 생성한다. 이 현상이 '암묵적으로 전역 변수를 만든다'고 해서 암묵적 전역이다.
암묵적 전역은 오류를 일으킬 수 있으므로 반드시 키워드로 변수를 선언한 뒤 할당해야 한다.
strict mode(엄격 모드)
오타나 문법 실수로 인해 발생할 수 있는 이러한 오류들을 근본적으로 방지하기 위해, 오류를 발생시킬 수 있는 코드에 명시적인 에러를 발생시키는 기능을 추가한 것. 따라서 strict mode는 ESLint와 비슷한 기능인데, strict mode보다 더욱 강력한 린트 도구를 사용하는 것을 추천.
ES6에서 도입된 클래스와 모듈은 기본적으로 strict mode가 적용된다.
20.2 strict mode의 적용
전역이나 함수 몸체의 선두에 "use strict"; 추가하면 된다. 그럼 해당 영역 안에서 적용됨. 반드시 선두에 쓸 것.
20.3 전역에 strict mode를 적용하는 것은 피하자
전역에 strict mode를 적용하면 스크립트 단위로 적용된다. 그런데 strict mode와 non-strict mode 스크립트를 혼용할 경우 오류를 발생시킬 수 있으므로 바람직하지 않다.
ex) 사용한 외부 라이브러리가 non-strict mode일 때
=> 즉시 실행 함수로 스크립트를 감싸 스코프를 구분하여 그 안에서 strict mode 적용.
20.4 함수 단위로 strict mode를 적용하는 것도 피하자
함수 단위로 strict mode를 적용하는 것도 함수별로 strict/non-strict 모드가 혼용될 수 있어서 바람직하지 않다. strict mode의 함수가 참조하는 함수 외부의 컨텍스트는 strict mode가 아닐 때 문제가 생길 수도 있다.
=> strict mode는 항상 즉시 실행 함수로 감싼 스크립트 단위로 적용!
20.5 strict mode가 발생시키는 에러
그냥 지나칠 수 있지만 strict mode일 때는 에러가 발생하는 경우! 즉, strict mode가 필요한 경우.
- 암묵적 전역(선언하지 않은 변수 참조) → ReferenceError
- 변수, 함수, 매개변수 삭제(delete 연산자로 삭제) → SyntaxError
- 매개변수 이름 중복 → SyntaxError
- with문 사용 → SyntaxError
20.6 strict mode 적용에 의한 변화
- 일반 함수의 this : 생성자 함수가 아닌 일반 함수로서 호출 시, this가 필요 없으므로, undefined가 바인딩됨.
- arguments 객체 : 매개변수에 전달된 인수를 재할당하여 변경해도 arguments 객체에 반영되지 않음.
(function (a) {
'use strict';
a = 2;
console.log(arguments); // { 0: 1, length: 1 }
}(1));
21장 빌트인 객체
21.1 자바스크립트 객체의 분류
자바스크립트 객체는 표준 빌트인 객체, 호스트 객체, 사용자 정의 객체가 있다.
- 표준 빌트인 객체
- ES 사양에 정의된 객체 → 자바스크립트 실행 환경에 관계 없이 사용 가능.
- 애플리케이션 전역의 공통 기능 제공.
- 전역 객체의 프로퍼티로서 제공되어 전역 변수처럼 언제나 참조 가능.
- 호스트 객체
- 자바스크립트 실행 환경에서 제공하는 객체 → 자바스크립트 실행 환경마다 다름.
- 브라우저 환경 : DOM, BOM, Canvas, fetch 등 클라이언트 사이드 Web API
- Node.js 환경 : Node.js 고유 API
- 사용자 정의 객체
21.2 표준 빌트인 객체
- Object, String, Number, Boolean, RegExp, Promise, JSON, Error 등 40여 개 존재.
- Math, Reflect, JSON : 예외적으로 생성자 함수 객체가 아님.
- 인스턴스 없이 호출 가능한 정적 메서드만 제공.
- 나머지는 모두 생성자 함수 객체로, 인스턴스 생성 가능.
- 프로토타입 메서드와 정적 메서드 제공.
- 인스턴스의 프로토타입 : 표준 빌트인 객체의 prototype 프로퍼티에 바인딩된 객체
- 프로토타입 메서드
- 예) Number 빌트인 생성자 함수로 생성한 숫자를 소수점 자리에서 반올림하는 toFixed()
- 예) https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String 에서 볼 수 있는 수많은 메서드들. 평소에 무심코 썼던 includes, indexOf, split 등
21.3 원시값과 래퍼 객체
원시값 문자열, 숫자, 불리언이 따로 있는데 String, Number, Boolean이 존재하는 이유
문자열이 객체가 아닌데 프로퍼티나 메서드를 사용할 수 있다. 예를 들어, "hello".length 는 5라고 계산되고, "hello".toUpperCase() 는 "HELLO" 로 계산된다. 문자열에도 객체처럼 (dot notation이나 bracket notation으로) 접근하면 자바스크립트 엔진이 암묵적으로 객체를 생성하여 동작을 수행한 뒤 원시값을 되돌려주기 때문이다. 이때 임시로 생성되는 객체를 래퍼 객체라고 한다.
동작 과정
(그림 21-1, 예제 21-6)
- 문자열(원시값)을 값으로 가진 식별자에 객체처럼 접근
- String 생성자 함수의 인스턴스(래퍼 객체) 생성
- 문자열 한글자씩 프로퍼티에 값으로 저장 => 문자열이 객체처럼 존재한다던 거?
- length 프로퍼티 생성
- 암묵적으로 식별자는 래퍼 객체 참조
- 래퍼 객체의 [[StringData]] 내부 슬롯에 문자열 할당
- 래퍼 객체로 상속받은 String.prototype의 메서드 호출
- 식별자는 다시 [[StringData]]에 저장되어있던 원래 문자열을 참조
- 래퍼 객체는 가비지 컬렉션의 대상
따라서 굳이 생성자 함수를 호출하여 인스턴스를 생성하지 않고 래퍼 객체를 활용하자.
※ 심벌은 Symbol 함수로만 생성할 수 있고, 생성자 함수가 아니므로 다른 원시값들과 다르다.
※ 문자열, 숫자, 불리언, 심벌 외의 원시값 null과 undefined는 래퍼 객체 생성하지 않는다.
21.4 전역 객체
전역 객체는 코드 실행도 전에, 가장 먼저 생성되는 객체이자 최상위 객체이다. 표준 빌트인 객체와 환경에 따른 호스트 객체, var 키워드로 선언한 전역 변수/함수를 프로퍼티로 갖는다. 따라서 전역 객체는 빌트인 객체의 최상위 객체이기도 하다. 전역 객체의 프로퍼티를 참조할 때는 window/global을 생략할 수 있다.
21.4.1 빌트인 전역 프로퍼티
= 전역 객체의 프로퍼티
- Infinity : 무한대 숫자값 가짐.
- NaN : 숫자가 아님을 나타내는 숫자값 가짐. Number.NaN 프로퍼티와 같음.
- undefined : 원시값 undefined 가짐.
21.4.2 빌트인 전역 함수
= 전역 객체의 메서드
- eval("string") : 자바스크립트 코드를 나타내는 문자열을 인수로 전달. 표현식은 평가 값하여 값 생성, 표현식이 아니거나 여러 문이면 실행(마지막 결과값 반환).
- 런타임에 실제로 일반 코드처럼 실행됨. 예를 들어, 문자열 코드에 변수선언문이 있으면 런타임에 실행한 뒤 그 변수를 불러올 수 있다.
- 객체 리터럴이나 함수 리터럴은 괄호로 둘러싸야 한다.
- eval 함수는 자신이 호출된 위치, 기존 스코프를 런타임에 동적으로 수정한다. 즉, eval 함수 인수의 코드가 그 자리에 있는 것처럼 동작한다. 그런데 strict mode에서는 eval 함수 자신의 스코프를 생성한다. 코드에 let, const 키워드를 사용한 변수 선언문이 있으면 strict mode가 적용된다.
- 문제 : 보안 취약하고, 최적화 수행되지 않아 속도 느림 => 사용하지 마라!!
- isFinite(number) : 인수가 정상적인 유한수인지 검사
- isNaN(num??) : 인수가 NaN인지 검사
- parseFloat("string") : 문자열 인수를 부동 소수점 숫자(실수)로 해석.
- 앞뒤 공백은 무시, 공백으로 구분된 문자열은 첫번째 문자열만 변환.
- parseInt("string") : 문자열 인수를 정수로 해석
- 두번째 인수로 진법 전달. 생략 시 기본 10진수, 하지만 문자열이 0x로 시작하면 16진수로 해석.
- 반환값은 항상 10진수.
- 10진수 숫자를 다른 진수로 변환하고 싶으면 Number.prototype.toString 메서드 사용.
- encodeURI / decodeURI
- encodeURI : 완전한 URI를 문자열로 받아 이스케이프 처리를 위해 인코딩
- 이스케이프 처리 : 네트워크로 정보 공유 시, 어떤 시스템에서도 읽을 수 있게 아스키 문자 셋으로 변환
- 인코딩 : URI 문자를 이스케이프 처리
- URL은 아스키 문자 셋으로만 구성돼야하므로 인코딩 필요
- url 복사하면 쿼리 스트링이 이상한 문자열로 길게 구성되는 것
- decodeURI : 인코딩된 URI를 인수로 받아 이스케이프 처리 이전으로 디코딩
- encodeURI : 완전한 URI를 문자열로 받아 이스케이프 처리를 위해 인코딩
- encodeURIComponent / decodeURIComponent
- encodeURIComponent : URI 구성요소(쿼리 스트링)를 인수로 받아 인코딩(이스케이프 처리)
- encodeURI는 받은 문자열을 완전한 URI라고 간주하지만, encodeURIComponent는 "쿼리 스트링"의 일부로 간주
- 따라서 쿼리 스트링 구분자 =, ?, &까지 인코딩
- decodeURIComponent : 전달된 URI 구성 요소 디코딩
- encodeURIComponent : URI 구성요소(쿼리 스트링)를 인수로 받아 인코딩(이스케이프 처리)
21.4.3 암묵적 전역
= 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되는 것.
- 함수(전역 하위 스코프) 안에서 할당하려고 했어도 전역 객체의 프로퍼티로 추가됨.
- 전역 변수처럼 동작하지만 전역 객체의 프로퍼티일뿐, 실제로 변수는 아니므로 변수 호이스팅 발생하지 않음.
- 또한 전역 변수와 달리 delete 연산자로 삭제할 수 있음.
22장 this
22.1 this 키워드
프로퍼티를 참조하고 변경하는 메서드가 프로퍼티를 참조하려면, 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
1. 객체 리터럴로 생성하는 경우
자신이 속한 객체를 가리키는 식별자를 재귀적으로 참조 가능.
- 객체 리터럴은 식별자에 할당되기 전에 평가되므로, 메서드가 호출되는 시점에 식별자에 평가된 객체가 할당되어있다. 따라서 메서드 내에서 식별자를 참조할 수 있다.
- 하지만 바람직하지 않은 방식 => this 참조
2. 생성자 함수로 인스턴스 생성하는 경우
반드시 this 참조.
- 생성자 함수를 정의할 때 프로퍼티와 메서드를 추가해야 하는데, 아직 생성자 함수를 호출하여 인스턴스를 생성하기 전이라 식별자를 알 수 없다.
- this : 자기 참조 변수.
- 자신이 속한 객체, 자신이 생성할 인스턴스를 가리키는 특수 식별자 키워드.
- 함수를 호출하면 arguments 객체와 함께 this가 암묵적으로 함수 내부에 전달. 지역 변수처럼 사용할 수 있음.
this 바인딩
: 함수 호출 방식에 의해 동적으로 결정(클래스 기반 언어에서는 항상 인스턴스).
- 객체 리터럴의 메서드 내부에서 this : 메서드를 호출한 객체
- 생성자 함수 내부에서 this : 인스턴스
- strict mode 적용 일반 함수 내부 : 객체의 매서드나 생성자 함수의 내부가 아닌, 일반함수에서는 this가 의미 없기 때문에 undefined 바인딩
22.2 함수 호출 방식과 this 바인딩
22.2.1 일반 함수 호출
this = 전역 객체(strict mode에서는 undefined)
메서드 내부의 중첩 함수, 콜백 함수도 일반 함수로 호출되면 this에 전역 객체가 바인딩되는데, 외부 함수의 this와 일치하지 않아 헬퍼 함수로서 역할하기에 문제가 생긴다.
this 바인딩 일치시키는 방법
- [예제 22-11] 메서드 내부의 this를 that 변수에 할당하여 중첩함수/콜백함수 내부에서 this 대신 that 참조.
- 명시적으로 this 바인딩하는 메서드 : Function.prototype.apply/call/bind
- 화살표 함수 내부 this는 상위 스코프의 this
22.2.2 메서드 호출
this = 메서드 호출한 객체(메서드 소유 객체 X)
[예제 22-14, 22-15]
메서드는 프로퍼티에 바인딩된 함수 객체로, 이 프로퍼티를 가진 객체에 포함된 것이 아니라 독립적인 별개의 객체이다. 따라서 이 메서드를 다른 객체의 프로퍼티에 할당하거나(다른 객체의 메서드) 변수에 할당하여 일반 함수로 호출할 수도 있다. 그럼 메서드로서, 일반 함수로서, 호출된 객체(혹은 전역 객체)에 바인딩된다.
22.2.3 생성자 함수 호출
this = 생성할 인스턴스
생성자 함수도 일반 함수로 호출하면 this에 전역 객체 바인딩. 인스턴스에 등록하려던 프로퍼티와 메서드가 전역 객체에 등록돼버림.
22.2.4 Function.prototype.apply/call/bind 메서드에 의한 간접 호출
this = 첫번째 인수로 전달한 객체
Function.prototype의 메서드로, 모든 함수가 상속 받아 사용 가능.
1. apply, call : (1)this로 쓸 객체와 (2)인수 리스트를 인수로 받아 함수 호출
- 과정 : 함수 호출 → 첫번째 인수(객체)를 호출한 함수의 this에 바인딩,
- 차이
- apply : 호출할 함수의 인수를 배열로 묶어 전달
- call : 인수를 쉼표로 구분한 리스트 형식으로 전달
- 용도 : 유사 배열 객체에 배열 메서드 사용
2. bind : 인수로 전달한 값으로 this 바인딩 교체한 함수 생성하여 반환
- 용도 : 메서드와 내부 중첩/콜백함수 this 불일치 해결
'TIL' 카테고리의 다른 글
[TIL-182] 모던 자바스크립트 24장 클로저 (0) | 2022.05.24 |
---|---|
노마드코더 React Native 챌린지 - 2주차(DAY 8~DAY12) (0) | 2022.05.24 |
노마드코더 유튜브 클론코딩 챌린지(2회차) - 4주차(DAY22~DAY26) (0) | 2022.05.23 |
[TIL-180] MyView 프로젝트 - MUI (0) | 2022.05.20 |
[TIL-179] MyView 프로젝트 - CRA 초기세팅 (0) | 2022.05.17 |