Javascript 치트시트

장펄씨·2023년 7월 3일
0

Front-End

목록 보기
2/2
post-thumbnail

프론트엔드 개발자로 진로를 정했기 때문에, 자바스크립트와 친해져야한다. C와 파이썬에 익숙해졌지만... 이제는 자바스크립트로 모든것을 해야한다! 이를 위해 자바스크립트로 뇌를 전환하는 동안 필요한 정보들을 정리해두려 한다.

자바스크립트란?

나무위키의 정의에 따르면: "Ecma International의 프로토타입 기반의 프로그래밍 언어로, 스크립트 언어에 해당된다". 프로토타입 기반 언어는 원형 객체를 복제하여 새로운 객체를 하는 생성하는 언어를 말하는데, 이에 대해서는 밑에서 자세히 얘기해보려 한다.

명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍 언어이며, 기본적으로 파이썬과 비슷한 인터프리터 언어고, 컴파일러 언어와는 다르게 한줄한줄 바로 실행된다. 하지만 성능상의 최적화를 위해 컴파일러 언어의 특성도 가지고 있고, 일부 브라우저에서는 소스코드를 컴파일해서 실행한다.

Tokenizer -> Parser -> AST -> 인터프리터를 거쳐 바이트 코드로 변환된다.

프로토타입 기반 언어?

ES2015부터 자바스크립트도 class 키워드 사용이 가능하지만, 이는 "문법적인 양념"일 뿐이며, 자바스크립트의 본질은 여전히 프로토타입 기반의 언어다. 모든 자바스크립트 객체들은 속성을 저장하는 공간과 프로토타입 객체에 대한 링크를 가지고, 객체의 속성에 접근할때 그 객체의 자체 속성 뿐만 아니라, 객체의 프로토타입, 프로토타입의 프로토타입.. 등 순으로 그 체인이 끝날때까지 속성이 탐색된다. mdn의 예시를 빌려 보면,

// o라는 객체가 있고, 속성 'a' 와 'b'를 갖고 있다고 하자.
let f = function () {
    this.a = 1;
    this.b = 2;
}
let o = new f(); // {a: 1, b: 2}

// f 함수의 prototype 속성 값들을 추가 하자.
f.prototype.b = 3;
f.prototype.c = 4;

// f.prototype = {b: 3, c: 4}; 라고 하지 마라, 해당 코드는 prototype chain 을 망가뜨린다.
// o.[[Prototype]]은 속성 'b'와 'c'를 가지고 있다.
// o.[[Prototype]].[[Prototype]] 은 Object.prototype 이다.
// 마지막으로 o.[[Prototype]].[[Prototype]].[[Prototype]]은 null이다.
// null은 프로토타입의 종단을 말하며 정의에 의해서 추가 [[Prototype]]은 없다.
// {a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null

console.log(o.a); // 1
// o는 'a'라는 속성을 가지는가? 그렇다. 속성의 값은 1이다.

console.log(o.b); // 2
// o는 'b'라는 속성을 가지는가? 그렇다. 속성의 값은 2이다.
// 프로토타입 역시 'b'라는 속성을 가지지만 이 값은 쓰이지 않는다. 이것을 "속성의 가려짐(property shadowing)" 이라고 부른다.

console.log(o.c); // 4
// o는 'c'라는 속성을 가지는가? 아니다. 프로토타입을 확인해보자.
// o.[[Prototype]]은 'c'라는 속성을 가지는가? 가지고 값은 4이다.

console.log(o.d); // undefined
// o는 'd'라는 속성을 가지는가? 아니다. 프로토타입을 확인해보자.
// o.[[Prototype]]은 'd'라는 속성을 가지는가? 아니다. 다시 프로토타입을 확인해보자.
// o.[[Prototype]].[[Prototype]]은 null이다. 찾는 것을 그만두자.
// 속성이 발견되지 않았기 때문에 undefined를 반환한다.

위처럼 본인 스스로가 속성을 가지고 있지 않더라도 상위 프로토타입으로 계속 올라가 속성을 찾는다.

변수 선언

var, let과 const

ES6 (ES2015) 이후 원래 있던 var외에 let과 const가 추가되었다.
공통적으로 변수 이름의 첫 글자는 무조건 문자, _ 혹은 $로 시작해야 하며 숫자나 다른 특수기호들은 안된다.

var 1value;
var .key;
var #obj;

모두 에러가 나는 케이스들이다.

var

함수 외부에서 var 변수가 선언 되면 그 범위는 전역이 된다. 하지만 함수 내에서 선언된다면 그 범위는 함수 범위 내로 한정되어 함수 스코프만을 가진다.

var halo = "hihi";	//전역 범위

function someFunction() {
	var hihi = "halo"; 	//함수 범위
}

console.log(hihi);	//error!

위와 같은 형식이다. var변수는 재선언, 업데이트가 모두 가능하다. var는 최소한으로 사용하는게 좋으며, for 루프 카운터 변수 선언 시 var을 사용하지 않는것이 좋다.

let

var은 재정의가 자유롭기 때문에, 의도치 않은 버그가 발생하는 경우들이 종종 있다. let이 이 문제를 해결하는데, let으로 선언된 변수는 해당 {} 블록 안에서만 사용 가능하다. var는 함수 내에서 선언 시 함수 스코프를 가지는데, 이와는 확연한 차이다.

if (sth) {
	let hello = "hi!";
}

console.log(hello);	//error!

위처럼 블록 외부에서는 인식되지 않는다.

let은 업데이트는 가능하지만, 재선언이 불가능하다. var과의 큰 차이중에 하나다.

const

파이썬과 C에서 자주 봐오던 개념이라 생소하진 않다.

const도 let처럼 블록 범위 안에서만 접근 가능하고, 나머지 둘과의 가장 큰 차이점은 한번 선언되고 나면 업데이트도, 재선언도 불가능하다는 점이다. 그만큼 최초에 무조건 초기화되어있어야 한다.

기본적으로 재선언은 불가하지만, 개체의 속성은 업데이트 가능하다. 예를 들어:

const me = {
	school: "pku",
    year: 4
};

me.school = "thu";

위의 경우는 문제가 없다.

호이스팅 (Hoisting)

변수 및 함수 선언문이 스코프 내 최상단으로 끌어올려지는 현상을 호이스팅이라고 한다. 모범 답변으론 "실행 컨텍스트 생성 시 렉시컬 스코프 내의 선언이 끌어올려 지는 게 호이스팅이다"/"인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다".

호이스팅의 대상은 var변수와 함수선언문이고, 이들의 선언만 위로 끌어 올려지고, 할당은 끌여 올려지지 않는다. let과 const의 경우에는 선언은 위로 끌어올려지지만, 초기화되지 않고 값이 선언되기 전에 변수 참조시 Reference Error가 발생한다. var은 선언과 동시에 undefined가 할당되어 초기화되기에, 이 경우 예시를 보면:

console.log(name)
var name = "percy";

은 아래와 같이 해석되어:

var name;
console.log(name)	//undefined
name = "percy"

프린트 단계에서는 undefined로 초기화된다.

함수선언문은 제대로 호이스팅 되지만, 함수표현식은 호이스팅 되지 않는다. 예시를 보면:

someFunction();	//Hoisted!

function someFunction() {
	console.log('Hoisted!');
}

는 문제없지만,

expression();	//TypeError: expression is not a function

var expression = function() {
	console.log('Hoisted!');
}

위같은 표현식은 호이스팅되지 않는다.

자바스크립트의 데이터 타입

자바스크립트의 거의 모든것은 객체지만, 원시 타입 (Primitive Type)도 있다. 이 원시 타입엔 Boolean, Null, Undefined, Number, BigInt, String, Symbol 타입이 속한다.

원시 타입

원시 타입의 데이터들은 모두 변경 불가하다. 그래서 C나 파이썬과는 다르게 문자열의 값을 바꿀수 없는 골때리는 상황들이 발생한다.

Number

다른 언어처럼 int, float/double 식으로 나뉘어 있지 않고 모두 실수로 처리하며, IEEE 754 표준에 따라서 Double precision 부동 소수점 데이터가 사용되어 -(2^53 − 1)부터 2^53 − 1까지의 수를 저장할 수 있다.

소수점 연산

OS수업에서도 배웠듯 부동 소수점 연산은 정밀도가 중요하고, 이 때문에 많은 오류가 발생한다. int타입이 아예 없기에 JS에서는 이런 오류가 생길게 뻔하다. 그래서 Number.EPSILON을 통해 연산 문제 해결이 가능하다.

const x = 0.2, y = 0.3, z = 0.1;
let eqaul = (Math.abs(x-y+z) < number.EPSILON);

NaN

자바스크립트에는 NaN (Not a Number)라는 특별한 속성도 존재하는데, 이는 숫자로 변환할 수 없는 값을 숫자로 변환하려고 하거나, 아래처럼 연산의 결과가 숫자가 아닐때 반환된다.

const a = 3 - 'a';
console.log(a);	// NaN

console.log(a === a);	// false

NaN은 자기 자신과 비교했을때도 동등하지 않을 결과가 반환되어, Number.isNaN()을 통해 숫자인지 판별할 수 있다.

String

우리가 흔히 아는 문자열이다. 16비트 유니코드 문자 집합이 저장된다. str.length로 길이에 접근 가능하다.

유용한 속성/함수들

문자열의 길이

const str = "PKU";
console.log(str.length);	// 3

부분 문자열

const str = "PKU";
console.log(str.substring(0, 2));	// PK

템플릿 리터럴

const name = 'percy';
const sentence = `I am ${name}`;

위처럼 백틱을 사용하고, 달러 기호와 중괄호를 통해 문자열 안에 값 삽입이 가능하다. 백틱으로 템플릿 리터럴을 사용하면 \n 필요 없이 여러 줄 의 문자열 표현도 가능하다.

Boolean

다른 언어들과 큰 차이 없는 true, false값을 가지는 타입이다.

Null, Undefined

null은 값이 없음을 나타내는 특별한 타입인데, undefined도 똑같이 값이 없음을 나타넨다. 둘의 차이는 undefined는 값이 할당되지 않은 변수, 혹은 반환 값이 없는 함수의 결괏값으로 할당된다.

Symbol

ES6에 새롭게 추가되었다. 데이터의 유일함을 나타낼 때 사용되고, uniqueness를 가진다. Symbol()함수를 사용하여 생성하고, 원시 타입이기에 new 키워드를 붙히지 않는다.

const sym1 = Symbol('key');
const sym2 = Symbol('key');

console.log(sym1 === sym2);	// false

BigInt

마지막으로 BigInt는 ES2020에 추가되어, 정수 뒤에 n을 붙혀 10진수 리터럴로 사용하거나, BigInt()함수를 호출하여 사용한다.

const bigInt1 = 9213849283094812334n;
const bigInt2 = BigInt(9213849283094812334);

유의할 점으로, BigInt 타입은 숫자 타입이나 Math 객체의 메서드와 함께 사용하여 연산이 불가하고, 기존 숫자 타입들은 명시적으로 타입을 BigInt로 변환하여 같이 계산해야 한다.

객체

이제 위에 얘기하던 원시 개체가 아닌 모오오오든 다른 값은 JS에서 객체다. 객체를 정의하자면, 이름(키):값형태로 여러 값을 포함하는 컨테이너이고, 컨테이너 내부의 값은 언제든지 변경이 가능하다.

오브젝트 생성

3가지 방법이 있다.

Object() 생성자 함수

const obj = new Object();
obj.id = 'id';
obj.name = 'name';

사실 인턴도 하면서 이렇게 만드는 케이스는 본적이 없다.

객체 리터럴

중괄호를 사용하는 방법이다.

const obj = {
	id = 'id';
    name = 'name';
}

제일 보편적인 방법이다.

생성자 함수

function Car(type) {
	this.type = type;
}

const car = new Car('car');

C의 냄새가 물씬 나는 방법이더. 생성자 함수는 첫 글자를 대문자로 표기하는게 관례다. 화살표 함수는 this 바인딩을 하지 않아 생성자 함수로 사용할 수 없다.

연산자

기초적인걸 제외한 몇가지 연산자를 짚고 넘어가본다

... Spread operator

let studentNames = ["Daniel", "Jane", "Joe"];

let names = [...studentNames];

console.log(names); // ["Daniel","Jane","Joe"]

let maleNames = ["Daniel", "Peter", "Joe"];
let femaleNames = ["Sandra", "Lucy", "Jane"];

let allNames = [...maleNames, ...femaleNames];

console.log(allNames); // ["Daniel","Peter","Joe","Sandra","Lucy","Jane"]

위처럼 원래 배열을 변경하지 않고 내부의 원소들을 복사할 수 있다.

let fruits = ["Mango", "Apple", "Mango", "Banana", "Mango"];

let uniqueFruits = [...new Set(fruits)];
console.log(uniqueFruits); // ["Mango","Apple","Banana"] 

위처럼 spread 연산자와 Set를 합쳐 고유 원소들만 빼낼 수도 있다.

let scores = [12, 33, 6]

const addAll = (a, b, c) => {
    console.log(a + b + c);
};

addAll(...scores); // 51

위처럼 활용해서 인자로 사용/반대의 경우다 가능하다.

함수

화살표 함수

ES2015에서 새롭게 등장하여, 기존 함수 표현식에 비해 간결하게 함수 정의가 가능하다.

특징

화살표 함수는 항상 익명 함수이고, 아래와 같은 기본 특징을 가진다:

  • function 키워드가 생략된다.
  • 매개변수가 하나면 괄호 생략이 가능하다.
  • 함수 몸체에 문이 하나밖에 없으면 중괄호, return을 생략해도 된다.

화살표 함수는 또한 argument객체와 this를 바인딩 하지 않는다.

const func = () => arguments[0];
func(1); // Error!

this

JS 함수에서는 this라는 특별한 키워드를 사용할 수 있다. 이는 읽기 전용 값으로, 함수를 호출한 방법에 의해 값이 달라진다.

일반 함수에서의 this

전역 실행 컨텍스트에서, this는 항상 전역 객체를 참조한다. 브라우저 환경에서는 window객체, Node.js 환경에선 global이 이에 해당한다.

함수에서 호출할 때를 보자.

function func() {
	console.log(this === window);	// true
}

func();

어레레. 여전히 전역 객체를 참조하고 있다. this 바인딩이 이루어지지 않아 기본값으로 전역 객체가 참조되고 있기 때문이다. 이 경우에는 엄격 모드를 사용하면 된다. ES5에 등장한 엄격 모드를 사용해보자.

function func() {
	'use strict';
    console.log(this === window);	// false
    console.log(this === undefined);	// true
}

func();

이제는 this 값이 전역 객체를 참조하지 않는다.

화살표 함수에서의 this

링크의 블로그를 많이 참고해서 이해하고 써본다.

화살표 함수는 this가 존재하지 않는다. 그래서 함수를 둘러싸고 있는 환경에서의 불변하는 렉시컬 this를 찾는다.

var name = "Global";

let user = {
  name: "Inpa",
  sayHi: () => { // 화살표 함수
    console.log(this.name); 
  }
};

user.sayHi(); // Global

위의 예제에선 만약 일반 함수였다면 정상적으로 name이 출력되었겠지만, 전역 객체를 참조한다.

하지만 콜백 함수로 사용할때 화살표 함수가 유용할 수 있다. 무슨 뜻인지 보도록 하자.

/* 일반 함수로 쓸 경우 */
let group;

group = {
  class: "1반",
  students: ["짱구", "철수", "훈이"],

  showList() {
    group.students.forEach(
      function(student) { 
        // this ==== window
      	console.log(this.class + ': ' + student) 
      } 
    );
  }
};

group.showList();	// undefined: 짱구..

콜백 함수는 기본적으로 전역 객체를 참조한다. 그때문에 undefined가 프린트 된다. 그렇다면 화살표 함수를 사용한다면?

/* 화살표 함수로 쓸 경우 */
// 화살표 함수의 this는 상위의 this를 그대로 가져온다.
let group;

group = {
  class: "1반",
  students: ["짱구", "철수", "훈이"],

  showList() {
    group.students.forEach(
      (student) => { 
        // this ==== group
      	console.log(this.class + ': ' + student) 
      } 
    );
  }
};

group.showList();	// 1반: 짱구...

화살표 함수는 스스로 this가 없기에 둘러쌓여있는 상위의 group의 this를 따라서 원하는 결과가 나온다!

배열

배열의 길이

const arr = ['a', 'b', 'c'];
console.log(arr.length);	// 3

신기하게도 length에 값을 지정해 배열의 길이를 바꿔버리고, 적당히 남는 원소들을 잘라낼 수 있다. 아래처럼:

const arr = [1, 2, 3];
arr.length = 2;

console.log(arr);	// [1, 2]

arr.length = 5;
console.log(arr);	// [ 1, 2, <3 empty items> ]

배열 내 최대값/최솟값 구하기

let maxVal = Math.max.apply(Math, candies); //min으로 최솟값

배열에 원소 추가/제거

arr.push(2); 
arr.pop();

push는 1개 이상의 원소를 추가하고, 변경된 배열의 길이를 반환한다.
pop은 배열 끝에서 한개 원소를 제거하고, 그 원소를 반환한다.

조건문

다른 언어의 조건문과 크게 다를것은 없고, 자주 쓰이는 ternary operator정도만 알아두면 좋겠다.

let len = word1.length >= word2.length ? word1.length : word2.length;

Math 활용

Math.floor(x)

Math.floor(2.1)	  // 2
Math.floor(5.95)  // 5;

MDN: Math.floor() 함수는 주어진 숫자와 같거나 작은 정수 중에서 가장 큰 수를 반환합니다.

숫자가 주어지면 주어진 수의 정수부분으로 반내림 해버린다 생각하면 된다.

Math.ceil(x)

Math.ceil(.95);   // 1
Math.ceil(4);     // 4

MDN: Math.ceil() 함수는 주어진 숫자보다 크거나 같은 숫자 중 가장 작은 숫자를 integer 로 반환합니다.

floor의 반대버전이다.

참고 자료

profile
Survived@PKU EECS;

0개의 댓글