javascript 기본 문법

Kiwoong Park·2022년 9월 9일
0

정신을 차리고 보니 회사일이 바쁘단 핑계로 개발을 너무 뒷전으로 둔 것 같다. 하루 30분이라도 공부 및 복습을 해보고자 정리하는 javascript의 기초

변수, 호이스팅, 클로저

자바스크립트는 동적 언어로 변수의 타입은 프로그램이 처리되는 과정에서 자동으로 파악되며, 프로그램이 파악한 데이터 타입을 확인하기 위해 typeof(변수)를 통해 확인할 수 있다.

ES6이후로 되도록 var 대신 const, let을 사용. 이유는 var의 변수 호이스팅(hoisting)과 functino-level-scope로 인해 생기는 문제 때문.

변수 호이스팅(Hoisting)은 변수의 선언과 초기화가 동시에 이루어져, 아직 값이 없음에도 오류가 나지 않는 현상.
※ Hoisting의 단어 의미는 '끌어올리다'

// variable hoisting
console.log(puppy)
var puppy = "cute";
console.log(puppy);

// 아직 `puppy`라는 변수가 값을 가지고 있지 않지만 떡하니 메모리 공간은 차지하고 있다는 뜻
>> undefined
>> cute

letconst 활용을 통한 변수 hoisting 해결

let cat;
cat = "happy";
console.log(cat); // happy
let cat = "happy"; // SyntaxError: Identifier 'cat' has already been declared

const cat1 = "cute";
const cat1 = "so cute"; // 'cat1' has already been declared
// `let` 과 `const` 를 사용하면 변수를 중복으로 선언할 수 없어 변수 호이스팅 문제 해결 가능
// 내가 값은 준 변수만이 제 역할을 할 수 있게 됨.

function vs block level scope

function-level-scope 사용
함수의 블록 범위 내에서 선언한 변수는 함수 내에서만 인정하고 함수 외부에서 선언한 변수는 모두 전역변수가 됨.

var는 function-level-scope를 따름. 그러므로 전역변수에 원하지 않는 값을 덮어쓸 수 있는 문제점이 존재.

var dog = "cute";
console.log(dog); //cute
{
  var dog = "so cute";
}
console.log(puppy); // so cute

letconstblock-level-scope. 블록 내부에서 선언된 변수는 외부에 영향을 끼치지 않음. 따라서 아래 코드에서 1행의 dog와 5행의 dog는 이름만 같을 뿐 다른 변수다.

let dog = "cute";
{
  let dog = "so cute";
}
console.log(dog); // cute

const 와 let, const는 수정X

const dog = "cute";
dog = "so cute!!"; // TypeError: Assignment to constant variable
let dog;
console.log(dog); // undefined
dog = "so lovely"; // 
console.log(dog); // so lovely

closure의 개념
내부 함수가 외부 함수의 스코프(범위)에 접근할 수 있는 것을 말함.
자바스크립트에서 스코프는 함수 단위로 생성되는데,
아래 예제에서 inner() 함수의 스코프가 outer()함수의 스코프를 참조하고 있고 outer()의 실행이 끝나고 소멸된 이후에도 inner() 함수가 outer() 함수의 스코프에 접근할 수 있는 것을 closure라고 함.

function outer() {
  var a = 'A';
  var b = 'B';
  function inner() {
    var a = 'AA';
    console.log(b)
  }
  return inner;
}
var outerFunc = outer()
outerFunc(); // B

객체와 배열

자바스크립트에서 객체는 키(key)와 값(value)의 쌍으로 이루어진 property의 정렬되지 않은 집합을 의미함. Ex. '직장인'이라는 객체를 생성한다고 하면 '직장인'이 가질 수 있는 특징을 property라고 정의하고 이를 키-값 쌍으로 매핑하여 저장함.

const worker = {
  name: "James",
  job: "Engineer",
  get_name: function () {
    return this.name;
  }
}
console.log(worker)

객체가 가진 특징, 정보를 property라고 하고 위처럼 키: '값' 형태로 나타냄. 객체는 행위를 가질 수 있는데 객체 안에 '함수'를 넣어 만드는 위의 예제에서 get_name이 이에 해당. 이를 메서드(method) 라고 함.

객체 배열 생성

const coffee = [];
coffee.push({ name: 'Ice Americano'});
coffee.push({ name: "Latte"});
console.log(coffee); // [ { name: 'Ice Americano' }, { name: 'Latte' } ]
console.log(coffee[0]); // { name: 'Ice Americano' }
console.log(coffee.length); // 2

배열은 [요소1, 요소2, 요소3 , ...]를 통해서 생성할 수 있고 배열 안에는 숫자, 문자열, 객체 등 어떤 것이든 요소로 넣을 수 있음. 요소를 추가하고 싶으면 .push() 내장 함수를 이용해 원하는 요소를 밀어 넣을 수 있음.

구조 분해 할당

const animal = ['dog','cat'];
let [first, second] = animal;
console.log(first); // dog
console.log(second); // cat

자바스크립트에서는 객체와 배열을 많이 사용하게 되는데 위의 예제처럼, 객체나 배열을 변수로 간편하게 '분해'해주는 문법이 바로 구조 분해 할당.

배열 내장 함수

함수설명
forEach()for문을 짧게 줄임
indexOf()원소의 인덱스를 반환
findIndex()배열의 요소가 객체, 배열일 때
shift()첫 번쨰 원소 제거 및 반환
unshift()맨 앞에 새 원소를 추가
join()배열 요소를 문자열로 합침
map()배열 각 원소 반환
find()찾은 값을 반환
filter()조건을 만족하는 배열 생성
slice()인덱스로 특정 항목을 제거
pop()마지막 원소 제거 및 반환
concat()배열 합침
reduce()누적 값을 계산

함수

함수의 선언과 화살표 함수의 선언

function add(a, b) {
  return a + b;
}
console.log(add(1, 4)); // 5
const add = (a, b) => {
  return a + b;
}
console.log(add(1, 4)); //5
// 함수 내에 return 밖에 없다면 아래와 같이 화살표 함수를 줄일 수 있다. 
const add = (a, b) => a+b;

화살표 함수에는 1.함수명, 2. arguments, 3. this 이 세 가지가 없음. 즉, 익명함수로 동작하며 arguments, this 가 없다는 것은

const func = function() {
  console.log(arguments);
}
func(1, 2, 3, 4); // [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }

위와 같이 보통 함수가 생성되면 자신의 스코프 안에 자기 자신을 가리키는 this와 파라미터가 담기는 arguments가 자동으로 생성됨.

const func = (...args) => {
  console.log(args);
}
func(1, 2, 3, 4); // [1, 2, 3, 4]

화살표 함수에는 arguments가 자동으로 생성되지 않기 때문에 arguments가 필요하다면 함수의 파라미터 부분에 ...args를 넣어 args라는 배열 안에 파라미터를 담을 수 있습니다.
...을 전개 연산자라고 하는데, "값이 몇 개가 될지 모르나 뒤에 오는 변수명에 값을 배열로 넣어라"라고 하는 ES6 이후 추가된 문법 중 하나 입니다.

자바스크립트에서 this는 다른 언어와 조금 다르게 동작하기 때문에 햇갈리는 개념인데,
자바스크립트에서 this는 호출하는 방법에 의해 결정된다.
참조 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this#%EA%B5%AC%EB%AC%B8
브라우저 콘솔에서 console.log(this);을 찍어보면 결과로 Window 객체가 나오게 된다.
console.log(this);을 호출한 함수는 전역(Global)이고 브라우저에서 Global은 Window 객체를 의미하기 때문에 이와 같은 결과가 나온다.

this의 사용

var people = {
  name: 'gildong',
  say: function() {
    console.log(this);
  }
}
people.say(); // { name: 'gildong', say: [Function: say] }
var sayPeople = people.say;
sayPeople(); // Window
<ref *1> Object [global] {
  global: [Circular *1],
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  queueMicrotask: [Function: queueMicrotask],
  performance: Performance {
    nodeTiming: PerformanceNodeTiming {
      name: 'node',
      entryType: 'node',
      startTime: 0,
      duration: 38.0198999941349,
      nodeStart: 0.6110000014305115,
      v8Start: 1.8744999915361404,
      bootstrapComplete: 22.913000002503395,
      environment: 10.90089999139309,
      loopStart: -1,
      loopExit: -1,
      idleTime: 0
    },
    timeOrigin: 1662819229827.893
  },
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  }
}

people.say()에서는 people 객체가 say()를 호출했으므로 this는 people 객체가 되고, sayPeople 변수에 people.say를 넣고 호출한 경우는 전역변수 즉, 전역(Global)이 호출한 주체가 되므로 this는 전역(Global) 객체가 됨.

bind 함수의 사용

var sayPeople = people.say.bind(people);
sayPeople(); // { name: 'gildong', say: [Function: say] }

this를 객체로 고정하고 싶다면 bind(this로 고정시킬 객체) 함수를 이용하면 됨.
하지만 화살표 함수에서는 bind를 사용해도 this를 주입할 수 없음. 때문에 생성자(new)를 사용해 화살표 함수로 만들어진 객체의 인스턴스를 생성할 수 없음. 만약 화살표 함수로 만들어진 객체에 this를 사용한다면 일반적인 인자나 변수와 동일하게 취급하게 되고 내부 스코프에 this가 없기 때문에 상위 함수의 this나 전역(Global) 객체의 this를 불러오게 됨.

프로토타입과 상속

프로토타입(prototype)은 자바스크립트로 객체 지향 프로그래밍을 할 수 있게 도와주는 것. 자바스크립트에는 클래스가 없으므로 '프로토타입'을 통해 비슷하게 흉내 냄. 때문에 자바스크립트는 객체 지향 언어라고 하지 않고 프로토타입 기반 언어라고 함.

function func() {};
console.log(func.prototype); // func {}
func.prototype.name = 'gildong';
console.log(func.prototype); // func { name: 'gildong' }

자바스크립트에서 기본 데이터 타입을 제외한 모든 것이 객체인데, 이 객체의 '원형'인 프로토타입을 이용해서 새로운 객체를 만들어내고 이렇게 생성된 객체는 또 다른 객체의 원형이 되어 새로운 객체를 만들어낼 수 있습니다. prototype은 객체의 프로퍼티 중 용도가 약속되어 있는 특수한 프로퍼티이고 이 역시도 객체입니다.


그림과 같이 func.hasOwnProperty()라는 프로퍼티도 내가 선언하지 않았지만 프로토타입 객체(proto)에 기본으로 저장되어 있어 사용할 수 있게 됨.
객체 안에 proto라는 프로퍼티가 있고 이 프로퍼티를 만들어낸 원형인 '프로토타입 객체'를 참조하는 숨겨진 링크가 있는데 이 링크를 '프로토타입'이라고 정의함.

프로토타입과 상속

const animal = {
  leg: 4,
  tail: 1,
  say() {
    console.log('I have 4 legs and 1 tail');
  }
}
const dog = {
  sound: 'Mung',
}
const cat = {
  sound: 'Yaong',
}
dog.__proto__ = animal;
cat.__proto__ = animal;
console.log(dog.leg); //4

이와 같이 프로토타입은 '상속'을 가능하게 하며 아래 예시와 같이 Prototype Chaining도 가능함. cat에 happy 프로퍼티가 없으므로 프로토타입인 dog의 프로퍼티에서 찾고, cat에 leg 프로퍼티가 없고 프로토타입인 dog에도 없으니 dog의 프로퍼티인 animal에서 찾음.

const animal = {
  leg: 4,
  tail: 1,
  say() {
    console.log('I have 4 legs and 1 tail');
  }
}
const dog = {
  sound: 'Mung',
  happy: true,
}
const cat = {
  sound: 'Yaong',
}
dog.__proto__ = animal;
cat.__proto__ = dog;
console.log(cat.happy); // true
console.log(cat.leg);   // 4

프로토타입을 클래스처럼 사용

function Animal() {}
Animal.prototype.legs = 4;
Animal.prototype.tail = 1;
const dog = new Animal();
const cat = new Animal();
console.log(cat.legs); //4
console.log(dog.tail); // 1

자바스크립트에서는 프로토타입을 이용해, 클래스 대신 function과 new를 통해 클래스를 흉내낼 수 있음. 그리고 객체.prototype.속성키 = 속성값을 통해 객체를 공유해서 사용할 수 있음. 위 예제에서 dog와 cat은 Animal.prototype.legs, Animal.prototype.tail이라는 객체를 공유해서 사용하기 때문에 총 2공간만 메모리에 할당됨. 만약 prototype을 이용하지 않고 this.legs, this.tail과 같이 사용했다면 총 4공간이 할당되므로 prototype을 사용해 공간 절약이 가능해짐.

profile
You matter, never give up

0개의 댓글