자바스크립트에 기초 없이 입문하면서
헷갈리거나, 새롭게 알게 된 사실들을 정리한 글입니다.
const room1 = "conference_room_a";
// room1은 변수를 가리키는 식별자,
// "conference_room_a" 는 문자열 리터럴인 동시에 room1의 값.
변수, 상수, 함수 이름을 부르는 용어.
이들 객체에는 두 가지 목적이 있다. 하나는 Number.INFINITY 같은 특별한 값을 저장하는 것이고, 다른 하나는 함수 형태로 기능을 제공하는 것이다.
toUpperCase
함수가 들어 있다. JS는 함수를 호출하는 즉시 임시 객체를 파괴한다.const s = "hello";
s.toUpperCase(); // "HELLO"
s.rating = 3; // 에러가 뜨지 않는다! (임시 String 객체 생성)
s.rating //undefined (임시 객체 파괴)
const multiline = "line1\n" +
"line2\n" +
"line3\n";
크게 상관은 없지만, IE버전 호환성, JSON 표기법에 따른 권장사항이다.
const arr = [1,2,3];
const arr2 =[1,2,3,];
const o = {one : 1,
two : 2,
three : 3};
const o = {one : 1,
two : 2,
three : 3,};
const halloween = new Date(2016, 9, 31); // 9는 10월이다
parseInt
나 parseFloat
은 기수(radix) 를 넘길 수 있으며, 변환 이전의 문자열이 엉망진창이어도 숫자만 인식해서 변환해준다.const a = parseInt("16 volts", 10); // 10진수 16
const b = parseInt("3a", 16); // 10진수 58
const c = parseFloat("15.5 kph"); // 10진수 15.5
const b = true;
const n = b ? 1 : 0;
const arr = [1, true, "hello"];
arr.toString(); // "1,true,hello"
const n = 0; // 거짓 같은 값
const b1 = !!n; // false
const b2 = Boolean(n); // false
if
문 안에서 블록 문과 블록 없는 문을 섞어 쓰는 건 좋지 않다.function rand(m,n) {
return m + Math.floor((n-m+1)*Math.random());
}
function randFace() {
return ["crown", "anchor", "heart", "spade", "club", "diamond"][rand(0,5)];
}
randFace(); // "anchor" or "heart" or ...
for
루프에는 다양한 패턴이 존재한다.for(let temp, i=0, j=1; j<30; temp = i, i = j, j = i + temp)
console.log(j);
// 무엇이 출력될까요?
for
루프의 다양한 패턴for(let temp, i=0, j=1; j < 30; temp = i, i = j, j = i+temp) {
console.log(j);
}
for(;;) console.log("I am forever!");
let s = '3';
let s = '3';
for(; s.length < 10; s = ' ' + s);
// s = " 3"
for(let x=0.2; x<3.0; x += 0.2) {
console.log(x);
}
for(; !player.isBroke;) {
console.log("Still playing!");
}
break
없는 case절은 동료에게 실수처럼 보일 수 있다.switch(totalBet) {
case 7:
totalBet = funds;
break;
case 13: funds = funds - 1; // totalBet이 13이면 case 11로 넘어가서 0으로 바꿈.
case 11 :
totalBet = 0;
break;
case 21:
totalBet = 21;
break;
}
break
가 없는case 13
에 대한 주석을 남겨 실수가 아님을 알린다.
switch
문의 default
와 break
는 optional 이다.하지만 웬만하면 쓰자.
switch
문의 간결한 표현법
switch(totalBet) {
case 7 : totalBet = funds; break;
case 11: totalBet = 0; break;
case 13: totalBet = 0; break;
case 21: totalBet = 21; break;
}
for
루프 밖에서 인덱스 번호 i
가 필요할 때는 다음과 같이 하면 된다.let i = 0;
for(; i < bigArrayOfNumbers.length; i++) {
if (isPrime(bigArrayofNumbers[i])) break;
}
if (i === bigArrayOfNumbers.length) console.log("No prime Numbers!");
else console.log(`First prime number found at position ${i}`)
const arr = [1,2,3,4,5,6,7,8,9,10];
const deleteEven = arr => {
for(let i = 0; i < arr.length; i++) {
if(arr[i] % 4 !== 0) arr.splice(i, 1);
}
console.log(arr);
}
deleteEven(arr); // 기댓값 : [4, 8] / 결과 : [2,4,6,8,10]
const foo = arr => {
for(let i = arr.length; i >= 0; i--) {
if(arr[i] % 4 !== 0) arr.splice(i, 1);
}
console.log(arr);
}
foo(arr); // 기댓값 : [4, 8] / 결과 : [4, 8]
const s = "5";
const y = 3 + +s; // 문자에 단항 플러스(+) 를 넣어 숫자로 변환
y === 8 // true
typeof null
의 반환값object
심볼은 항상 유일하다.
다른 어떤 심볼과도 일치하지 않는다.
이런 면에서 객체와 유사하다. 객체는 모두 유일하기 때문.
심볼을 생성하는 방법은 다음과 같다.
const RED = Symbol("The color of a sunset!");
const ORANGE = Symbol("The color of a sunset!");
RED === ORANGE // false
원시 타입과 달리 여러 값을 나타낼 수 있으며, 변할 수도 있다.
객체의 본질은 컨테이너이다.
컨테이너의 내용물은 시간이 지나면서 바뀔 수 있지만, 컨테이너가 바뀌는 건 아니다.
const obj = {};
객체의 콘텐츠는 프로퍼티 또는 멤버라고 부른다.
프로퍼티는 키
와과 값
으로 구성된다. 프로퍼티 이름은 반드시 문자열 또는 심볼이어야 한다. 값은 상관 X
프로퍼티 이름이 유효한 식별자가 아니라면 계산된 멤버 접근자 []
를 써야 한다.
obj["not an identifier"] = 3;
obj["not an identifier"]; // 3
delete
연산자를 사용한다.delete obj.color;
regex 또는 regexp로도 쓴다.
문자열에서 필요한 복잡한 검색과 교체 작업을 비교적 단순하게 만든다. (지금은 복잡해 보인다..)
const email = /\b[a-z]0-9._-]+@[a-z_-]+(?:\.[a-z]+)+\b/;
while
문과 달리 조건이 false
이더라도 최소 한 번은 실행 되는 반복문이다.
let dobi = "slave";
do{
console.log("dobi is free!");
}while(dobi !== slave);
// dobi is free!
while
문 내의 조건이 거짓임에도 불구하고 반복문이 한 번 실행되었다.
자바스크립트에서는 모든 데이터 타입을 참 같은 값과 거짓 같은 값으로 나눌 수 있다.
undefined
null
false
0
NaN
''
(빈 문자열)valueOf()
메서드를 호출했을 때 false
를 반환하는 객체도 참 같은 값에 속한다."false"
x
가 거짓 같은 값이면 x && y
는 y
의 값을 평가할 필요도 없이 false
이다.
이런 동작을 단축 평가 (short-circuit evaluation) 라고 한다.
const x = true;
let y = 0;
const result = x || y++;
console.log(y); // 0
x | y | x && y |
---|---|---|
거짓 같은 값 | 거짓 같은 값 | x (거짓 같은 값) |
거짓 같은 값 | 참 같은 값 | x (거짓 같은 값) |
참 같은 값 | 거짓 같은 값 | y (거짓 같은 값) |
참 같은 값 | 참 같은 값 | y (참 같은 값) |
x | y | x || y |
---|---|---|
거짓 같은 값 | 거짓 같은 값 | y (거짓 같은 값) |
거짓 같은 값 | 참 같은 값 | y (참 같은 값) |
참 같은 값 | 거짓 같은 값 | x (참 같은 값) |
참 같은 값 | 참 같은 값 | x (참 같은 값) |
const options = suppliedOptions || {name : "Default"};
// suppliedOptions 가 객체(참 같은 값)이면 options = suppliedOptions
// "" 가 null 혹은 undefined면 options = {name : "Default"}
조건 연산자는 if...else
문과 동등한 표현식이다.
const doIt = false;
const result = doIt ? "Did it!" : "Didn't do it.";
if(isPrime(n)) {
label = 'prime';
} else {
label = 'non-prime';
}
->
const label = isPrime(n) ? 'prime' : 'non-prime';
if-else
문이 매우 짧아진다.
ES6 에 도입된 기능으로, 객체나 배열내 원소들을 변수로 해체할 수 있다.
// 객체 선언
const obj = {b : 2, c : 3, d : 4};
// 해체 할당 ver.1
const {a, b, c} = obj;
a; // undefined
b; // 2
c; // 3
d; // ReferenceError
// 해체 할당 ver.2
let a,b,c;
({a,b,c} = obj);
객체를 해체할 때는 반드시 변수 이름과 객체의 프로퍼티 이름이 일치해야 한다.
// 배열 선언
const arr= [1,2,3];
const arr2 = [1,2,3,4,5];
// 해체 할당 ver.1
let [x,y] = arr;
x; // 1
y; // 2
z; // ReferenceError
// 해체 할당 ver.2
let [x,y, ...rest] = arr2;
x; // 1
y; // 2
rest; // [3, 4, 5]
쉼표 연산자는 표현식을 결합하여 두 표현식을 평가한 후,
두 번째 표현식의 결과를 반환한다.
let x = 0, y = 10, z;
z = (x++, y++); // 10
쉼표 연산자는 우선순위가 가장 낮아 괄호를 사용했다.
피연산자를 평가한 후 undefined
를 반환한다.
<a href="javascript:void 0">Do nothing.<a>
브라우저에서 다른 페이지로 이동하는 일을 막을 수 있다.
function getSentence({ subject, verb, object}) {
return `${subject} ${verb} ${object}`;
}
const o = {
subject = "I",
verb = "love",
object = "JavaScript",
};
getSentence(o); // I love JavaScript
function getSentence([subject, verb, object]) {
return `${subject} ${verb} ${object}`;
}
const o = ["I", "love", "JavaScript"];
getSentence(o); // I love JavaScript
function addPrefix(prefix, ...words) {
const prefixedWords = [];
for(let i = 0; i < words.length; i++) {
prefixedWords[i] = prefix + words[i];
}
return prefixedWords;
}
addPrefix("con", "verse", "vex"); // ["converse", "convex" ]
function f(a, b = "default", c = 3) {
return `${a} - ${b} - ${c}`;
}
f(5, 6, 7); // 5-6-7
f(5, 6); // 5-6-3
f(5); // 5 - `default` - 3
f(); // undefined - `default - 3
함수 바디 안에는 특별한 읽기 전용 값인 this
가 있다.
메서드를 호출하면 this
는 호출한 메서드를 소유하는 객체가 된다.
this
는 함수를 어떻게 선언했느냐가 아니라 어떻게 호출했느냐에 따라 달라진다.
const o = {
name: "Wallace";
speack() { return `My name is ${this.name!}`},
}
o.speak(); // 'My name is Wallace!'
this
가o
에 묶인 이유는speak
가o
의 프로퍼티여서가 아니라,
o
에서speak
를 호출했기 때문이다.클래스의 인스턴스 객체의
this
를 생각해보면 쉽다.
this
사용하기함수 안에서 this
가 들어있는 함수를 호출하는 경우, 내부 함수에서의 this
는 엉뚱한 객체를 가리키는 경우가 많다.
const o = {
name : "Julie",
greetBackwards: function() {
function getReverseName() {
let nameBackwards = '';
for(let i = this.name.length-1; i >=0; i--) {
nameBackwards += this.name[i];
}
return nameBackwards;
}
return `${getReverseName()} si eman ym ,olleH`;
}
};
o.greetBackwards();
// Cannot read of property 'length' of undefined
getReverseName() 은 객체 o 안에 있음에도 this 가 undefined로 할당된다.
위 코드는 함수 안의 함수로 들어가기 전에, this
를 변수에 저장해주면 해결할 수 있다.
const o = {
name : "Julie",
greetBackwards: function() {
const self = this; // this 를 self 라는 변수에 할당
function getReverseName() {
let nameBackwards = '';
for(let i = self.name.length-1; i >=0; i--) {
nameBackwards += self.name[i];
}
return nameBackwards;
}
return `${getReverseName()} si eman ym ,olleH`;
}
};
o.greetBackwards();
// `eiluj si eman ym ,olleH`
화살표 표기법은 function
이라는 단어와 중괄호 숫자를 줄이기 위해 고안된 단축 문법이다.
function
을 생략해도 괜찮다.return
도 생략할 수 있다.const f1 = function() {return "hello!";}
const f1 = () => "hello!";
const f2 = function(name) {return `bye! ${name}`;}
const f2 = (name) => `bye! ${name}`;
const f3 = function(a, b) {return a + b};
const f3 = (a,b) => a + b;
화살표 함수는 위 예제와 달리 익명 함수를 만들어 다른 곳에 전달할 때 매우 유용하다.
화살표 함수에서 this
는 정적으로(lexically) 묶인다.
this
를 사용할 수 있다.const o = {
name : "Julie",
greetBackwards: function() {
const getReverseName = () => { // 내부 함수가 화살표 함수를 쓰면
// this 사용 가능
let nameBackwards = '';
for(let i = this.name.length-1; i >=0; i--) {
nameBackwards += this.name[i];
}
return nameBackwards;
}
return `${getReverseName()} si eman ym ,olleH`;
}
};
o.greetBackwards();
const arr = [2, 3, -5, 15, 7];
Math.min.apply(null, arr); // -5
Math.max.apply(null, arr); // 15
Math.min 과 Math.max 는
this
와 관계없이 동작한다.
정적 스코프는 어떤 변수가 함수 스코프 안에 있는지 함수를 정의할 때 알 수 있다는 뜻이다.
const x = 3;
function f() {
console.log(x);
console.log(y);
}
{
const y = 5;
f();
}
// 3
// undefined
Immediately Invoked Function Expression 의 준말이며,
선언 즉시 실행되는 함수이다.
count message = (function() {
const secret = "I'm a Secret.";
return `The secret is ${secret.length} characters long.`;
})();
console.log(message);
// `The secret is 6 characters long.`
변수
secret
은 스코프 안에서 보호되며, 외부에서 접근할 수 없다.
함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 것.
스코프를 함수 주변으로 좁히는(closing) 하는 것이라고 생각하면 편하다.
let globalFunc; // 정의되지 않은 전역 함수
{
let blockVar = 'a'; // 블록 스코프에 있는 변수
globalFunc = function() {
console.log(blockVar);
}
}
globalFunc(); // "a"
let f;
{
let o = { note: "Safe"};
f = function() {
return o;
}
}
let oRef = f();
oRef.note = "Not so safe after all!";
let
으로 선언하는 변수는 선언하기 전까지 존재하지 않는다.
스코프 안에서 변수의 사각지대는 변수가 선언되기 전의 코드이다.
암시적 전역 변수를 허용하지 않는 모드.
class Person{
constructor(name) {
this.name = name;
this.id = Person.nextId++;
}
}
Person.nextId = 0;
const jamie = new Person("Jamie"), // id = 0
juliet = new Person("Juliet"), // id = 1
peter = new Person("Peter"), // id = 2
jay = new Person("Jay"); // id = 3
const arr = [1,2,3,4,5];
const evenOfEven = arr.filter(x => x % 2 === 0)
.map(x => x * 2);
console.log(evenOfEven); //[4, 8]
const words = ["Beachball", "Rodeo", "Angel", "Aardvark", "Xylophone", "November", "Chocolate", "Papaya", "Uniform", "Joker", "Clover", "Bali"];
const alphabetical = words.reduce((acc, x) => {
if(!acc[x[0]]) {
acc[x[0]] = [];
}
acc[x[0]].push(x);
return acc;
}, {});
콜백을 받는 메서드들은 옵션으로 콜백을 호출할 때 this
로 사용할 값을 받을 수 있다. 이 매개변수를 활용하면 콜백 함수를 메서드처럼 사용할 수 있다.