자바스크립트 중급 강좌 ①

프론트엔드/Javascript|2021. 5. 30. 20:03

변수/ 호이스팅/ TDZ(Temporal Dead Zone)


변수

let / const es6부터 생겼다.

var과의 차이를 알아보자.

var는 한번 선언된 변수를 다시 선언할 수 있다.

var name = 'Mike';

console.log(name); // Mike

var name = 'Jane' ; 

console.log(name); //Jane


let name = 'Mike';

console.log(name); // Mike

let name = 'Jane' ;

console.log(name);  // error 발생


var는 선언하기 전에 사용할 수 있다.

console.log(name); // undefined, 에러 발생하지 않는다.

var name = 'Mike'; 

 

위는 아래처럼 동작하기 때문에

var name; 

console.log(name); //undefined

name = 'Mike';

var로 선언한 모든 변수는 코드가 실제로 이동하지는 않지만, 최상위로 끌어 올린 것처럼 동작한다.

이를 호이스팅이라고 한다. 

undefined라고 찍히는 이유는 선언은 호이스팅 되지만, 할당은 호이스팅 되지 않기 때문이다.


같은 상황에서 let은 var과 다르게 에러가 난다.

console.log(name) // ReferenceError

let name = 'Mike' ;

사실 let과 const도 호이스팅이 된다.

호이스팅 : 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것 처럼 행동


 

그러면 왜 let은 에러가 발생하는가?

Temporal Dead Zone (TDZ) 때문이다.

console.log(name)      // Temporal Dead Zone

const name = "Mike" // 함수 선언 및 할당

console.log(name) // 사용 가능

TDZ 영역에 있는 변수들은 사용할 수 없다. let과 const는 TDZ영역에 영향을 받는다.

할당을 하기전에는 사용할 수 없다.

이는 코드를 예측가능하게 하고 잠재적인 버그를 줄일 수 있다.


위 코드는 문제가 없다.


위 코드는 문제가 발생한다.

let은 호이스팅 되지 않는 다고 오해하기 쉽다. 

호이스팅은 스코프 단위로 일어난다. 

let으로 선언한 두번째 변수가 호이스팅을 일으킨다.



var 은 선언과 초기화를 동시에 된다.

할당 전에 호출하면 에러를 내지 않고 undefined 가 나온다.


let은 선언단계와 초기화단계가 분리되어서 진행된다.

호이스팅 되면서 선언단계가 이루어지지만, 초기화 단계는 실제 코드에 도달했을 때 되기 때문에 레퍼런스 에러가 발생한다.


const는 선언과 할당이 동시에 되어야 한다.

let 과 var은 선언만 해두고, 나중에 할당하는 것을 허용한다. let과 var는 값을 바꿀 수 있으니까


name과 age는 괜찮지만, const로 선언한 gender 부분에서는 에러가 발생한다.

선언하면서 바로 할당을 안했기 때문이다.


스코프

블록 스코프는 모든 코드 블록 내에서 선언된 변수는 코드 블록 내에서만 유효하며, 외부에서는 접근할 수 없다.

즉, 코드 내부에서 선언한 변수는 지역변수이다. 여기서 코드블록은 함수, if문, for문, while문, try/catch문을 의미한다.


반면 함수 스코프는 함수 내에서 선언된 변수만 그 지역 변수가 된다.

예를 들어서 if문 안에서 var로 선언된 변수는 if문 밖에서 사용이 가능하다. 

그러나, let과 const는 중괄호 {  } 안에서만 사용가능하다. 


var도 함수내에서 선언되면 함수밖에서 사용할 수 없다.

유일하게 벗어날수 없는 스코프가 함수라고 생각하면 된다.


생성자 함수 

 

하지만, 비슷한 객체를 여러개 만들어야 하는 상황이 생긴다.

그럴 때 쓸 수 있는 것이 생성자 함수이다.


생성자함수는 첫글자를 대문자로 해서 함수를 만들어 준다.

이름과 나이를 인자로 받아서 this에 넣어주고 있다.

new 연산자를 사용해서 함수를 호출한다.

각각 다른 변수명으로 세번 연달아서 호출했다. 전달하는 값도 각각 다르게 전달했다.

순식간에 비슷한 객체 3개를 만들었다.


생성자함수는 붕어빵 틀이나 와플 팬이라 생각하면 된다. 필요한 재료들을 넣어주고 찍어낸다.

필요한 재료는 이름과 나이, 생성되는 객체는 와플이다.


생성자 알고리즘원리

일단, 빈 객체를 만들고 this에 할당한다. this에 name가 age를 추가하고 this를 반환한다.

실제로 저 두줄은 코드에 없다. 

이렇게 객체를 만들면, 일일이 객체리터럴을 쓰는것보다 훨씬 빠르고 일관성있게 객체를 만들 수 있다.

스펙이 변경되어도 생성자 함수만 고치면된다.


this는 user5이다. 


실습> 

function Item(title, price){

//this = {};

this.title = title;

this.price = price;

this.showPrice = function(){

console.log(`가격은 ${price}원 입니다.`);

};

//return this;

}

const item1 = new Item("인형", 3000); 

const item2 = new Item("가방", 4000);

const item3 = new Item("지갑", 9000);

console.log(item1, item2, item3); // Item {title: "인형", price:3000, showPrice: f}  Item{title:"가방", price:4000, showPrice: f} Item{title:"지갑", price:9000, showPrice:f} 출력된다.

item3.showPrice(); // 가격은 9000원 입니다.


Computed property(계산된 프로퍼티)

 

let a = 'age' ;

const user = {

     name : 'Mike',

     [a] : 30 // age : 30 과 같다. 변수 a에 할당된 값이 들어간다. [a] 을 Computed proerty라고 한다.

}


const user = {

[1 + 4] : 5,

["안녕" + "하세요"] : "Hello"

}

console.log(user) // { 5: 5, 안녕하세요: "Hello" } 출력, 식자체를 넣는 것도 가능하다.


객체에 사용할 수  있는 메소드

Object.assign() : 객체복제

Object.keys()  : 객체 키를 배열로 반환

Object.values() : 객체 값을 배열로 반환

Object.entries() : 객체 키와 값 쌍을 배열로 반환

Object.fromEntries() : 키와 값 쌍으로 된 배열을 객체로 반환


Object.assign()

객체를 복제할 수 있다.

const user = {name : 'Mike',age: 30}

const cloneUser = user; //이렇게 하면 객체 복제가 되지 않는다. user변수에는 객체 자체가 들어가 있는 것이 아니라, 객체가 저장되어 있는 메모리 주소인 객체에 대한 참조 값이 저장됩니다. cloneUser 를 만들어서 user를 넣으면 , 객체가 복사되어서 들어가는게 아니라, 그 참조 값만 복사된다.즉 cloneUser의 name을 바꾸면, user의 name도 바뀐다. 하나의 객체를 두변수가 접근하고 있는거다.동일하게 복제하려면 Object.assign() 메소드를 써야한다.

const newUser = Object.assign({}, user); // {}는 초기값이다. 두번째 매개변수로 들어온 객체들이 초기값에 병합된다.newUser.name = 'Tom' ; // 이름을 바꾸어도 user에는 변함이 없다.

console.log(user.name); // 'Mike' newUser != user 


Object.assign({gender:'male'}, user); // 성별값만 있는 객체가 user 객체를 병합하는거다.  즉 gender : 'male', name : 'Mike', age: 30 총 3개의 프로퍼티를 가지게 된다.

Object.assign({name :'Tom' }, user); // 만약 병합을 하는데, name이라는 키가 같다면 Tom으로 덮어쓰게 된다.


두개 이상의 객체도 병합할 수 있다.

const user = {

name : 'Mike'

}

const info1 = {

age : 30,

}

const info2 = {

gender : 'male',

}

Object.assign(user, info1, info2) // info1과 info2가 user로 병합될수 있다.


Object.keys() 

객체 프로퍼티의 키를 배열로 반환한다.

const user = {

name : 'Mike',

age:30,

gender : 'male',

}

Object.keys(user); //["name", "age", "gender"] 배열이 반환된다.


Object.values()

객체 프로퍼티의 값들을 배열로 반환한다.

const user = {

name : 'Mike',

age:30,

gender:'male',

}

Object.values(user); // ["Mike", 30, "male"] 배열로 반환된다.


Object.entries()

키와 값을 모두 배열로 반환한다.

const user = {

name : 'Mike',

age:30,

gender:'male',

}

Object.entries(user); // [ ["name", "Mike"], ["age" , 30], ["gender", "male"] ] 출력된다. 배열안의 배열

 


Object.fromEntries()

키와 값을 쌍으로 묶은 배열들을 넣어주면, 반대로 객체로 만들어준다. 

const arr =

[

["name", "Mike"],

["age", 30],

["gender","male"]

];

Object.fromEntries(arr); // { name : 'Mike', age:30, gender:'male' , } 이 출력된다.


실습>

let n = "name";

let a = "age";

const user = {

[n] : "Mike",

[a] : 30,

[1+4] : 5,

};

console.log(user); // {5:5, name:"Mike", age: 30} 출력된다.


function makeObj(key, val) {

return{

[key] : val,

};

}

const obj = makeObj("성별", "male");

console.log(obj); // {성별 : "male"} 이 출력된다.


const user = {

name: "Mike", 

age:30,

};

const user2 = Object.assign({}, user);

user2.name="Tom";

console.log(user); // {name:"Mike", age:30} 이 출력된다.

console.log(user2); //{name:"Tom", age:30} 이 출력된다.


const user = {

name: "Mike", 

age:30,

};

const result = Object.keys(user);

console.log(result); // ["name", "age"] 출력된다.

const result = Object.values(user);

console.log(result); //["Mike", 30 ]이 출력된다.

const result = Object.entries(user);

console.log(result); // [ ["name", "Mike"], ["age", 30] ] 이 출력된다.


let arr = [

["mon", "월"],

["tue", "화"],

];

const result = Object.fromEntries(arr);

console.log(result); // {mon: "월", tue: "화"} 출력된다.


지금까지 객체 프로퍼티 키는 문자형으로 하였다.

const obj = {

1: '1입니다.',

false : '거짓'

}

Object.keys(obj); // ["1", "false"]

obj['1'] // "1입니다."

obj['false'] //"거짓"


심볼(Symbol)

유일한 식별자를 만들 때 사용한다.

 

const a = Symbol(); //new를 붙이지 않는다!!

const b = Symbol();

console.log(a) // Symbol()

console.log(b) // Symbol()

a === b; //false

a == b ; //false


심볼은 유일성이 보장된다.

const id = Symbol('id'); // Symbol은 만들 때 'id' 같이 설명을 적어 줄 수 있다. 디버깅 할 때 편하다. 설명문자열은 심볼생성에 영향을 미치지 않는다.

 


심볼을 객체의 키로 사용해보자.

const id = Symbol('id');

const user = {

name : 'Mike',

age : 30,

[id] : 'myid'

}

console.log(user) // {name:"Mike", age:30, Symbol(id):"myid"}

console.log(user[id]) // "myid" 출력된다.

Object.keys(user); // ["name", "age"] 출력된다. 키가 심볼형은 건너뛴다.

Object.values(user); // ["Mike", 30] , 키가 심볼형은 건너뛴다.

Object.entries(user); // [Array(2), Array(2)]  ,키가 심볼형은 건너뛴다.

for(let a in user){} // for.. in 을 써도 키가 심볼형은 건너뛴다.


Symbol은 어디에 사용되는가??

특정객체에 원본은 건드리지 않고 ,속성을 추가 할 수 있다.

const user = {

name : 'Mike',

age: 30

}

const id = Symbol('id');

user[id] = 'myid';

user.name = 'myname'; (X)

user.a_key_no_one_used = 'hahaha'; (X)


Symbol.for() : 전역 심볼

심볼은 이름이 같더라도 모두 다른 존재이다.

그런데 가끔, 전역변수처럼 이름이 같으면 같은 객체를 가르켜야 할 때가 있다.

이럴 때 사용할 수 있는 것이 Symbol.for() 이다.

  • 하나의 심볼만 보장받을 수 있음
  • 없으면 만들고, 있으면 가져오기 때문
  • Symbol 함수는 매번 다른 Symbol 값을 생성하지만,
  • Symbol.for 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유

const id1 = Symbol.for('id');

const id2 = Symbol.for('id');

id1 === id2; // true

Symbol.keyFor(id1) // "id" 출력, 이름을 얻고 싶으면 Symbol.keyFor()을 쓰면 된다.


description

전역심볼이 아닌 심볼은 description을 통해 이름을 알 수 있다.

const id = Symbol('id 입니다.');

id.descripttion; // "id 입니다. "


심볼 실습>

// ① 다른 개발자가 만들어 놓은 객체 

const user = {

name : "Mike", 

age : 30,

};

 

//①과 ② 이후, 내가  추가작업하면

//user.showName = function () {}; // 이렇게 해버리면 사용자가 접속하면 보는 메시지 for ..in문에서 함수가 출력된다.

// His showName is function () { } 이런 말도 안되는 메세지가 출력된다. 

const showName = Symbol('show name'); // 이렇게 심볼을 만들어서 넣으면 다른 개발자가 만들어놓은 코드에 영향을 미치지 않는다.

user[showName] = function () {

console.log(this.name);

};

user[showName]();  // Mike가 출력된다. 

 

// ② 사용자가 접속하면 보는 메시지, 다른 개발자가 만들어 놓은 거

for ( let key in user) {

console.log(`His ${key} is ${user[key]}.`); // His name is Mike . His age is 30 이 출력된다.

} // 


Number 숫자, Math 수학


toString()

10진수 →2진수/16진수

let num = 10;

num.toString(); // "10"

num.toString(2); // "1010" 괄호안에 숫자를 쓰면 진법으로 나타난다.

let num2 = 255;

num2.toString(16); // "ff" 


Math

자바스크립트에는 수학과 관련된 프로퍼티와 메서드를 가지고 있는 Math 라는 내장객체가 있다.

Math.PI; //3.141592653589793 원주율 출력


Math.ceil() : 올림

let num1 = 5.1 ;

let num2 = 5.7;

Math.ceil(num1); // 6

Math.ceil(num2); // 6 


Math.floor() : 내림

let num1 = 5.1;

let num2 = 5.7;

Math.floor(num1) // 5

Math.floor(num2) // 5


Math.round() : 반올림

let num1 = 5.1;

let num2 = 5.7;

Math.round(num1) // 5

Math.round(num2) // 6


소숫점 자릿수

요구사항 : 소수점 둘재자리 까지 표현(셋째 자리에서 반올림)

let userRate = 30.1234;

① userRate * 100 // 3012. 34가 된다. 100을 곱한다.

② Math.round(userRate * 100) // 3012가 된다. 반올림을 해준다.

③ Math.round(userRate * 100) / 100 // 30.12가 된다. 다시 100을 나눈다.


소숫점 자릿수 : toFixed() 

숫자를 인수로 받아, 그 숫자만큼 소숫점 이하를 반영한다.

let userRate = 30.1234;

userRate.toFixed(2); // "30.12" 가 출력된다. 

userRate.toFixed(0); // "30" 가 출력된다.

userRate.toFixed(6); // "30.123400" 가 출력된다. 0으로 채워준다.

단, 문자로 반환한다. Number()를 이용해 숫자로 변환한 후 작업한다.

Number(userRate.toFixed(2)); //30.12


isNaN()

NaN 인지 판단해준다.

let x = Number('x'); //NaN

x == NaN // false

x == NaN // false

NaN == NaN // false

isNaN(x) // true

isNaN(3) // false


parseInt()

문자열을 숫자로 바꿔준다.

Number()와 다른점은 문자와 혼용되어 있어도 동작을 한다.

let margin = '10px' ;

parseInt(margin); //10

Number(margin); //NaN

let redColor = 'f3' ;

parseInt(redColor); //NaN , 숫자로 시작하지 않으므로

parseInt()는 두번째 인수를 받아서 진수를 지정할 수 있다. 

let redColor = 'f3';

parseInt(redColor); //NaN

let redColor = 'f3' ;

parseInt(redColor, 16) ; // 243, 16진수로 바꾸었다.

parseInt('11' , 2 ) ; // 3 , 문자열 11을 숫자로 바꾸고 2진수에서 10진수로 바꾸었다.


parseFloat()

부동소수를 반환한다.

let padding = '18.5%';

parseInt(padding); // 18 , 정수만 반환

parseFloat(padding); // 18.5 , 부동소수점 같이 반환한다.


Math.random()

0~1 의 무작위 숫자를 생성한다.

1~100 사이 임의의 숫자를 뽑고 싶다면?

Math.floor(Math.random()*100)+1


Math.max() / Math.min()

Math.max(1,4,-1,5,10,9,5.54); //10, 괄호안 최대값

Math.min(1,4,-1,5,10,9,5.54); // -1 , 괄호안 최소값


Math.abs() : 절대값

절대값을 구해준다. absolute의 약자이다.

Math.abs(-1) // 1 출력


Math.pow(n, m) : 제곱

n의 m승 값, 거듭제곱, power의 약자이다.

Math.pow(2,10); //1024


Math.sqrt() : 제곱근

제곱근을 구해준다. square root의 약자이다.

Math.sqrt(16) // 4 출력

 

댓글()
구독