본문 바로가기

JavaScript

20210517 JavaSciprt 02 : 함수(매개변수, 인수, arguments 키워드, Arrow function, IIFE, 호이스팅, 타이머함수, 콜백) , 클래스(prototype, functional class, this(normal, arrow), ES6class, 상속)

JavaScript02

Javascript : 함수

함수 선언

function sum(x, y) {
  console.log(`process : ${x + y}`);
  // 함수가 호출되면 항상 실행하는 부분
  return x + y;
  // 함수 호출 결과로 변수에 담을 수 있는 곳
}

매개 변수와 인수

  • 인수 : 함수를 사용하면서 사용되어지는 진짜 값 sum(1, 3) -> 1, 3
  • 매개변수 : 들어 오는 인수를 어떻게 사용할지 함수 로직을 표현함에 있어서 인수와 로직을 연결하는 변수 sum(x, y) -> x, y

함수의 사용

변수에 담아서 사용하는 경우

  • 반복되는 값을 연산하는 경우 함수 실행 값을 담아서 사용하는 것이 효율적임
// 함수 호출 : process 실행 & 변수에 return 값 저장
const a = sum(1, 3);
const b = sum(4, 12);

// 변수에는 호출 결과만 담겨 있기 때문에 process는 호출 되지 않음
console.log(a);
console.log(b);
console.log(a + b);

직접적으로 함수를 사용하는 경우

  • 아래 방식으로 직접적으로 함수를 넣어 연산하는 경우에는 컴퓨터 자원을 반복적으로 사용하고,
  • 함수의 process를 다시 실행 시킴으로서 비효율 적임
console.log(sum(1, 3));
console.log(sum(4, 12));
console.log(sum(1, 3) + sum(4, 12));
  • 함수의 실행을 최소한으로 하는게 좋다. (컴퓨터 자원이 들어가기 때문)
  • 함수의 결과를 반복적으로 사용하느냐에 따라서
  • 함수의 결과를 담느냐(반복 결과) or 바로 함수를 호출 하여 사용하느냐(단일 결과)

기명함수 vs 익명함수 vs 함수 표현

  • 기명함수 : 일반적인 사용으로 함수 이름이 들어간 함수
  • 익명함수 : 함수 이름이 없는 함수
  • 함수 표현 : 함수를 변수에 담아서 표현(선언)하는 경우
// 기명함수
function sum(x, y) {
    return x + y;
}

// 익명함수
function (x, y) {
  return x + y
}

// 함수 표현
const sum1 = function (x, y) {
    return x + y;
};

return 키워드

  • return 키워드는 함수를 종료를 의미하고 그 다음의 코드는 실행 하지 않음
function sum2(x, y) {
  if (x < 2) {
    return;
  }
  return x + y;
}

console.log(sum2(1, 3)); // undefined
console.log(sum2(3, 3)); // 6

arguments 키워드

  • 받는 인수가 너무 많아 일일이 매개변수 이름을 지정하기 힘든 경우
  • Arguments 객체는 들어온 인자를 배열 형태로 만듦
function sumArguments() {
  console.log(arguments);
  return arguments[0] + arguments[1];
}

console.log(sumArguments(1, 3));
// Arguments(2) [1, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 4

Arrow 함수 (() => {} vs function () {})

  • 화살표 함수 (Arrow function) : 기존함수와 다르게 축약형으로 함수를 표현할 수 있음
  • 기존 함수
const double = function (x) {
  return x * 2;
};
console.log("double: ", double(7));
  • 화살표 함수
const doubleArrow = (x) => {
  return x * 2;
};
console.log("doubleArrow : ", doubleArrow(7));
  • 축약형
// retunr 생략, 중괄호 생략 한줄로 나타낼 수 있음
const doubleShort01 = (x) => x * 2;

// retunr 생략, 매개변수 한개인 경우 소괄호 생략 가능
const doubleShort02 = (x) => x * 2;

// 중괄호 사용시에는 return 키워드를 꼭 적어주어야 함
const doubleShort03 = (x) => {
  return x * 2;
};

// 중괄호를 코드 블럭으로 인식하기 때문에 객체를 표현할 때는 소괄호로 한번 묶어 주어야 함
const doubleShort04 = (x) => ({
  name: "Tom",
});

console.log("doubleShort01 : ", doubleShort01(7)); // doubleShort01 :  14
console.log("doubleShort02 : ", doubleShort02(7)); // doubleShort02 :  14
console.log("doubleShort03 : ", doubleShort03(7)); // doubleShort03 :  14
console.log("doubleShort04 : ", doubleShort04(7)); // doubleShort04 :  {name: "Tom"}

즉시 실행 함수 (IIFE)

  • IIFE, Immediately-Invoked Function Expression
// 기존 함수 선언과 사용법
const a = 7;
function double() {
  console.log(a * 2);
}
double();
  • 함수를 만들자 마자 바로 실행 시키는 법
    • 방법01 : 소괄호 안에 익명함수를 넣고 외부에 소괄호를 쓰는 방법
    • 방법02 : 익명함수 뒤에 소괄호를 적고 소괄호로 감싸는 방법
// 방법 01
(function () {
  console.log(a * 2);
})();

// 방법 02
(function () {
  console.log(a * 2);
})();

호이스팅 (Hoisting)

  • 함수 선언부가 유효범위 최상단으로 끌어 올려지는 현상
    • (함수 선언 방식O , 함수 표현 방식X)
  • 함수 명만 보고도 대충 어떤 역할인지 알기 때문에 보통 함수 선언하는 곳을 최하단으로 하여 가시성을 높임 그래서 호이스팅을 사용함
const a = 7;

double(); // 함수 표현의 경우 -> Uncaught TypeError: double is not a function

// 함수 표현 방식은 호이스팅이 없음
const double = function () {
  console.log(a * 2);
};

// 함수 선언 방식은 호이스팅이 있음
function double() {
  console.log(a * 2);
}

타이머 함수 (Timer function)

  • setTimeout(함수, 시간) : 일정 시간 후 함수 실행
  • setInterval(함수, 시간) : 시간 간격 마다 함수 실행
  • clearTimeout(): 설정된 Timeout 함수를 종료
    • (해당 타이머 함수의 return을 받아서 인자로 넣으면 해당 timer 종료)
  • clearInterval(): 설정된 Interval 함수를 종료
    • (해당 타이머 함수의 return을 받아서 인자로 넣으면 해당 interval 종료)
  • 1000ms -> 1초
// Timeout
// 기본 방식
const timer = setTimeout(function () {
  console.log("setTimeout! : test stop!");
}, 3000);

// 화살표 함수 방식
setTimeout(() => {
  console.log("setTimeout! : Arrow");
}, 3050);

// clear 설정
const h1El = document.querySelector("h1");
h1El.addEventListener("click", () => {
  clearTimeout(timer);
});

// Interval
const interval = setInterval(function () {
  console.log("setInterval! : test stop!");
}, 3000);

const h2El = document.querySelector("h2");
h2El.addEventListener("click", () => {
  clearInterval(interval);
});

콜백 (callback)

  • 함수의 인수로 사용되는 함수
  • 실행되는 특정한 위치를 보장하기 위해서 사용함
    • 특정함수 다음에 실행 되게 하고 싶은 경우 callback 함수를 처리 함으로서, 특정함수가 실행되고 callback 함수가 실행되게 의도할 수 있음
function timeout(callback) {
  setTimeout(() => {
    console.log("middle");
    callback();
  }, 2000);
}

timeout(() => {
  console.log("Done!");
});

console.log("start");
// start
// middle
// Done!

Javascript : Class

생성자 함수 (prototype)

생각의 시작

  • 아래 처럼 객체 데이터를 일정한 형식으로 사용하는데 반복되어 자주 사용되는 경우에는 일일이 변수에 객체 데이터 형식을 지정하여 주기가 번거 롭고 비효율 적이다.
    • this : 객체 데이터가 다른 변수의 이름으로 할당 될수도 있으므로 this 키워드를 통해 객체 데이터 전체를 지칭함
    • property와 method로 이루어진 객체 데이터에서 property와 method를 통틀어서 member라고 부름
  • 일일이 하나씩 객체를 만들어야 하는 문제가 생겨 효율이 떨어짐(메모리 관리상)
  • 함수도 계속 메모리에 할당되어 소모가 됨
// 리터럴 방식
const heropy = {
  firstName: "Heropy", // property
  lastName: "Park", // property
  getFullName: function () {
    // method
    return `${this.firstName} ${this.lastName}`;
  },
};
const amy = {
  firstName: "Amy",
  lastName: "Clarke",
  getFullName: function () {
    return `${this.firstName} ${this.lastName}`;
  },
};
const neo = {
  firstName: "Neo",
  lastName: "Smith",
  getFullName: function () {
    return `${this.firstName} ${this.lastName}`;
  },
};
console.log(heropy.getFullName());
console.log(amy.getFullName());
console.log(neo.getFullName());

생성자 함수 사용

  • 반복과 중복을 제거하기 위해서 일정한 형식의 객체를 만드는 함수를 만들게 됨 (생성자 함수)
  • 다른 함수와 다르게 하기 위해서 함수 이름을 파스칼 케이스로 작성 (앞 문자 대문자) -> 생성자 함수 구나 알수 있음
function User(first, last) {
  this.firstName = first;
  this.lastName = last;
}
  • 객체 형식을 유지하고 각 객체의 특성마다 조금씩 다르게 데이터를 가지게 하는데, 이때 이렇게 각각 조금 씩 다른 데이터를 가진 객체 변수를 인스턴스라고 함
    • heropy1 , amy1 , neo1 : 인스턴스 들
  • 객체를 표현하는 방식은 두가지 방식이 있음
    • 생성자 함수를 사용한 방식
    • 리터럴 방식 ({}, "", [] 와 같이 데이터 형식을 나타내는 표현을 활용한 방식)
// 생성자 함수를 사용한 방식
const heropy1 = new User("Heropy", "Park");
const amy1 = new User("Amy", "Clarke");
const neo1 = new User("Neo", "Smith");
  • 그리고 각 객체(인스턴스)들 마다 사용하는 함수가 완전히 일치하는 경우, 각 객체들 마다 함수를 가지고 있는 것은 중복이고 메모리 낭비를 초래하게 된다.
  • 그래서 prototype을 이용하여 어떤 객체든지 메모리에 딱 한번만 만들어져 있는 함수를 참조하게 함 (인스턴스가 생성되면 그때 만들어진 함수를 다른 인스턴스가 참조하게 됨)
  • prototype은 어떤 자료형이든지 가지고 있는 인스턴스들 간의 공통 method 객체임
User.prototype.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
};
console.log(heropy1); // User {firstName: "Heropy", lastName: "Park"}
console.log(heropy1.getFullName()); // Heropy Park
console.log(amy1); // User {firstName: "Amy", lastName: "Clarke"}
console.log(neo1); // User {firstName: "Neo", lastName: "Smith"}

this

  • 일반 함수는 호출 위치에서 따라 this 정의!
  • 화살표 함수는 자신이 선언된 함수 범위(만들어지는 그 위치)에서 this 정의!
  • 즉, this를 포함한 일반 함수인 경우, this는 일반 함수가 호출된 위치의 객체를 가르키는 것이고
  • this를 포함한 화살표 함수는인 경우, this는 화살표 함수가 선언된 함수 블록이 가르키는 객체를 나타낸다.
// 예제 1
const heropy = {
  name: "Heropy",
  normal: function () {
    console.log(this);
  },
  arrow: () => {
    console.log(this); // {}
  },
};
heropy.normal(); // {name: "Heropy", normal: ƒ, arrow: ƒ}
// heropy 내부에 normal이라는 method 실행 -> 호출위치 : heropy
heropy.arrow(); // 호출 위치와 상관 없이, 화살표 함수를 둘러싼 함수가 없기 때문에 가르키는 것이 없어 빈 객체를 가르킴

// 예제 2
const amy = {
  name: "Amy",
  normal: heropy.normal,
  // normal: function () {
  //     console.log(this);
  // }, 이것과 같음
  arrow: heropy.arrow, // {}
};

amy.normal(); // 찾아가는게 아니고 해당 함수만 고대로 참조하는 것이라서 {name: "Amy", normal: ƒ, arrow: ƒ}
amy.arrow(); // {}

// 예제 3
function User(name) {
  this.name = name;
}

User.prototype.normal = function () {
  console.log(this);
};

User.prototype.arrow = () => {
  console.log(this);
};

const heropy1 = new User("Heropy");

heropy1.normal(); // User {name: "Heropy"}
heropy1.arrow(); // {}

//예제 4
const timer = {
  name: "Tom",
  timeout: function () {
    setTimeout(function () {
      // setTimeout의 내부 콜백으로 들어가서 호출 됨
      console.log(this); // window
    }, 2000);
  },
};
timer.timeout();
// Window {window: Window, self: Window, document: document, name: "", location: Location, …}

const timer1 = {
  name: "Tom",
  timeout: function () {
    setTimeout(() => {
      console.log(this);
      // 화살표 함수가 선언되고 이를 감싸 선언되고 있는 함수 : timeout -> timeout에서 가르키고 있는 객체인 this : timer1
    }, 2000);
  },
};
timer1.timeout();
// {name: "Tom", timeout: ƒ}

ES6 class

메소드 코드 축약 가능

  • : function 키워드 없이 바로 축약해서 선언 가능함
const heropy = {
  name: "Heropy",
  normal() {
    console.log(this);
  },
  arrow: () => {
    console.log(this);
  },
};
heropy.normal();
heropy.arrow();

class 키워드 사용

  • class 키워드를 사용함
  • function을 사용할때 인자로 받던것을 constructor 키워드를 사용하여 생성 인자를 받음
  • 함수명(매개변수) {구현부} 형식으로 넣으면 기존의 function 사용 class의 prototype에 함수가 들어감
class UserClass {
  constructor(first, last) {
    this.firstName = first;
    this.lastName = last;
  }
  getFullName() {
    // prototype과 마찬가지로 기능함
    return `${this.firstName} ${this.lastName}`;
  }
}

const heropy2 = new UserClass("Heropy", "Park");
const amy2 = new UserClass("Amy", "Clarke");
const neo2 = new UserClass("Neo", "Smith");

console.log(heropy2.getFullName()); // Heropy Park
console.log(amy2.getFullName()); // Amy Clarke
console.log(neo1); // User {firstName: "Neo", lastName: "Smith"}

상속 inheritance (확장 extends)

  • extends 키워드를 사용하여 class에 이미 있던 기능에 살을 붙여 확장해 나가는 작업
  • super 키워드를 통해 부모 class constructor 접근
  • 일단 자식 메서드 및 프로퍼티 먼저 확인하고, 없으면 부모것을 확인 함
class Vehicle {
  constructor(name, wheel) {
    this.name = name;
    this.wheel = wheel;
  }
}
const myVehicle = new Vehicle("운송수단", 2);
console.log(myVehicle);

class Bicycle extends Vehicle {
  constructor(name, wheel) {
    super(name, wheel);
  }
}

const myBicycle = new Bicycle("삼천리", 2);
const daughtersBicycle = new Bicycle("세발", 3);
console.log(myBicycle);
console.log(daughtersBicycle);

class Car extends Vehicle {
  constructor(name, wheel, license) {
    super(name, wheel);
    this.license = license;
  }
}
const myCar = new Car("벤츠", 4, true);
const daughtersCar = new Car("포르쉐", 4, false);

console.log(myCar);
console.log(daughtersCar);