3.Javascript 변수
1. Variable (변수)
-
1.1 변수 키워드 의 종류
-
var , let , const
-
EM6 부터는 키워드를 let과 const만을 사용한다.
-
-
1.2 var의 문제점
-
함수레벨스코프(Funtion-level cope) , 함수안의 “{}”
- 자바스크립 변수는 함수레벨 스코프이며 대표적으로 var가 있다. 함수레벨 스코프란 함수 안에 선언된 변수는 함수 밖에서 가져올수 없으나, 함수 밖에서 선언된 변수는 함수 안에서 가져올수 있다. 그러나 블록에서는 함수 안에 선언된 변수는 함수 밖에서 가져올수도 있고, 함수 밖에서 선언된 변수를 함수 안아서도 가져올수 있다. (하지만 ES6 에서 도입한 const와 let 키워드를 사용함에따라 블록레벨스코프를 사용할수 있게 되었다.)
var a = 20; function abc() { console.log(a); var b = 30; } { var c = 40; } abc(); // 20 console.log(c); // 40 console.log(b); // b is not defined
-
이렇게 블록안에서 선언된 변수조차 외부에서 가져올수 있기 때문에, 전역 변수의 남발할수 있다.
-
for loop(for 문) 초기화 식에서 사용한 변수를 for loop 외부 또는 전역에서 참조할수 있다. == 블록안에서 선언된 변수를 블록 외부에서 또는 전역에서 참조할수 있다.
-
-
var 키워드 생략 허용
- var 키워드를 생략하여 사용하면 의도하지 않게 변수가 전역화 된다.
-
중복 선언 허용
- 의도하지 않은 변수값 변경
-
변수 호이스팅
- 변수를 선언하기 전에 참조가 가능하다.
const hoisting = () => { console.log("First-Name:", name); var name = "Marcus"; console.log("Last-Name:", name); }; hoisting(); // First Name : undefined // Last Name : Marcus // First Name이 undefined인 이유는 지역변수 name이 호이스트 되었기 때문이다.
위 hoisting이라는 함수는 자바스크립트로 이해하면 다음과 같이 표현할 수 있다. 변수 호이스팅이 일어나, name이 선언된 곳의 스코프 가장 위로 올라가게 되며, 선언단계와 초기화 단계가 한번에 이루어지게 된다.
- 선언단계 : 변수객체에 변수를 등록한다.
- 초기화 단계 : 변수 객체에 등록된 변수를 메모리에 할당한다. 이 단계에서 변수는 undefined로 초기화
- 할당 단계 : undefined로 초기화된 변수에 실제값을 할당.
const hoisting = () => { var name; // name 변수는 호이스트 되었습니다. 선언과 초기화가 한번에 이루어졌습니다. console.log("First name : " + name); // First Name : undefined name = "Marcus"; // 할당 단계 console.log("Last Name : " + name); // Last Name : Ford };
-
1.3 var 문제의 개선방향
- 블록레벨스코프(Block-level Scope) : const와 let이 대표적인 블록레벨 스코프이며 , 블록안에 있는 변수를 블록외부에서 가져올수 없고, 블록 밖에 있는 변수는 블록 안에서 가져올수 있다.
let a = 20; function abc() { console.log(a); let b = 30; } { let c = 40; } abc(); //20 console.log(b); // b is not defined console.log(c); // c is not defined
-
1.4 let
-
- 1.5 Constants
- 웬만해서는 값이 할당된 다음에 값이 변경되지 않을 데이터 타입을 넣을때 사용하는 키워드
2. 호이스팅(변수 호이스팅 , 함수 호이스팅)
-
2.1 호이스팅의 개념 : 해당 Scope의 선두로 옮겨진 것처럼 동작하는 특성을 말한다. 쉽게 말하면 변수가 함수내에서 정의되었을 경우 선언이 함수의 최상위로, 함수 바깥에서 정의되었을 경우는 전역 컨텍스트의 최상위로 변경됩니다.그리고 자바스크립트의 모든 선언문(var,let,const,function,class)이 호이스팅이 된다.
-
호이스팅의 단계 : 선언단계, 초기화 단계, 할당단계
-
선언단계 : 변수 객체에 변수를 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
-
초기화 단계 : 변수 객체에 등록된 변수를 메모리에 할당한다. 이 단계에서는 변수는 undefined로 초기화된다.
-
할당단계 : undefined로 초기화된 변수에 실제값을 할당한다.
-
-
2.2 변수 호이스팅(1) : 선언된 변수는 선언단계와 초기화 단계가 한번에 이루어진다.즉 스코프에 변수가 등록되고 변수는 메모리에 공간을 확보한후 undefined로 초기화 된다. 따라서 변수 선언문 이전에 변수에 접근하여도 에러가 발생하지 않는다.
-
변수를 선언하기 전에 참조가 가능하다.
const hoisting = () => {
console.log("First-Name:", name);
var name = "Marcus";
console.log("Last-Name:", name);
};
hoisting();
// First Name : undefined
// Last Name : Marcus
// First Name이 undefined인 이유는 지역변수 name이 호이스트 되었기 때문이다.
위 hoisting이라는 함수는 자바스크립트로 이해하면 다음과 같이 표현할 수 있다. 변수 호이스팅이 일어나, name이 선언된 곳의 스코프 가장 위로 올라가게 되며, 선언단계와 초기화 단계가 한번에 이루어지게 된다.
const hoisting = () => {
var name; // name 변수는 호이스트 되었습니다. 선언과 초기화가 한번에 이루어졌습니다.
console.log("First name : " + name); // First Name : undefined
name = "Marcus"; // 할당 단계
console.log("Last Name : " + name); // Last Name : Ford
};
- 2.3 변수 호이스팅(2) : let 과 const도 호이스팅이 될까?
const hoisting = () => {
console.log("Name:", name);
let name = "Marcus"; //변수가 실제 있는 위치
};
hoisting();
// ReferenceError: name is not defined
-
결론 : let과 const 호이스팅이 된다는 말이다, 하지만 코드 실행이 변수가 실제 있는 위치에 도달할 때까지 엑세스를 접근 할수가 없다. 이 개념을 더 자세하게 알고 싶으면 호이스팅과 TDZ에 잘 나와있다.
-
2.4 함수 호이스팅 : 기존에 변수이 선언,초기화이 한번에 이루어졌다면, 함수는 선언,초기화,할당이 한번에 이루어진다.그렇기 때문에 함수 선언의 위치와는 상관없이 소스 내 어느 곳에서든지 호출이 가능하다.
다음은 함수 선언문으로 함수를 정의한 경우이다.
let res = square(5);
function square(number) {
return number * number;
//25
}
다음은 함수 표현식으로 함수를 정의한 경우이다.
var res = square(5); //squre is not a function
console.log(square); // undefined
var square = function (number) {
return number * number;
};
함수 선언문의 경우와는 달리 TypeError가 발생하였다. 함수표현식의 경우 함수 호이스팅이 아니라 변수 호이스팅이 발생한다. 즉 선언과 초기화 할당이 한번에 이루어지는것이 아닌 선언 및 초기화 와 할당이 분리되어 이루어진다는 말이다.즉 여기서 square 는 선언과 초기화가 되었고 이 변수는 undefined로 초기화 되고 실제값(function(number){})는 할당문에서 이루어지기 때문에 함수값을 받지 못한 squaure은 함수가 아니라는 표시가 나오게 된다.(squre is not a function).
만약 다음과 같은 경우일때는?
let res = square(5); //squre is not a function
console.log(square); //Cannot access 'square' before initialization
let square = function (number) {
return number * number;
};
앞에서 설명한것처럼 코드 실행이 변수가 실제 있는 위치에 도달할 때까지 엑세스를 접근 할수가 없으므로 (Cannot access ‘square’ before initialization) 오류가 뜨게 된다.
만약 또 다음과 같은 경우에는?
var res = square(5); //squre is not a function
console.log(square); //Cannot access 'square' before initialization
let square = function (number) {
return number * number;
};
let res = square(5); //squre is not a function
console.log(square); //undefined
var square = function (number) {
return number * number;
};
따라서 자바스크립트의 권위자는 이와 같은 문제 때문에 함수표현식(anonymouse function과 named function이 있음)만을사용할 것을 권고하고 있다.
3. Variable types (데이터(변수) 타입)
-
3.1 Premitive type(원시 타입) : 더이상 작은 단위로 나눠질수 없는 하나의 아이템, Single item
-
3.1.1 Number : 정수나 소수점에 상관없이 모두 number타입이다.
const count = 15; const size = 17.1; console.log(`value: ${count}, type : ${typeof count}` // value : 15 type :number console.log(`value: ${count}, type : ${typeof count}`); // value : 17.1 type :number
-
3.1.2 infinity , -infinity , Nan
const infinity = 1 / 0; const negativeInfinity = -1 / 0; const nAn = "not a number" / 2; console.log(infinity); // infinity console.log(negativeInfinity); // -infinity console.log(nAn); // Nan
-
3.1.3 BigInt (그냥 있다고만 알아두자)
const bigInt = 12313413491308401938413904091849018940n; console.log(`value: ${bigInt} type: ${typeof bigInt}`);
-
3.1.4 String : 문자열이던 문자행이던 모두 String으로 취급한다.
const char = "c"; const brendan = "brendan"; const greeting = "hello" + brendan; console.log(`value:${greeting}, type:${typeof greeting}`); // value hello brendan, type : string
-
3.1.5 boolean
-
false : 0 , null , undefined , NaN ,’’
-
true : any other value
-
-
3.1.6 null , undefiend
let nothing = null; console.log(`value:${nothing}, type : ${typeof nothing}`); // value : null , type : object let x; // === let x = undefined console.log(`value:${x}, type : ${typeof x}`); //value : undefined , type : undefined
-
여기서 null의 타입이 object가 나왔는데, 이것은 javascript의 오류라고 하니 참고만 해두자.
-
또한 undefined는 의도적으로 할당하는 경우는 권장하지 않는다. 변수의 값이 없다고 명시하고 싶은경우 undefined이 아닌 null을 할당한다.
-
-
3.1.7 Symbol : 맵이나 자료구조에서 고유한 식별자가 필요하거나 동시다발적으로 코드에서 우선순위를 주고 싶을때 사용, symbol은 바로 출력할수 없고 .description을 이용해 string으로 변환하여 출력해야한다.
const symbol1 = Symbol("id"); const symbol2 = Symbol("id"); console.log(symbol1 === symbol2); //false const symbol1 = Symbol.for("id"); const symbol2 = Symbol.for("id"); console.log(symbol1 === symbol2); //true console.log(`value: ${symbol1}, type: ${typeof symbol1})`; //Cannot convert a Symbol value to a string console.log(`value: ${symbol1.description}, type: ${typeof symbol1})`; // vlaue : id , type : symbol
-
-
3.2 object type(객체 타입): Single item box
-
3.2.1 객체
-
여기서 주목해야할 부분은 ellie가 const인데 접근이 eliie의 name 과 age에는 접근이 가능하다 그 이유는 그림을 보면 더 이해가 될것이다.
const ellie = { name: "ellie", age: 20 }; ellie.age = 21; //ellie는 const인데 가능하다고? ellie 는 const이므로 이 주소로 가는 길은 접근이 불가능 하지만 ,elli object 안에는 변수가 존재하므로 ,그 값들은 접근이 가능하다. let c = ellie; //불가능 const a = 111111; let b = a; //불가능
;
-
4. 데이터 타입의 Mutable vs Immutable
-
4.1 Immutable : 변하지 않은 (변경이 불가능하다는 뜻은 메모리 영역에서의 변경이 불가능하다는 뜻이지, 재할당은 가능하다.)
-
Primitive 타입 즉 원시타입은 변경불가능한 값이다.
-
Boolean , null , undefined , Number , String , Symbol(New in ES6)
-
메모리 영역에서 변경 불가 , 재할당 가능의 예시
var str = "Hello"; str = "world";
첫번째 구문이 실행되면 메모리에 문자열 “Hello”가 생성되고 식별자 str은 “Hello” 값을 가리킨다. 그리고 두번째 구문이 실행되면 이전에 생성된 문자열 “Hello”를 수정하는 것이 아니라 새로운 문자열 “world” 메모리에 생성하고 식별자는 이것을 가리킨다. 즉 문자열 “Hello”와 “world”는 모두 메모리에 존재한다. **변수 str은 “Hello”를 가리키고 있다가 문자열 “world”를 가리키도록 변경되었을뿐이다.
var statement = "i am an immutable value"; var otherStr = statement.slice(8, 17); console.log(otherStr); // "immutable" console.log(statement); // "i am an immutable value"
2행에서 String 객체의 slice() 메소드는 statement 변수에 저장된 문자열을 변경하는것이 아니라 사실은 새로운 문자열을 생성하여 반환하고 있다. 그 이유는 문자열은 immutable하기 때문이다.
-
-
4.2 Mutable : 변하기 쉬운
- object 타입 즉 객체타입은 변경 가능한 값이다. 메모리에 저장할때 값이 아닌 주소를 저장한다.
-
object 타입의 예시
var user = {
name: "Lee",
address: {
city: "Seoul",
},
};
var myName = user.name;
user.name = Kim;
console.log(myName); // "Lee"
myName = user.name;
console.log(myName); // "KIm"
user.name의 값을 변경했지만 변수 myName의 값은 변경되지 않았다. 이는 변수 myNmae에 user.name을 할당했을 떄 user.name의 참조를 할당하는 것이 아니라 immutable한 값 “Lee”가 메모리에 생성되고 MyNmae은 이것을 참조하기 때문이다. 따라서 user.name의 값이 변경되더라도 변수 MyName이 참조하고 있는 “Lee”는 변함이 없다.
var user1 = {
name: "Lee",
address: {
city: "Seoul",
},
};
var user2 = uesr1;
user2.name = "Kim";
console.log(user1.name); // "Kim"
console.log(user2.name); // "KIm"
위의 경우 객체 user2의 name 프로퍼티에 새로운 값을 할당하면 객체는 변경불가능한 값이 아니므로 객체 user2는 변경된다 그런데 변경하지 않은 객체 user1도 동시에 변경된다 이는 user1과 user2가 같은 주소를 참조하고 있기 때문이다.
-
4.3 불변데이터 패턴
위에서 본것처럼 객체는 mutable하기 때문에. 의도하지 않은 객체의 변경이 발생하는 원인의 대다수는 “래퍼런스를 참조한 다른객체에서 객체를 변경” 하기 떄문이다. 따라서 이 문제를 해결하기 위한 방법(이 불변데이터 패턴이다. 3가지 패턴이 있지만, 실무에서 사용하는 패턴이 무엇인지 파악한다음에 더 배워보자 (object.freeze(), object.assign , immutable.js)
5. Dynamic typing : 할당된 값에 따라 타입이 변함
단점 : 데이터가 타입이 명시되어잇지 않고 , 내가 잘못 넣었을때도 값에 의해 타입이 변형되므로 오류가 발생하지 않아 문제가 발생할 가능성이 높아진다.
let text = "hello";
console.log(`value: ${text}, type: ${typeof text}`);
//value : hello , type : string
text = 1;
console.log(`value: ${text}, type: ${typeof text}`);
//lvaue : 1 , type : number
text = "7" + 5;
console.log(`value: ${text}, type: ${typeof text}`);
//value : 75 , type : string
이 단점을 해결하기위해 등장한것 Typescript Typescript는 말그대로 javascript에 type을 올려둔것으로 데이터 타입을 명시함에 따라 Javascript의 Dynamic typing의 문제를 해결해주었다.
Leave a comment