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);