Javascript Strict Mode, with

21518 단어 jsjs

Strict Mode 란

Javascript의 ES5에 추가된 Strict Mode에 대해서 알아보자.

function func() {
  x = 10;
  return x;
}

console.log(x);

위 코드에서 x는 선언되지 않았다.

여기서 x값을 참조하려면 자바스크립트 엔진은 변수 x가 어디서 선언되었었는지 스코프 체인을 찾아볼 것이다.

상위 스코프로 가서도 찾지 못하면 참조 에러가 나는 것이 아니라 전역 객체에 x를 생성하여 전역변수가 된다. 이런 변수를 암묵적 전역변수라고 한다. 이런 상황은 개발자가 의도한 것이 아닌 실수로 인한 것이 많을 것이다. 이런 상황을 만들지 않으려면 선언 키워드를 사용하여 변수를 선언하여 사용한다.

이런 개발자의 실수를 에러로 표현해주면 좋을텐데 표현되지 않는 오류들이 있다. 그렇다면 개발자는 잘못된 상황이란 것을 알아차리기 어렵고 나중에 알게될 때는 해결하기 어려운 상황이 될 수도 있다. 이런 기존에 무시되던 오류를 명시적으로 에러를 발생시키게 지원된 것이 strict mode 이다. 이런 역할을 하는 도구 중에는 ESLint 가 있다. ESLint는 strictMode와 유사한 효과를 얻을 수 있다. ESLint는 문법적 오류와 잠재적 오류까지 리포팅해준다.

Strict Mode 적용

'use strict'; 이 문법을 적용시키고자 하는 곳 위에 추가한다. 이 문법은 코드보다 위에 위치하는 것이 좋다. 주로 전역의 제일 위나 함수의 제일 위에 위치한다. 선두에 위치하지 않으면 제대로 작동하지 않는다.

전역 사용

'use strict';

function func() {
  a = 10; 
}

func();

전역의 제일 위에 추가하면 스크립트 전체에 strict mode가 적용된다. 전역의 지역 스코프 모두 적용된다. 위의 경우 a가 정의되지 않았다는 ReferenceError: a is not defined가 발생한다.

함수 사용

function func() {
  'use strict';
  a = 10; 
}

func();

함수의 제일 위에 추가하면 함수내부와 내부함수에 strict mode가 적용된다. 함수 안에 적용되면 위에는 ReferenceError: a is not defined 에러가 발생한다.

strict mode 사용시 유의사항

1. 전역에 strict mode 를 사용할 때

전역에 사용한 strict mode는 해당 스크립트에 적용된다. 같은 스크립트에 있는 함수들은 모두 적용되지만 다른 스크립트는 적용되지 않는다. 여러 script가 적용된다면 모든 script에 전역으로 적용해야 한다. 전역 strict mode는 스크립트 단위로 적용되기 때문이다.

<!DOCTYPE html>
<html>
<body>
  <script>
    'use strict';
  </script>
  <script>
    a = 1; // 에러가 발생하지 않는다.
    console.log(a); // 1
  </script>
  <script>
    'use strict';
    b = 1; // ReferenceError: b is not defined
    console.log(b);
  </script>
</body>
</html>

외부 라이브러리 중에는 non strict mode를 사용하는 라이브러리도 있고 어떤 스크립트는 적용하고 어떤 스크립트는 적용하지 않으면 에러를 발생할 수 있다. 전역에 사용하는 것은 여러 상황을 고려하고 사용하는 것이 좋다. 잘 고려하기 어렵다면 전역에 사용하는 것은 피하는 것이 좋다. 즉시 실행 함수에 strict mode를 적용하는 방법이 있다.

(function () {
  'use strict';
  ...
}());

2. 함수에서 strict mode를 사용할 때

전역에서 스크립트 단위로 strict mode를 적용할 때와 비슷하다. 함수 단위에서도 제일 위에 'use strict';를 추가하면 strict mode를 적용할 수 있다. 그렇게되면 모든 함수에 다 strict mode를 적용해야 한다. 어떤 함수에는 적용하고 적용하지 않는다면 에러가 발생하게 된다.

전역에서 언급했던 것 처럼 스크립트 단위로 즉시실행함수에 strict mode를 적용하는 것이 가장 관리하기가 용이하다.

strict mode가 발생시키는 에러

1. 선언하지 않은 변수를 참조하는 경우

(function () {
  'use strict';
  a = 10;
  console.log(a);
}());

a를 선언하지 않고 참조하면 ReferenceError: a is not defined 에러를 발생시킨다.

2. 변수, 함수, 매개변수의 삭제

(function () {
  'use strict';

  let a = 1;
  delete a;

  function func(b) {
    delete b;
  }
  delete func;
}());

선언한 변수, 매개변수, 함수를 strict mode 안에서 삭제하면 SyntaxError: Delete of an unqualified identifier in strict mode. 에러를 발생시킨다.

strict mode가 없으면 아무런 에러가 발생하지 않는다.

3. 매개변수 중복사용

(function () {
  'use strict';

  function func(a, a) {
    return a + a;
  }
  console.log(foo(1, 2));
}());

하나의 함수에서 같은 이름의 매개변수를 중복 사용하면 SyntaxError: Duplicate parameter name not allowed in this context 에러를 발생시킨다.

4. with 문의 사용

(function () {
  'use strict';

  with({ x: 1 }) {
    console.log(x);
  }
}());

with 문을 사용하면 문법 에러 SyntaxError: Strict mode code may not include a with statement를 발생시킨다.

5. 일반 함수의 this

(function () {
  'use strict';

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

  function Func() {
    console.log(this); // Func
  }
  new Func();
}());

생성자 함수가 아닌 일반 함수에서 this를 호출하면 undefined가 된다. 일반 함수에서는 this가 사용될 일이 없기 때문인데 에러는 나지 않는다.


생소한 with 에 대해 알아보자.

(function () {
  let user = { 
    name: "kimcoding", 
    role: "back-end", 
    language: "java" 
  } 
  with (user) { 
    console.log(name === "kimcoding"); // true
    console.log(role === "back-end");  // true
    console.log(language === "java");  // true
    language = "javascript"; 
  } 
  console.log(user.language === "javascript"); // true
}());

user 객체에 정보가 담겨있는데 with문을 사용하고 객체를 괄호 안에 넣으면 블록 안에서는 name, role 과 같은 키 값에 user객체 정보를 입력하지 않고 바로 접근이 가능하다. with 문 안에서 language에 바로 접근이 가능해 지는것이다. 이것은 user 객체를 scope chain에 추가했기 때문이다.
다른 예를 보자.

(function acc() {
  let r = 10, a, x, y; 
  with (Math) { 
    a = PI * r * r; 
    x = r * cos(PI); 
    y = r * sin(PI / 2); 
  }
  console.log(a, x, y); // 314.1592653589793 -10 10
}());

Math를 객체 안에 있는 메소드들을 더 손 쉽게 접근할 수 있다.

하지만 전문가들은 사용하면 안된다고 한다. 가장 큰 이유는 with로 인한 모호성 때문이다.

(function func(value, obj) { 
  with (obj) { 
    value = "parameter or obj?"; 
    console.log(value); 
  }
}());

value 매개변수에서 왔는지 obj의 키 중에 value가 있어서 value 키에서 왔는지 이 코드만으로 판단할 수 있을까? 이렇게 함수의 매개변수와 참조하려는 객체의 키 값이 같은 경우 어디로부터 참조하였는지 알기가 어렵게 된다.

조금 편하긴하지만 코드 가독성이 떨어질 수 있고 유지 보수에 혼란을 가져올 수 있기 때문에 권장하지 않는다고 한다.

참고자료 [All-round programmer]

좋은 웹페이지 즐겨찾기