변수를 선언할 때, var/let 그리고 const가 있습니다. var와 let은 대입연산자로 들어온 내용을 고정이 아니라 가변적으로 변경할 수 있다는 특징이 있지만. var가 let에 비해서 더 다채로운 특성을 가지고 있음을 사용자는 유의해야 한다.
let name = 'Mike';
let name = 'Edwin'; // error
var name = 'Mike';
var name = 'Edwin';
한번 선언한 변수에 대해서 let은 다시 선언할 수 없고, 해당 내용의 대입된 데이터를 변경만 가능하지만, var의 경우에는 한 번 선언된 변수라도 다시 선언할 수 있다는 측면이 있다. 그러나 이렇게 되면, 먼저 선언된 var name = 'Mike'가 인식되지 않는다는 것은 기억해야 한다. 뿐만 아니다.
console.log(name);
var name = 'Edwin';
Javascript는 위에서부터 순차적으로 문서를 읽어나가기에, 위와 같이 입력하면 읽어지지 않을 것이라고 배웠지만, var는 다르다. 이는 함수와 같이 호이스팅(hoisting)의 결과로 문서가 읽어질 때, 먼저 읽어지고 그리고 실행을 위한 순차적인 실행이 이뤄지기 때문이다. 그러나 let과 const는 작동방식에 있어서 이러한 순차적인 실행에 해당되는 TDZ로 인하여, 호이스팅이 되지만 작동되지 못하는 성격을 가진다.
let age = 30;
function hey() {
console.log(age);
let age = 20;}
hey();
두번째 사용된 지역변수 (age=20)은 전역변수(age=30)과 다르기 때문에 선언할 수 있지만, TDZ(Temporal Dead Zone)에 의하여 console.log(age)의 실행을 방해한다. 그 결과 오류가 발생된다.
변수는 1) 선언단계 2) 초기화 단계 3) 할당단계를 걸쳐서 형성된다.
var name;
name = 'Edwin';
console.log(name);
let name2;
name2 = 'Edwin';
console.log(name2);
const name3;
name3 = 'Edwin';
console.log(nam3);
그러기에 할당까지 한번에 이뤄지는 const를 위와 같은 코드로 작성하면, 변수의 초기값을 가지고 있지 않다는 오류를 콘솔에서 확인할 것이다.
함수 스코프란 조건문 같이 구문 안에서 지역변수적으로 선언한 변수라고 할지라도, 해당 구문 밖에서도 사용이 가능하다는 것이다. 반면에, 블록 스코프는 이렇게 사용할 수가 없다. 그러나 함수 스코프라고 할지라도, 예외적으로 함수구문(function()) 내에서는 지역변수로만 선언된다.
const age 30;
if (age>19) {
var txt = 'Edwin';
}
console.log(txt);
위와 같은 코드를 실행하면, 지역변수적으로 선언한 var변수가 콘솔에 기록되는 것을 볼 수 있다. 이는 var가 가진 스코프의 특성 때문에 가능한 일이다. 그러나, let과 const는 해당 구문 밖에서 사용할 수 없다. 이는 let과 const가 가진 스코프의 한계 때문에, 지역변수로만 사용할 수 있기 때문이다. 현대에는 많은 경우 예측가능한 let과 const로 변수를 선언하는 것이 선호되고 있다.
let user = {
name : "Edwin",
age : 30,
}
위와 같은 형태를 기초강좌에서 "객체(Odject)"라고 공부하였다. 그리고 이렇게 객체를 선언하는 것을 객체 리터럴이라고 부른다. 그런데 코드를 작성하다보면, 회원정보와 같은 객체들이 무수히 많이 반복될 것을 예상하게 된다. 이러한 반복적인 일들에 대해서 효과적으로 대응할 수 있는 방법이 있을까? 이러한 질문을 기반으로 하여 이번시간에는 "생성자 함수"를 다뤄보자. 생성자 함수를 실행할 때 암묵적인 규칙이 있는데, 함수명을 대문자로 선언한다는 점이다.
function User(name, age) {
this.name = name,
this.age = age,
}
let user1 = new User ("Edwin", 30);
let user2 = new User ("Elis", 30);
let user3 = new User ("jun", 30);
함수의 parameter로 name과 age를 선언하였고, let을 통하여 새로운 객체(new 객체명)를 생성할 때, argument로 name과 age의 값을 선언해주면, 손쉽게 객체를 3개 형성할 수 있게 된다. 이렇게 객체를 형성함에 있어서 method(함수)도 형성할 수 있음을 공부했는데, 적용해보자.
function User(name, age) {
this.name = name,
this.age = age,
this.sayName = function() {
console.log(this.name);
console.log(`${this.name}님 저장되었습니다.`)
},
}
let user5 = new User ("Edwin", 30);
여기서 배운 것을 적용해 보고 가자. 함수선언문에서 함수는 화살표형으로도 작성이 가능한 것을 공부했었다. 해당 내용으로 아래와 같이 기록해도 동일한 결과를 얻을 수 있다.
function User(name, age) {
this.name = name,
this.age = age,
this.sayName = ()=>{
console.log(this.name);
console.log(`${this.name}님 저장되었습니다.`)
},
}
let user6 = new User ("Edwin", 30);
그런데 = () 이 부분, 매개변수가 없기 때문에 생략해도 될까? 안된다. 왜냐면, 규칙이기 때문이다. 또한 객체의 property를 이용함에 있어서, 문구로 작성할 때 백틱을 통해서 ${}로 가져다 사용할 property의 key명을 입력하면 해당 property의 value를 이용할 수 있다.
본격 코린이 2개월차에 만난 처음보는 javascript 용어가 등장했다. computed property(계산된 프로퍼티)이다. 개념에 대해서 이해해보자.
let a = 'age';
const user = {
name = 'Mike',
[a] :30,
[1+4] : 5,
}
개념상으로 선언된 변수a를 변수const에서 property의 key의 이름으로 사용할 때, 위와 같이 입력하는 것을 computed property라고 한다. 이러한 computed property에는 [1+4]와 같이 연산이 가능한 내용을 기록해도 된다. 정보출력의 관점에서 해당 property의 key명은 5가 될 것이다.
let user = {
name : "Edwin",
age : 30,
}
user라는 객체가 있을 때, 해당 객체를 복제하는 방법에는 어떠한 방법이 있을까?
let user = {
name : "Edwin",
age : 30,
}
const user2 = user;
틀렸다. 이렇게 선언하면, user에 접근할 수 있는 변수명이 2개가 생성된 것일 뿐, 객체가 복제된 것이 아니다.
user2.name = 'Park'
그러기에, 변수user2의 이름의 값을 변경하면, 변수user의 이름도 변경되는 것을 확인할 수 있다. 그렇다면 어떻게 해야 동일한 복제가 가능할까?
let user = {
name : "Edwin",
age : 30,
}
const newName = Object.assign({}, user)
newName.name = "Park"
document.write(user.name + "<br>")
document.write(newName.name)
Object.assign을 선언해야 복제가 가능하다. 이때 ({})는 초기값을 설정하는 것인데 Object.assign({}, user) 후속하는 코드에서 볼 수 있듯, 초기값으로는 선언된 user객체로 하겠다는 코드가 기록되어 user객체를 newUser가 복제한 것을 확인할 수 있다. 그러기에 마지막 코드에서 이름을 변경하더라도 user객체를 newUser객체의 이름이 모두 변경되는 것이 아니라, 다른 이름으로 설정되는 것을 확인할 수 있다. 약간의 변경이 있는데, 콘솔에 기록하는 것 대신 화면에 기록(document.write)하도록 코드를 살짝 변경하였다. 이는 아래의 사진이 codepen에서 실행한 내용인데, 콘솔이 불편하기 때문에, 변경하였다.
기록된 내용에서 볼 수 있듯이 화면에 기록(document.write)할 때 HTML언어로 줄바꿈 태그를 실행하여 화면에 user.name과 newName.name이 출력되도록 기록하였다.
let user = {
name : "Edwin",
age : 30,
}
const newName = Object.assign({gender : 'M'}, user)
document.write(newName.name + ", " + newName.age)
만약 복제할 때, property의 값을 할당하면, 해당property가 존재하면 덮어쓰기가, 해당property가 존재하지 않는다면, 새로운 property를 생성한다. 그런데 코드펜에서는 해당property가 존재하면 덮어쓰기가 실행되지 않아서, 이미지에 적용된 결과를 포함하지는 않았다.
객체를 복제하는 것에 있어서 다음과 같은 재미난 효과도 가능하다.
let user = {
name : "Edwin"
}
let info1 = {
age : 30
}
let info2 = {
gender : "M"
}
const newName = Object.assign(user, info1, info2)
document.write(newName.name + ", " + newName.age + ", " + newName.gender)
각각으로 나누어진 객체가 존재할 때, 해당 내용을 하나의 객체로 복제하여 생성하는 것도 가능하다.
let user = {
name : "Edwin",
age : 30,
}
document.write(Object.keys(user) + "<br>");
document.write(Object.values(user));
Object.keys()는 객체의 key의 값을 배열 데이터로 반환해주고, Object.values()는 객체의 value의 값을 배열 데이터로 반환해준다. 이렇게 실행한 결과의 이미지를 살펴보자.
Object의 key와 value의 값 모두를 배열로 변환한다. 순서는 property 순으로 하나씩 변환한다. 이를 콘솔에 기록해보면 [[property1Key, property1value], [property2Key, property2value]] 배열 안에 property별로 배열을 형성하여 담겨있는 것을 확인할 수 있다.
fromEntries은 배열데이터를 객체데이터로 변환시켜준다.
객체 메소드를 학습하며 보았던 property의 key는 문자형 또는 숫자형, boolean형으로 기록할 수 있었다. property의 key에는 하나 더 사용할 수 있는 무엇이 있는데, 바로 심볼(symbol)이다.
위의 코드를 해석해보자. 03번에서 학습한 computed property로 [id]와 [id2]에 같은 'myid'라는 값을 할당하였다. 콘솔에 찍어보면 동일한 내용이 기록되는 것을 볼 수 있다. 그런데, 변수id(Symbol)과 변수id2(Symbol)이 같은 것일까? 아쉽지만 담겨있는 내용이 동일할 뿐 두 개는 다른 것이다. 그래서 결과로 false가 산출된다. Symbol은 아래와 같이 생성해주는데, 마치 HTMl에서 속성 id값을 주었던 것을 기억하자. 문서 내에서 해당Symbol은 한 번만 사용이 가능하다. 해당 내용에 value의 값이 동일하더라도, 그것을 가지고 있는 key가 다르기에 다른 존재인 것이다. Symbol('설명적 내용을 담을 수 있다.')
const a = Symbol();
const id = Symbol('id');
const id2 = Symbol('id');
const user = {
name : "Edwin",
age : 30,
[id] :'myid',
[id2] : 'myid'
};
console.log(user);
console.log(user.[id]);
console.log(user.[id2]);
console.log(id == id2);
위와 같이, 내용을 입력하고 console.log(user)에 찍어보면, 에메랄드박스에서 볼 수 있는 것과 같이 Symbol()의 데이터는 출력되지 않는다. console.log(user.[id])와 같이 직접 입력하여 확인할 수는 있더라도, console.log(user)에 출력되지도, 객체 배열반환을 하더라도 실행되지 않는다.
그렇다면 Symbol()은 언제 사용할까? 예를들어 다른 사람이 작성한 원본 객체를 가져올 경우, 내가 추가한 property를 감추어 사용할 때이다. Symbol()로 설정하지 않고 객체를 추가하면, 데이터가 반영되는 어딘가에 오류를 발생시킬(원하지 않는 값을 드러낼) 수도 있게 되기 때문이다.
이렇듯 Symbol()은 고유한 값을 가진 존재입니다. 그런데 이러한 고유한 값을 가진 심볼은 마치 함수와 같이 여러곳에서 재사용할 수도 있다. 즉 전역심볼을 만들어줌으로 이는 가능한데, Symbol.for()로 만들어주면 가능하다.
정리하면 Symbol()은 매번 각개의 서로 다른 Symbol을 생성하지만, Symbol.for() 메소드는 하나를 생성한 뒤 키를 통해 같은 Symbol을 공유할 수 있다는 것이다.
위의 이미지에서 볼 수 있듯, Symbol()은 그 내용이 동일하더라도 false이지만, Symbol.for()은 true인 것을 볼 수 있다. 이렇게 생성한 Symbol.for() 해당 문서 어디에서든지 사용할 수 있다.
const id3 = Symbol.for('id');
const id4 = Symbol.for('id');
console.log(Symbol.keyFor(id3));
Symbol.keyFor(변수명)을 이용하면, Symbol.for의 value를 알아낼 수 있다. 그러나 Symbol.keyFor(변수명)은 전역심볼인 Symbol.for()는 알아낼 수 있지만, Symbol()의 value는 알아낼 수 없다. 대신 변수명.description을 통해서 접근할 수 있다.
const id = Symbol('id');
const user = {
name : "Edwin",
age : 30,
[id] :'myid',
};
위와 같이 기록되어 있다면,
보여준다.
일상에서 사용하는 숫자는 위와 같은 10진수의 체계이다. 그러나 컴퓨터와 소통하다보면 색상표현을 위해서 2진수 또는 16진수가 요구될 때가 있다.
let num = 100;
console.log(num.toString());
console.log(num.toString(2));
console.log(num.toString(16));
toString()는 10진수, toString(2)는 2진수, toString(16)는 16진수에 해당되는 변수 num의 값을 문자열(String)로 보여줄 것이다. 각각 "100", "1100100", "64"이 콘솔에 기록되는 것을 확인해 볼 수 있다.
그런데, 3.12312315 라는 숫자가 있다고 하자. 소수점 두번째자리까지 표현하고 싶다 어떻게 하면 될까?
let num1 = 3.12312315;
console.log(Math.ceil(num1*100)/100);
console.log(Math.floor(num1*100)/100);
console.log(Math.round(num1*100)/100);
이렇게 함으로 원하는 소수점의 위치에서 올림, 내림, 반올림 등이 가능하다. 더 간단한 방법이 있습니다.
let num1 = 3.12312315;
console.log(num1.toFixed(0));
console.log(num1.toFixed(2));
console.log(num1.toFixed(10));
작동 방식은 이와 같다. toFixed는 숫자의 소수점을 argument로 가져온다. 그리고 설정한 소수점의 자리수를 기점으로 숫자를 반올림한 값을 돌려준다. 만약 toFixed(0)로 기록했다면 정수만 기록해 준다는 것이고, toFixed(10)과 같이 본래 가지고 있는 변수의 소수점을 넘어서면 0으로 해당 내용을 채워준다는 것이다. !! 그런데 toFixed()는 숫자형자료를 문자형으로 형변환을 실행하니 이점에 유의하자. 숫자형으로 사용하고 싶다면, 다시 이를 숫자형으로 형변환 시켜주면 된다.
let num1 = 3.12312315;
let num2 = num1.toFixed(2);
let num3 = Number(num1.toFixed(2));
console.log(typeof num2)
console.log(typeof num3)
코딩앙마 중급강좌5 2분40부분부터 나오는 isNaN에 대한 부분이 무슨 말인지 모르겠다. 구글링을 통해서 살펴보자. isNaN()은 매개변수(parameter)가 숫자인지 검사하는 Javascript 내장 메소드이다. 아하! Not a Number.
let a = Number('x');
let b = 3
console.log(isNaN(a)); // console : true
console.log(isNaN(b)); // console : false
즉 isNaN()은 데이터타입이 숫자인지 아닌지를 판별하는 메소드이다.
let a = '123abc';
console.log(parseInt(a)); // console : 123
console.log(Number(a)); // console : NaN
parseInt(parameter)에 숫자가 있으면 숫자를 숫자형으로 형변환을 시켜준다. 그런데 유의할 점이 있다. parseInt()는 parameter가 숫자로 시작해야만 실행된다는 점이다. 만약 parameter가 문자로 시작하면, 해당값은 NaN이된다. 반면에 Number(a)와 같이 실행하면, 해당 내용이 숫자만 가지고 있는 값이 아니기에 NaN이 출력된다.
그런데 16진수로 표현된 숫자들이 존재할 때, 이를 숫자로 형변환 할 수 있을까?
IBM 10진수, 16진수, 2진수 변환표
let redColor = 'f3';
console.log(parseInt(redColor, 16));
아스키코드(ASCII)에서 확인 할 수 있는 것처럼 16진수(F3)이 형변환되어, 10진수 243을 콘솔에 출력하는 것을 볼 수 있다. 이를 변수(redColorDecimalNumber)로 담고 typeof redColorDecimalNumber를 물어보자. 숫자형인 것을 확인해 볼 수 있다. 이런 식으로 parseInt(parameter, 2)를 실행하면 2진수로 기록된 숫자를 10진수로 변환할 수 있다.
let binaryNumber = "11110011"
console.log(parseInt(binaryNumber, 2));
결과는 "11110011"(2진수)에 대한 10진수 값인 243이 산출되는 것을 볼 수 있다.
이미지를 통해서 확인해볼 수 있는 것처럼, 0~1 사이에 해당하는 무작위 숫자를 생선한다. 만약 1~100 사이에 존재하는 숫자를 임의로 추출하고 싶다면 어떻게 해야될까?
console.log(Math.floor(Math.random()*100)+1);
console.log(Math.ceil(Math.random()*100));
console.log(Math.ceil(0.00000001*100)); // 결과 1
console.log(Math.ceil(0.99999999*100)); // 결과 100
그런데 내림보다 간단하게 올림을 하면 되지 않을까?
console.log(Math.max(1, 3, 345, -112, -1, 0, 3234));
console.log(Math.min(1, 3, 345, -112, -1, 0, 3234));
결과는 Max는 3234가, Min은 -112이 산출될 것이다. 내용 안에 최대값과 최소값이 어디에 있던지, 해당 메소드는 일치하는 값을 산출하여 보여준다. 이럴 때 보면, 컴퓨터의 발전은 대단한 것 같다. 사람이 할 수 없는 일을 순식간에 처리하니 말이다.
여기서 번외로 잡담하나만 하고자 한다. 필자의 전공을 언급하며 컴퓨터 이야기를 하고자 한다. 신학세계에 3개의 거대한 프로그램이 있다. 필자는 그 중에 2개를 정품으로 가지고 있는데, 하나는 바이블웍스10이고, 하나는 로고스10이다.
GUI환경으로 된 프로그램에서 헬라어 "예수, Ἰησοῦς"에 대한 모든 형태(주격, 소유격, 여격, 목적격)를 찾으라 명령했다. 단 0.13초만에, 867구절에서 4개형태에 해당하는 "예수, Ἰησοῦς"에 대한 모든 형태 906개를(신약성경에는 예수에 대한 언급이 총 906번 된다.) 찾았다. 이를 찾는데 단 0.13초밖에 들지 않다.
수포자에게 수학용어는... 힘들다. 절대값이란(위키백과) 수학에서 수직선 위에서 원점으로부터 어떤 수를 나타내는 점까지의 거리라고 한다.
Math.abs(parameter) // 절대값
Math.pow(n, m) // n의 제곱(m)
Math.sqrt(parameter) // 제곱근
이러한 메소드들은 쇼핑몰이나, 통계를 산출할 때 필수적인 메소드들이기에 모르면 취업 못한다.
"큰따옴표", '작은따옴표', 그리고 백틱, "큰"/'작은'과은 둘 가운데 어떤 것을 사용해도 무방하다. 그러나 만약 문자열 가운데 I'm과 같이 기록된 구분이 있다면 "큰따옴표"로 감싸주어야 할 것이다. "큰"/'작은'과 백틱의 가장 큰 차이는 줄바꿈의 유무에 있다. 백틱에서는 줄바꿈의 경우 줄을 바꿔주면 된다. 그러나 "큰"/'작은'과에서는 \n을 넣어줄 수 있겠지만, 에러가 발생되기에, 결국 "큰"/'작은'에서는 줄바꿈을 할 수 없다.
let txt = "coding is funny";
txt.length; // 문자열의 길이를 구할 때
txt[2]; // 특정한 위치로 접근할 수 있지만, 배열과 다르게 한글자만 변경할 수 없다.
txt.toUpperCase(); // 영어만 해당되며, 모든 문자 대문자로 전환
txt.toLowerCase(); // 영어만 해당되며, 모든 문자 소문자로 전환
txt.indexOf(is); // 해당 argument가 변수의 몇번째 위치하는지 확인가능 찾을 수 없다면 -1
txt.includes(is); // 해당 argument가 변수에 있는지 유무(true, false)만 판별해 준다.
indexOf()의 경우, 찾고자 하는 단어가 첫자리에 있으면 출력되지 못한다. 왜냐하면 coding의 인텍스는 첫알파벳인 c가 0번지에 있기 때문에, 조건문에서 0은 false로 반영되어, 실행되지 않는 것이다. 이를 위해서 처리해줄 부분이 있다. 이러한 특성 때문에 조건문을 선언할 때 -1보다 큰 값인지 물어보아야 실행된다.
아래의 예제로 한 번 더 살펴보자.
txt('나는 사이다가 좋다')에는 "콜라"가 없다. 찾을 수 없기 때문에 해당값은 -1이 된다. 조건문을 설정해 주지 않으면, 그냥 다 통과가 될 것이기 때문에, 없는값보다 크다는 조건 즉 >-1 을 설정해주면, 해당 parameter에 금지어가 존재하지 않는다면, else문으로 넘어갈 것이다.
그러나 txt('나는 콜라가 좋다')에서 "콜라"는 그 위치가 문장 내에서 4번째 위치함으로, if문에서 포착된다. 그 결과 "금지어가 있습니다"가 출력되는 것이다.
그런데 이를 includes를 통해 사용하면 >-1과 같은 조건을 달아주지 않아도 가능하다. inCludes는 포함 여부만 묻기 때문이다. 포함하고 있으면 true, 포함하지 않으면 false가 된다.
function txt(str) {
if(str.includes('콜라')) {
console.log('금지어가 있습니다');
} else {
console.log('통과');
}
}
txt('나는 사이다가 좋다.')
txt('나는 사이다가 좋다.')
txt('나는 콜라가 좋다.')
let txt = "codingisfunny";
txt.slice(n,m); // n번지부터 m번지 전번지까지 내용을 가져와라.
txt.slice(n); // n번지부터 끝까지 내용을 가져와라.
txt.slice(n,-m); // n번지부터 뒤에서 -m번지까지 내용을 가져와라.
txt.substring(n, m) // (2,5) (5,2) 를 기록하더라도 2번지에서 5번지 전번지까지, 0(음수)는 허용하지 않는다.
txt.substr(n, m) // n번지에서 시작해서, m개를 가져와라
let txt2 = " sdfsdfsd "
txt2.trim() // 문자열 앞뒤에 있는 공백을 제거해라
txt2.repeat(n) // 문자열 내에 단어를 n번 반복하라는 말입니다.
"a".codePointAt(0)
String.fromCodePoint(97)
"a"의 십진표 표기를 보고 싶다면 codePointAt(0)을 통해서, 십진표 97의 내용을 보고 싶다면 String.fromCodePoint(숫자)를 입력함으로 설정된 값을 볼 수 있다.
author. EDWIN
date. 23/01/27