Javascript 를 복습하기 위해, 위 youtube 강의를 듣고 정리한 내용입니다.
인자 전달
function showName(name) {
console.log(name);
}
showName('Mike'); // 'Mike'
showName('Mike', 'Tom'); // 'Mike'
showName(); // undefined
arguments
function showName(name) {
console.log(arguments.length);
console.log(arguments.length);
console.log(arguments.length);
}
showName('Mike', 'Tom');
// 2
// 'Mike'
// 'Tom'
Rest parameters
ES6 환경에서는 나머지 매개 변수 사용을 권장한다.
function showName(...names) {
console.log(names);
}
showName('Mike'); // ['Mike']
showName('Mike', 'Tom'); // ['Mike', 'Tom']
showName(); // []
function add(...numbers) {
let result = numbers.reduce((prev, cur) => prev + cur);
console.log(result)
}
add(1, 2, 3);
add(1, 2, 3, 4, 5, 6, 7, 8, 9);
function User(name, age, ...skills) {
this.name = name;
this.age = age;
this.skills = skills;
}
const user1 = new User('Mike', 30, 'html', 'css');
const user2 = new User('Mike', 30, 'JS', 'React', 'Vue');
const user3 = new User('Mike', 30, 'English');
Spread syntax
배열
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let result = [0, ...arr1, ...arr2, 7, 8, 9];
객체
let user = {name: 'Mike', age: 30};
let user2 = {...user};
let user3 = {
...user,
skill: "JS"
}
user2.name = "Tom";
console.log(user.name); // 'Mike'
console.log(user2.name); // 'Tom'
Lexical environment
// 코드가 실행되면, 선언한 변수들이 lexical 환경 위로 올라간다. (전역 lexical 환경)
// one : 초기화 X (사용 불가)
// addOne : function
let one; // one : undefined (사용 가능)
one = 1; // one : 1 (할당)
function addOne(num) {
console.log(one + num);
}
addOne(5);
// 함수 실행 시 새로운 lexical 환경 생성 (매개변수와 지역변수들이 저장) (함수 내부 lexical 환경)
// num : 5
add 함수를 만들어주는 makeAdder
함수가 있다고 해보자.
// 코드 실행 시 `makeAdder` 와 `add3` 가 전역 Lexical 환경에 올라간다.
// makeAdder: function, add3: 초기화 X
function makeAdder(x) {
return funtion(y) {
return x + y;
}
}
const add3 = makeAdder(3);
// makeAdder Lexical 환경이 생성된다.
// x : 3
// 전역 Lexical 환경의 add3 이 function 으로 초기화됨
console.log(add3(2));
// 위 코드 실행 시, add3에 대한 익명함수 Lexical 환경이 생성
// y : 2
const add10 = makeAdder(10);
console.log(add10(5)); // 15
console.log(add3(1)); // 4
makeAdder
에 의해 add3
함수가 생성된 "이후" 에도, 상위 함수인 makeAdder 의 x 에 접근이 가능하다.
add10
과 add3
은 서로 다른 환경을 가지게 된다!이런 경우를 "Closure" 라고 부른다.
function makeCounter() {
let num = 0; // 은닉화
return function () {
return num++;
}
}
let counter = makeCounter();
console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter()); // 2
function fn() {
console.log(3)
}
setTimeout(fn, 3000); // 3초후 fn 실행
setTimeout(function() {
console.log(3)
}, 3000);
function showName(name) {
console.log(name);
}
setTimeout(showName, 3000, 'Mike'); // 매개변후 마지막에 전달
setTimeout()
은 tId
를 반환한다. clearTimout(tid)
으로 예정된 작업을 없앨 수 있다. function showName(name) {
console.log(name);
}
const tId = setInterval(showName, 3000, 'Mike');
clearInterval(tid)
도 존재.setTimeout(() => console.log(2), 0);
console.log(1);
// 1
// 2
let num = 0;
function showTime() {
console.log(`접속하진지. ${num++}초가 지났습니다.`);
if (num > 5) {
clearInterval(tId);
}
}
const tId = setInterval(showTime, 1000);
JavaScript 에선, 함수 호출 방식과 관계없이 this 를 지정할 수 있다.
const mike = {
name: 'Mike',
};
const tom = {
name: 'Tom',
};
function showThisName() {
console.log(this.name);
}
showThisName(); // this 는 window 를 가리키므로, window.name 즉 빈 문자열 출력
showThisName.call(mike); // Mike
showThisName.call(tom); // Tom
function update(birthYear, occupation) {
this.birthYear = birthYear;
this.occupation = occupation;
}
update.call(mike, 1999, 'singer'); // 첫번째 매개변수는 this 로 사용할 값, 뒤 부터는 함수 매개변수
console.log(mike); // {name: "Mike", birthYear: 1999, occupation: "singer"}
update.apply(tom, [2002, 'teacher']); // apply 는 함수 매개변수를 배열로 받는다.
console.log(tom);
call, apply 를 활용한 min, max
const nums = [3, 10, 1, 6, 4];
const minNum = Math.min(...nums);
const maxNum = Math.max(...nums);
const minNum2 = Math.min.apply(null, nums);
const maxNum2 = Math.max.call(null, ...nums);
bind
const updateMike = update.bind(mike);
updateMike(1990, "police");
console.log(mike); // mike updated !!
const user = {
name: "Mike",
showName: function () {
console.log(`hello, ${this.name}`);
}
}
user.showName(); // hello, MIke
let fn = user.showName;
fn(); // hello,
fn.call(user); // hello, Mike
fn.apply(user); // hello, MIke
let boundFn = fn.bind(user);
boundFn(); // hello, Mike
const user = {
name : "Mike"
}
user.name // "Mike"
user.hasOwnProperty('name'); // 'true'
user.hasOwnProperty('age'); // false
user
// {name: "Mike"}
// __proto__: Object
// ...
hasOwnProperty()
메서드는 우리가 만든게 아닌데, 어디 있는 걸까? __proto__
객체의 메서드이다. hasOwnProperty()
라는 메서드를 직접 가지고 있으면, user 객체의 메서드를 사용하고, 없을 경우에만 __proto__
에서 찾는다. 상속
const bmw = {
color: "red",
navigation: 1,
wheels: 4,
drive() {
console.log("drive...");
}
};
const benz = {
color: "black",
wheels: 4,
drive() {
console.log("drive...");
}
};
const bmw = {
color: "blue",
wheels: 4,
drive() {
console.log("drive...");
}
};
위 코드를 아래와 같이 변경 가능
const car = {
wheels: 4,
drive() {
console.log("drive...");
}
}
const bmw = {
color: "red",
navigation: 1,
};
const benz = {
color: "black",
};
const bmw = {
color: "blue",
};
bmw.__proto__ = car;
benz.__proto__ = car;
audi.__proto__ = car;
console 에서 bmw 출력 시엔, color 와 navigation 속성 만이 드러난다.
상속은 계속 이어질 수 있다.
const x5 = {
color: "white",
name: "x5"
}
x5.__proto__ = bmw;
반복문
for(p in x5) {
console.log(p);
} // color, name, navigation, wheels, drive
Object.keys(x5) // ["color", "name"]
Object.values(x5) // ["white", "x5"]
for(p in x5) {
if(x5.hasOwnProperty(p)) {
console.log('o', p);
} else {
console.log('x', p);
}
}
// o color
// o name
// x navigation
// x wheels
// x drive
생성자 함수
const car = {
wheels: 4,
drive() {
console.log("drive...");
}
}
const Bmw = function (color) {
this.color = color;
}
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
x5.__proto__ = car;
z4.__proto__ = car;
or 아래와 같이 중복 코드를 줄일 수 있다.
const Bmw = function (color) {
this.color = color;
}
Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function () {
console.log("drive...");
}
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
console.log(z4 instanceof Bmw); // true
console.log(z4.constructor === Bmw); // true
or 한번 더
const Bmw = function (color) {
this.color = color;
}
Bmw.prototype = {
wheels: 4,
drive() {
console.log("drive...");
},
navigation: 1,
stop() {
console.log("STOP!");
},
};
const x5 = new Bmw("red");
const z4 = new Bmw("blue");
console.log(z4 instanceof Bmw); // true
console.log(z4.constructor === Bmw); // false !!!
constructor 비교 시 false 를 반환한다.
Bmw.prototype = {
constructor: Bmw,
wheels: 4,
drive() {
console.log("drive...");
},
navigation: 1,
stop() {
console.log("STOP!");
},
};
Closure 를 이용한 은닉화
const Bmw = function (color) {
const c = color;
this.getColor = function () {
console.log(c);
}
}
비슷한 형태의 객체를 생성하기 위해 생성자 함수를 이용했었다.
ES6 부터 추가된 Class 를 사용해도 된다.
const User = function (name, age) {
this.name = name;
this.age = age;
this.showName = function () {
console.log(this.name);
};
};
const mike = new User("Mike", 30);
class User2 {
constructor(name, age) {
this.name = name;
this.age = age;
}
showName() {
console.log(this.name);
}
}
const tom = new User2("Tom", 19);
showName()
과 같이 class 내에 정의한 메서드는 __proto__
에 저장된다. 생성자 함수를 class 와 동일하게 동작하게 하려면?
const User = function (name, age) {
this.name = name;
this.age = age;
};
User.prototype.showName = function () {
console.log(this.name);
}
그렇다면 왜 class 를 사용하는가?
// 위의 User 와 User2 를 살펴보면
const mike = User("Mike", 30); // new 가 없는 경우
console.log(mike); // undefined
const tom User2("Tom", 19);
// TypeError : constructor Users cannot be invoked without 'new'
for(const p in mike) {
console.log(p);
} // name, age, showName
for(const p in tom) {
console.log(p);
} // name, age
상속
// extends
class Car {
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive...");
}
stop() {
console.log("STOP!");
}
}
class Bmw extends Car {
park() {
console.log("PARK");
}
}
const z4 = new Bmw("blue");
console.log(z4);
// constructor 로 생성된 color 와 wheels 가 객체 속성 (최상단) 에 위치하고,
// __proto__ 내부에 park 이 위치
// drive, stop 은 그 다음 __proto__ 에 위치
z4.drive();
// z4 객체 내에서 drive 를 찾고, __proto__, 그 다음 __proto__ 까지 가서 찾는다.
메서드 오버라이딩
class Bmw extends Car {
park() {
console.log("PARK");
}
stop() {
console.log("OFF"); // car 클래스에도 존재하는 메서드. 오버라이드 가능
}
}
class Bmw extends Car {
park() {
console.log("PARK");
}
stop() {
super.stop(); // 부모의 stop 메서드 사용
console.log("OFF");
}
}
z4.stop();
// STOP!
// OFF
생성자 오버라이딩
class Bmw extends Car {
constructor(color) {
super(color);
this.navigation = 1;
}
park() {
console.log("PARK");
}
}
super()
로 부모 클래스의 생성자를 실행해야 한다.