JavaScript 중급 1

Yongjun Park·2022년 9월 15일
0

2022 OSAM 해커톤

목록 보기
3/11

2022 OSAM 해커톤 사전 온라인 교육에서 배운 내용입니다.
모르는 내용만 발췌하여 정리한 것이기 때문에 내용의 연결성이 부족한 점 양해 부탁드립니다.

let, const는 호이스팅을 하지 않는다? (X)

문법과 자료형 - JavaScript | MDN

호이스팅 : 스코프 내부 어디서든, 변수 및 함수의 선언은 스코프 최상위에 있는 것처럼 행동하는 것.

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

/* ----- JS 엔진 ----- */
var name; // 선언만 호이스팅된다. var는 name=undefined도 동시에 이루어짐

console.log(name); // undefined
name = "yopark"; // 할당은 호이스팅되지 않는다. 

ECMAScript 2015의 let과 const는 변수를 블록의 상단으로 끌어올리지만 초기화하지 않습니다. 변수가 선언되기 전에 블록 안에서 변수를 참조하게 되면 [ReferenceError](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError)를 발생시키게 되는데, 변수는 블록 시작부터 선언이 처리될 때까지 "temporal dead zone"에 위치하게 되기 때문입니다.

변수의 생성과정 : 선언(호이스팅) → 초기화 → 할당

  • var
    1. 선언 + 초기화 : 선언하자마자 undefined로 초기화
    2. 할당
  • let
    1. 선언
    2. 초기화 : 실제 코드에 도달했을 때 undefined로 초기화
    3. 할당
  • const
    1. 선언 + 초기화 + 할당 : 상수이기 때문에, 선언만 하고 나중에 값을 넣을 수 없다.

함수 스코프 vs 블록 스코프

  • var : 함수 스코프 (JavaScript 기본)
  • let, const : 블록 스코프
함수 스코프블록 스코프
functionOO
if, for 등XO

쉽게 말해서, 함수 스코프에서는 if, for 문으로 만들어진 스코프에는 전혀 개의치 않는다!

따라서 다음의 예시도 정상 작동한다.

if (true) {
	var name = "yopark";
}
console.log(name); // yopark

생성자 함수

💡 클래스 내의 생성자(construct)와는 다른 개념입니다!

  1. 함수 이름을 Capitalize하는 것이 관례.
  2. 반드시 new 연산자를 붙여 실행해야 한다.
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("지갑", 9000); // 유사한 객체를 여러개 만들 때 유리

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

계산된 프로퍼티

function makeObj(key, val) {
	return {
		// key: val; 로 했더라면, {key: "male"} 이 저장되겠죠?
		[key]: val
	};
}

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

Object 메서드

Object.assign()

const user = {
	name: "Mike"
};

const newUser = Object.assign({}, user); 
// deepcopy, {name: "Mike"} Object 복사

const newUser2 = Object.assign({age: 30}, user); 
// deepcopy, {age: 30, name: "Mike"} Object 병합

const newUser3 = Object.assign({name: "Tom"}, user);
// deepcopy, { name: "Mike" } 덮어쓰기

const newUser4 = Object.assign(user, {age: 30}, {gender: "Male"});
// 여러 객체를 한번에 병합할 수 있다. 이때, newUser4는 복사본이 아니라 user와 동일한 주소를 갖는다. 
newUser4; // {name: "Mike", age: 30, gender: "Male"}
user; // {name: "Mike", age: 30, gender: "Male"}

깊은 복사 주의점

Object.assign()은 속성의 값을 복사하기 때문에, 깊은 복사를 수행하려면 다른 방법을 사용해야 합니다.

Object.assign()이 새로운 인스턴스를 만들긴 하지만, 내부 프로퍼티 중 객체에 대한 참조 주소가 있다면 객체 자체가 복사되는 건 아니다. 따라서 완전한 deepcopy라고 할 수는 없다.

const user = {
	name: "Mike"
};

const newUser = Object.assign({}, user); 
const newUser2 = {...user}; // 전개 구문으로도 복제가 가능하다. 

Object.keys(), Object.values(), Object.entries()

  • keys : key 배열
  • values : value 배열
  • entries : [key, value] 배열
    • Object.fromEntries() : Object.entries()의 역

Symbol

특징

  1. 유일성 보장
const a = Symbol();
const b = Symbol();

a === b; // false
a == b; // false
  1. (선택) 무슨 Symbol인지 설명할 수 있음.
const id = Symbol("id info");

id.description; // id info
  1. Symbol은 keys, values, entries, for … in 에서 모두 숨겨진다.
const id = Symbol("id");
const user = {
	name: "Mike",
	age: 30,
	[id]: "myId"
};

Object.keys(user); // ["name", "age"]
for (let key in user) { } // key="name", key="age

Reflect.ownKeys(user); // ["name", "age", Symbol(id)]. Symbol까지 볼 수 있음. 
Object.getOwnPropertySymbols(user); // [Symbol(id)]. Symbol만 모아보기

사용 예시 - 표준 빌트인 객체 확장

showName까지 순회해버리는 불상사가…

Symbol을 사용하는 이유는 뭘까 | symbol usage

  1. enum의 상수값 중복 방지
  2. 프로퍼티 은닉하기(외부에 노출할 필요 없을 때)
  3. 표준 빌트인 객체 확장

Symbol.for()

const id1 = Symbol.for("id");
const id2 = Symbol.for("id");

id1 === id2; // true. key만 같다면 동일한 Symbol로 인정한다. 
Symbol.keyFor(id1); // id
Symbol.keyFor(id2); // id

전역 심볼 레지스트리에 저장되며, 하나의 key에는 하나의 Symbol만 오도록 유지한다.

소수 둘째자리까지 반올림

let userRate = 30.1234;

// Math.round에는 자릿수 기능이 없다. 무조건 정수로 반올림
Math.round(userRate * 100) / 100 // 30.12

// 자르는게 아니라 반올림이다! 완벽히 대체 가능
userRate.toFixed(2); // 30.12

isNaN()

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

x == NaN // false
x === NaN // false
NaN == NaN // 심지어 이것도 false

isNaN(x) // true. isNaN()만이 NaN을 판별하는 유일한 방법이다.

String 메서드

진수 변환

  • 10진수 → x진수 String
let n = 10;

n.toString(); // "10"
n.toString(2); // "1010"
  • x진수 String → 10진수
let redColor = "f3";
parseInt(redColor, 16); // 243
  • parseInt()와 Number()의 차이
let margin = "10px";

parseInt(margin); // 10 -> atoi라서 되는데까지만 파싱함. 
// 물론 처음부터 not digit이라면 NaN
Number(margin); // NaN

str.slice() vs str.substring() vs str.substr()

  • str.slice(n, m) : 파이썬의 슬라이싱(str[n:m]; n 이상, m 미만)과 완전히 동일함.
  • str.substring(n, m) : 슬라이싱과 비슷하지만,
    • 음수 인덱스는 안됨. (0으로 인식)
    • n > m 이더라도 str[m:n]으로 동작해줌.
  • substr(n, m) : n 이상으로 m개 가져옴.

ASCII ↔ 문자

"a".codePointAt(0); // 97. ord("a")와 동일
String.fromCodePoint(97); // a. chr(97)과 동일

Array 메서드

배열 검색 : arr.indexOf(), arr.includes(), arr.find(), arr.findIndex()

let arr = [10, 9, 8, 7, 6];

arr.indexOf(10); // 0
arr.indexOf(0); // 없으면 -1

arr.includes(10); // true
arr.includes(0); // false

arr.find((item) => {
	return item%2 === 1;
}); // 9. 요소 반환, 없으면 undefined

arr.findIndex((item) => {
	return item%2 === 1;
}); // 1. index 반환, 없으면 -1

arr.sort()의 기본 비교 함수는 strcmp다…

const arr = [2, 1, 3, 10];

arr.sort()
arr; // [1, 10, 2, 3] 와...

arr.sort((a, b) => {
	return a-b;
});
arr; // [1, 2, 3, 10] 이제서야 이렇게 됨

// lodash 라이브러리를 이용하여
_.sortBy(arr); // 로 쓰면, python처럼 숫자든 문자든 정렬 가능!

맵과 리듀스

let arr = [1, 2, 3];

const result = arr.map((val) => {
	if (val%2 === 0) {
		return "홀수";
	}
	return "짝수";
});

result; // ["홀수", "짝수", "홀수"];
arr = [1, 2, 3];

const result = arr.reduce((prev, cur) => {
	return prev + cur;
}, 0); // 초기 prev 값

result; // 6 (1+2+3)

클로저를 활용한 간단한 예시

function makeCounter() {
	let num = 0;

	return function() {
		return num++;
	}; // 함수를 반환한다!
}

let counter = makeCounter(); 
// 반환 함수 { return num++; }가 counter에 담겨있다! 
// 반환 함수가 안 사라졌기 때문에 makeCounter의 num값도 유지한다. 

console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter()); // 2
// num 은닉에 성공

setTimeout을 0초 후에 실행되도록 만든다면?

setTimeout(function() {
	console.log(2);
}, 0);

console.log(1);

이렇게 하더라도 1, 2 순서대로 출력된다.

  1. 현재 실행 중인 스크립트가 모두 종료된 이후, 스케줄링 함수를 실행하기 때문
  2. 브라우저는 기본적으로 4ms 이상의 대기시간이 있음. 그래서 실행 중인 스크립트가 모두 종료된 직후라도, 스케줄링 함수가 실행되는 데에 4ms 이상은 걸린다.
profile
추상화되었던 기술을 밑단까지 이해했을 때의 쾌감을 잊지 못합니다

0개의 댓글