공부+

190317 javascript

-변수의 선언

이처럼 변수는 애플리케이션에서 한번 쓰고 버리는 값이 아닌 일정 기간 유지할 필요가 있는 값에 사용한다.

또한 변수를 사용하면 값의 의미가 명확해져서 코드의 가독성이 좋아진다.

변수의 존재 목적을 쉽게 이해할 수 있도록 의미있는 변수명을 지정하여야한다.

변수명은 식별자(identifier)로 불리기도 하며 명명 규칙이 존재한다.

값을 할당하지 않은 변수 즉 선언만 되어 있는 변수는 undefined로 초기값을 갖는다.

선언하지 않은 변수에 접근하면 ReferenceError가 발생한다.


-변수의 중복 선언

var 키워드로 선언한 변수는 중복선언이 가능하다.

다시말해 변수명이 같은 변수를 중복해 선언해도 에러가 발생하지 않는다.

만약 동일한 변수명이 선언되어 있는 것을 모르고 변수를 중복 선언했다면 의도치 않게 변수의 값을 변경하는 부작용을 발생시킨다.

따라서 변수의 중복 선언은 문법적으로 허용되지만 사용하지 않는 것이 좋다.


-암묵적 전역 변수

변수 선언시 var 키워드를 생략할 수 있다. 이때 변수는 "전역 변수"가 된다. 이러한 변수를 "암묵적 전역 변수"라 한다.

var 키워드의 생략은 문법적으로 허용되지만 의도하지 않게 변수를 전역화할 수 있으므로 사용하지 않는 것이 좋다.


-동적 타이핑

자바스크립트는 동적타입(dynamic / weak type) 언어이다.

이것은 변수의 타입 지정 없이 값이 할당되는 과정에서 값의 타입에 의해 자동으로 타입이 결정될 것이라는 뜻이다.

이를 동적 타이핑이라 한다.




-변수 호이스팅


var 키워드를 사용하여 선언한 변수는 중복 선언이 가능하기 때문에 위의 코드는 문법적으로 문제가 없다.

(1)에서 변수 foo는 아직 선언되지 않았으므로 ReferenceError:foo is not defined가 발생할 것을 기대했겠지만 콘솔에는 undefined가 출력된다.

*이것은 다른 c-family 언어와는 차별되는 자바스크립트의 특징으로 모든 선언문은 호이스팅(Hoisting)이 되기 때문이다.

호이스팅이란 var 선언문이나 function 선언문 등 모든 선언문이 해당 scope(유효범위)의 선두로 옮겨진 것처럼 동작하는 특성을 말한다.

*즉, 자바스크립트는 모든 선언문 (var, let, const, function, function*, class)이 선언되기 이전에 참조 가능하다.


-변수가 어떻게 생성되며 호이스팅은 어떻게 이루어지는가?

변수는 3단계에 걸쳐 생성된다.


선언 단계 (Declaration phase)

변수 객체(Variable Object)에 변수를 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.


초기화 단계 (Initialization phase)

변수 객체에 등록된 변수를 메모리에 할당한다. 이 단계에서 변수는 undefined로 초기화된다.


할당 단계 (Assignment phase)

undefined로 초기화된 변수에 실제값을 할당한다.


var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다.

즉, 스코프에 변수가 등록되고 변수는 메모리에 공간을 확보한 후 undefined로 초기화 된다.

따라서 변수 선언문 이전에 변수에 접근하여도 변수 객체(Variable Object)에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다.

*이러한 현상을 변수 호이스팅(Variable Hoisting)이라 한다.

이후 변수 할당문에 도달하면 비로소 값의 할당이 이루어진다.





앞에 살펴본 예제를 호이스팅 관점에서 다시 한번 알아보도록 하자.


(1)이 실행되기 이전에 (== undefined)

var foo = 123;이 호이스팅 되어 (1)구문 앞에 var foo;가 옮겨진다.

(실제로 변수 선언이 코드 레벨로 옮겨진 것은 아니고 변수 객체 (Variable Object)에 등록되고 undefined로 초기화된 것이다.)

하지만 변수 선언 단계와 초기화 단계가 / 할당 단계와 분리되어 진행되기 때문에 이 단계에서는 foo에는 undefined가 할당되어 있다.

변수 foo에 값이 할당되는 것은 2행에서 실시된다.

(2)에서는 변수에 값이 할당되었기 때문에 123이 출력된다.

자바스크립트의 변수는 다른 c-family와는 달리 블록 레벨 스코프(block-level scope)를 가지지 않고

함수 레벨 스코프(function-level scope)를 갖는다.

단, ECMAScript 6에서 도입된 let, const 키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다.


-함수 레벨 스코프(Function-level scope)

*함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에는 참조할 수 없다.

즉, 함수 내부에서 선언한 변수는 지역변수이며 함수 외부에서 선언한 변수는 모두 전역변수이다.


-블록 레벨 스코프(Block-level scope)

코드 블록 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다.


따라서 코드 블록 내의 변수 foo는 전역변수이므로 전역에 선언된 변수 foo에 할당된 값을 재할당하기 때문에 (3)의 결과는 456이 된다.


-var 키워드로 선언된 변수의 문제점

ES5에서 변수를 선언할 수 있는 유일한 방법은 var 키워드를 사용하는 것이다.

var 키워드로 선언된 변수는 아래와 같은 특징을 갖는다.

이는 다른 c-family언어와는 차별되는 특징 (설계상의 오류)이므로 주의를 기울이지 않으면 심각한 문제를 발생시킨다.


-함수 레벨 스코프(function-level scope)

전역 변수의 남발

for loop 초기화식에서 사용한 변수를 for loop 외부 또는 전역에서 참조할 수 있다.


-var 키워드 생략 허용

의도하지 않은 변수의 전역화


-중복선언 허용

의도하지 않은 변수값 변경


-변수 호이스팅

변수를 선언하기 전에 참조가 가능하다.


대부분의 문제는 전역변수로 인해 발생한다.

전역 변수는 간단한 애플리케이션의 경우, 사용이 편리한 면이 있지만 불가피한 상황을 제외하고 사용을 억제해야한다.

*전역 변수는 유효 범위(scope)가 넓어서 어디에서 어떻게 사용될 지 파악하기 힘들다.

이는 의도치 않은 변수의 변경이 발생할 수 있는 가능성이 증가한다.

또한 여러 함수와 상호 의존하는 등 부수 효과(side effect)가 있을 수 있어서 복잡성이 증가한다.

변수의 유효범위(scope)는 좁을수록 좋다.

ES6는 이러한 var의 단점을 보완하기 위해 let과 const 키워드를 도입하였다.


-표현식과 연산자

값은 다양한 방법으로 생성할 수 있다. 여기서 말하는 다양한 방법이란 표현식을 말한다.

표현식은 리터럴, 식별자, 연산자, 함수 호출 등의 조합을 말한다.

표현식은 평가(evaluation, 표현식을 실행하여 하나의 값을 만드는 과정)되어 하나의 값을 만든다.

즉, 표현식은 하나의 값으로 평가될 수 있는 문(statement)이다.


표현식은 리터럴 표현식, 식별자 표현식, 연산자 표현식, 함수/메소드 호출 표현식등으로 나누어 볼 수 있지만

결국 평가되어 하나의 값을 만든다는 점에서 모두 동일하다.



표현식은 평가되어 결국 하나의 값이 되므로 표현식과 값은 동등한 관계, 즉 동치(Equivalent)다.

다시 말해 표현식은 값처럼 사용할 수 있다. 이것은 값이 위치할 수 있는 자리에는 표현식도 위치할 수 있다는 의미다.

예를 들어 산술 연산자 +의 좌항과 우항에는 숫자 값이 위치해야한다.

숫자 값으로 평가될 수 있는 표현식이라면 숫자 값 대신 사용할 수 있다.


이처럼 표현식은 다른 표현식의 일부가 되어 새로운 값을 만들어낼 수 있다.

연산자 표현식은 표현식을 결합해 새로운 값을 만들어 내는 가장 일반적인 표현식이다.


-문과 표현식
문(statement)은 자바스크립트 엔진에게 내리는 명령이다. 문이 실행되면 무슨 일인가가 일어나게 된다.
변수 선언문을 실행하면 변수가 선언이 되고, 할당문을 실행하면 할당이 된다.
조건문을 실행하면 주어진 조건에 따라 코드 블록({})의 실행이 결정되고, 반복문을 실행하면 코드 블록이 반복 실행된다.


문은 리터럴, 연산자, 표현식, 키워드 등으로 구성되며 세미콜론(;)으로 끝나야한다. (코드 블록 {...}은 제외)
문의 끝에 붙이는 세미콜론은 옵션으로 쓰지 않아도 상관없다.
자바스크립트 엔진이 스크립트를 해석할 때, 자바스크립트 엔진에는 문의 끝이라고 예측되는 지점에
세미콜론을 자동으로 붙여주는 세미콜론 자동 삽입 기능(ASI, automatic semicolon insertion)이 있기 때문이다.
하지만 세미콜론 자동 삽입 기능의 예측과 개발자의 예측과 다른 경우가 간혹 있다.


세미콜론을 반드시 붙여야 한다는 주장이 대다수를 차지하지만 붙이지 말아야 한다는 주장도 설득력이 있다.
하지만 ESLint와 같은 정적 분석 도구에서도 세미콜론 사용을 기본으로 설정하고 있고
TC39(ECMAScript 기술 위원회)도 세미콜론 사용을 권장하는 분위기 이므로 세미콜론을 붙이도록 하겠다.
자바스크립트의 모든 코드는 문 또는 표현식이다.
*자연어에서 문이 마침표로 끝나는 하나의 완정한 문장(Sentence)이라고 한다면 표현식은 문을 구성하는 요소이다.
표현식은 그 자체로 하나의 문이 될 수도 있다.
*표현식과 문은 유사하여 구별이 어려울 수 있다. 표현식은 평가되어 값을 만들지만 그 이상의 행위는 할 수 없다.
문은 var, let, const, function, class와 같은 선언 키워드를 사용하여 "변수나 함수를 생성하기도 하고"
if, for, while문과 같은 제어문을 생성하여 "프로그램의 흐름을 제어"하기도 한다.

*표현식의 역할은 "값을 생성하는 것"이다.
문의 역할은 "표현식으로 생성한 값을 사용해 컴퓨터에게 명령을 내리는 것"이다.

*문에는 "표현식인 문"과 "표현식이 아닌 문"이 있다.
*예를 들어 선언문은 값으로 평가될 수 없다. 따라서 "표현식이 아닌 문"이다.
*하지만 할당문은 그 자체가 "표현식인 문"이다.



위 예제의 선언문은 표현식이 아닌 문이다.

다시 말해 값으로 평가될 수 없다. 따라서 선언문은 값처럼 사용할 수 없다.

이에 반해 할당문은 그 자체가 표현식이다.
다시 말해 할당문은 표현식인 문이다.

크롬 개발자 툴에서 표현식이 아닌 문은 언제나 undefined를 반환하고
표현식인 문은 언제나 값을 반환한다.

*할당문은 "표현식인 문"이기 때문에 값처럼 사용할 수 있다.


할당문을 값처럼 변수에 할당했다. 할당문은 할당한 값으로 평가된다.
즉, x = 100은 변수 x에 할당한 값 100으로 평가될 수 있다.
*따라서 변수 foo에는 100이 할당된다.






그 다음.




-----



[Reference, origin]

PoiemaWeb


'공부+' 카테고리의 다른 글

190419 javascript  (0) 2019.04.19
190329 javascript 제어문  (0) 2019.03.29
190326 javascript  (0) 2019.03.26
190318 javascript  (0) 2019.03.18
190316 javascript  (0) 2019.03.16