카테고리 없음
24/1/3 자바스크립트 문법(2)
한지지우우
2024. 1. 3. 21:02
<클로저>
let one;
one = 1;
function addOne(num) {
console.log(one + num) //지역스코프에서 one을 찾아보고 없으므로 전역에서 one을 찾는다.
}
addOne(5); // 6
function makeAdder(x) { //함수를 리턴하는 함수 이다.
return function (y) {
return x + y;
};
}
const add3 = makeAdder(3); // x값을 설정해준다.
add3(2); //5 //add3에서는 여전히 makeAdder의 x에 접근할수 있다. makeAdder(3)은 은닉화하여 add3으로 밖에
//접근할수 없다.const add10 = makeAdder(10) //동일한 함수에 다른값을 넣어본다.
add10(5) // 15 // 동일 makeAdder함수를 썻지만 값이 다르다. 서로 간섭하지 않는다.
add3(1) //4 //은닉화된 함수에 간섭할수 있는것은 리턴된 함수 뿐이다.
<setTimeout 과 setInterval >
function fn(name) {
console.log(name);
}
const tid = setTimeout(fn, 3000, "mike"); // 이렇게 실행하면 첫번째 인자가 함수 두번째가 딜레이된 시간 3번째가
//함수에 넣을 인자이다. 3초뒤에 실행된다.
clearTimeout(tid); // 해당 타임아웃을 실행되기 전에 미리 끝내고 싶을때 이 로직을 쓰면 된다.
let num=0
function showName(name) { //setInterval을 일정 시간 간격으로 함수를 반복 실행한다.
console.log(name);
num++; //함수가 1초마다 실행되고 num이 5가 되었을때
if (num > 5) {
clearInterval(tid2); //이 로직으로 함수가 종료 된다.
}
}
const tid2 = setInterval(showName, 1000, "tom"); //함수를 첫번째 매개변수로 두번째는 시간의 갭 , 세번째는
//함수에 들어가는 인자이다.
<this와 관련된 call , apply ,bind> 를 알아보자
const mike = {
name: "mike",
};
const tom = {
name: "tom",
};
function showThisName(birth, job) { //요렇게 디스를 받을 함수를 만들고
console.log(this.name);
this.birth = birth;
this.job = job;
}
showThisName(); // 이렇게 호출하면 아무것도 안준다. 이때의 this는 window
//window.name 은 빈문자열이다.
//콜을 쓰면 첫번째 매개 변수 를 this로 사용하고 앞의 함수를 매서드처럼 쓴다.
showThisName.call(mike, 2000, "singer"); //두번쨰 인자부터 함수의 매개변수가 됨 이제 마이크는
showThisName.call(tom, 2010, "teacher"); //birth와 job을 갖는 객체가 되는거임
showThisName.apply(mike, [2005, "doctor"]); // apply는 call 과 다 똑같고 다만 함수의 매개변수로 array 형태를 받음
showThisName.apply(tom, [2015, "prince"]);
const showMike = showThisName.bind(mike); //바인드는 this를 mike로 고정해버리는 새 함수를 만들어냄
showMike(1500, "housekeepper"); //{ name: 'mike', birth: 1500, job: 'housekeepper' } 요래하면 이렇게 뜸
const user = {
name: "jane", //한번더 해보자
showme: function () {
console.log(this.name); 이때 this는 user 다
},
};
user.showme(); //이러면 user가 this 이므로 jane이 나온다.
let fn = user.showme; // 이러면 fn의 콘솔값은 없다 this를 잃는다.
fn.call(user); //요래 user를 this로 해주면 된다.
fn.apply(user);
let boundFn = fn.bind(user); //유저를 고정으로 this로 하는 새함수를 만들자
boundFn(); //이렇게 호출하면 this가 user인 showme()가 호출된다.
<상속 프로토타입 >
const user = {
name: "james",
};
user.name; // 당연히 james가 나온다.
user.hasOwnProperty("name"); //true 요매서드는 user가 매개변수를 속성으로 갖는지 알려준다.
//hasOwnProperty라는 매서드는 설정한적이 없는데 어디서 온걸까? //각 객체는 __proto__라는 객체를 갖는데 객체에 해당 속성이 없으면
//여기서 찾아본다.
//여기서 찾아본다.
const car = { //요래 해보자
wheels: 4,
drive() {
console.log("drive");
},
};
const bmw = {
color: "red",
navigation: 1,
};
bmw.__proto__ = car; //이렇게 하면 bmw는 car를 상속하는거고 car가 bmw의 프로토타입이 되는것이다.
//이러면 bmw.wheels 를 콘솔로 찍으면 4가 나온다. bmw객체에서 wheels를 찾아보고 //없으니까 프로토타입인 car에서 찾아보고 반환한다.
const x5 = {
color: "white",
name: "x5",
};
x5.__proto__ = bmw; //이하동문
생성자 함수를 사용해서 응용해보자
const Bmw = function(color){ //생성자 함수 비엠떠블유다
this.color = color
}
Bmw.prototype.wheels=4 //프로토타입으로 키와 벨류를 넣는 방식은 이렇게도 쓸수 있다.
Bmw.prototype.drive = function(){ //특정 객체를 상속하는게 아니라 직접 넣어준다.
console.log('drive')
}
const x5 = new Bmw('red') //이렇게 생성한 x5와 z4는 프로토타입 객체에 wheels와 drivw를 갖는다.
const z4 = new Bmw('blue')
const Audi = function (color) { //클로저를 사용해 응용해보자.
const c = color;
this.getColor = function () { //요렇게 해버리면 getColor안에서는 c가 원래 color를 기억하고 있게 된다.
console.log(c);
};
};
const brother = new Audi("white"); //브라더는 아우디의 프로토 타입이기 때문에 color를 가질텐데
//brother.color를 재할당해버리면 color가 바뀐다.
brother.getColor();
<class> 그동한 반복적이고 비슷한 객체를 뽑기위해 생성자 함수를 썻지만 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); //이렇게 쓰면 큰문제가 없지만
const mike = User("mike", 30); //만일 new를 빼먹은다면? 그냥 함수로 사용되서 undefined가 할당된다.//개발자실수
class User2 { //요게 class문법
constructor(name, age) { //단순한 애들은 요래
(this.name = name), (this.age = age);
}
showName() {
console.log(this.name); //매서드는 요래 써준다. //매서드는 prototype의 속성이 된다. 함수생성자랑 다른점
}
}
const tom = new User2("tom", 19); //class의 경우 new를 빼먹는순간 error라고 알려준다.
<class의 상속>
class Car { // Bmw의 프로토타입인 car
constructor(color) {
this.color = color;
this.wheels = 4;
}
drive() {
console.log("drive");
}
stop() {
console.log("stop");
}
}
class Bmw extends Car { //extends라는 것을 써서 car라는 class를 상속받는다.
constructor(color) { //매개변수로 color를 받을텐데 프로토타입이 car에서 color가 필요하므로
super(color); //super를 통해 인자로 전달해준다. // 전달할게 없다면 super는 필요 없다.
this.navigation = 1;
}
park() {
console.log("park"); // 동일한 매서드 변수명이 있다면 BMW에 있는 매서드가 호출된다.
}
}
const z4 = new Bmw("red"); //여기서 red를 넘긴다.
<promise>를 알아봅세다.
const pr = new Promise((resolve, reject) => { //promise 는 결과 값을 추후에 받는 함수이다.
setTimeout(() => { //안에 resolve랑 reject 는 콜백함수로 promise가 실행되고 수행된다.
resolve(ok); //첫번째 매개변수인 resolve는 성공했을때 로직이다.
//reject(new Error("err다")); //두번째 매개변수인 실패했을때쓰이는 reject 로직이다.
}, 1000);
});
console.log("시작");
pr.then((result) => { //promise가 완료되고 다음작업 수행은 then을 쓴다.
console.log(result); //결과를 찍어준다.
})
.catch((err) => { //catch는 에러작업을 수행할시 사용한다.
console.log(err);
})
.finally(() => { //fulfilled되든 rejected되든 무조건 수행되는 finally이다 로직이 작동했는지 확인용도이다.
console.log("끝");
});
프로미스가 연속될때는 어떻게 될까?
const f1 = () => {
return new Promise((res, rej) => { //1초의 시간이 걸리는 프로미스
setTimeout(() => {
res("1번주문완료");
}, 1000);
});
};
const f2 = (res) => {
console.log(res);
return new Promise((res, rej) => {
setTimeout(() => {
res("2번주문완료");
}, 2000);
});
};
const f3 = (res) => {
console.log(res);
return new Promise((res, rej) => {
setTimeout(() => {
res("3번주문완료");
}, 3000);
});
};
console.log("시작"); //시작을 콘솔로 찍어주고
f1()
.then((res) => f2(res)) //f1()이 시작되고 그 return 값을 f2()에주고
.then((res) => f3(res)) //f2()의 return을 f3()에 넘겨준다.
.then((res) => console.log(res)) //f3()까지 정상적으로 완료되면 콘솔에 결과를 띄운다.
.catch(() => {
console.log("에러"); //이 실행 과정중 에러가 발생하면 에러를 찍어준다.
})
.finally(() => {
console.log("끝"); //catch문과 finally문에 콜백함수 형식이 아닌 그냥 콘솔을 찍으면 안된다.
}); //프로미스 연쇄작업중 중간에서 에러가 나면 거기서 실행이 종료된다.
//각각의 프로미스가 1초 2초 3초가 걸리므로 총 6초가 소요된다.Promise.all([f1(), f2(), f3()]).then((res) => console.log(res)); //promise.all은 모든프로미스를 한번에 수행
//가장늦은 프로미스가 3초가 걸리므로 총 3초가 걸린다.//하나라도 에러가 발생하면 모든 프로미스가 화면을 그려주지 않는다.
Promise.race([f1(), f2(), f3()]).then((res) => console.log(res));
//promise.race 는 가장 빠르게 프로미스과 fulfilled 되면 다른 프로미스는
//안보고 완결된다. 총 1초가 걸리게 된다.
<async ,await> promise 를 다루는 문법으로 then문법보다 가독서이 좋다.
async function getName() { //함수 앞에 async가 붙으면 이 함수는 promise를 반환한다.
// return 'mike'
return Promise.resolve("tom");
// throw new Error("err");
}
console.log(getName()); // 프로미스로 결과값이 나온다.
getName().then((name) => {
console.log(name); //then을 사용하여 결과값을 네임으로 받으면 tom이 출력된다.
});
async function getName() {
// return 'mike'
// return Promise.resolve("tom");
throw new Error("err"); //에러경우일때 로직
}
console.log(getName());
getName().catch((err) => {
console.log(err); //에러이므로 catch문에서 받아지며 err가 출력된다.
});
function getName(name){
return new Promise((res ,rej)=>{ //프로미스 함수를 결과값으로 받는 함수
setTimeout(() => {
res(name)
}, 1000);
})
}
async function showName(){
const result = await getName('mike') //await 는 async 함수 내부에서만 존재할수 있다.
console.log(result) //getName을 1초 기다렸다가 result를 받는다.
}
showName() //1초후에 마이크가 찍힌다.
const f1 = () => {
return new Promise((res, rej) => { //위에서 했던 then 대신에 async await 를 이용해 보자
setTimeout(() => {
res("1번주문완료");
}, 1000);
});
};
const f2 = (res) => {
console.log(res);
return new Promise((res, rej) => {
setTimeout(() => {
res("2번주문완료");
}, 2000);
});
};
const f3 = (res) => {
console.log(res);
return new Promise((res, rej) => {
setTimeout(() => {
res("3번주문완료");
}, 3000);
});
};
try {
const result1 = await f1(); //then과 catch가 아닌 try catch를 사용하면 된다.
const result2 = await f2(result1); //각각의 await를 기다려서 그결과값을 다음 프로미스에 넣어준다.
const result3 = await f3(result2);
console.log(result3); //마지막 콘솔 찍고 끝
} catch (error) {
console.log(error); //에러발생시 에러를 콘솔에 띄어준다.
}
}
order(); // 훨씬 가독성이 좋아졌다.
<generator> 솔직히 이거 어따 쓸지 모르겄다. 일단 기본만
function* fn() { //generator 는 함수 function뒤에 *를 하는데 함수 내부에 yield까지 단계대로 진행한다고 한다.
yield 1;
yield 2;
yield 3;
return "finish";
}
const a = fn();
console.log(a.next()); //a.next()로 단계적으로 작동 시킨다.
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
//{ value: 1, done: false } yield 옆에 값을 value로 얻고 완료됐는지 여부를 done으로 확인하는 객체가 출력된다.//{ value: 2, done: false }
//{ value: 3, done: false }
//{ value: 'finish', done: true } 4번째 수행때 완료된다.
//{ value: undefined, done: true } 더이상 수행되지 않고 value는 없다.
console.log(a.next());
console.log(a.next());
console.log(a.return("end")); //return이라는 매서드도 가지고 있는데 generator를 바로 종료시킨다.
console.log(a.next());
console.log(a.next());
//{ value: 1, done: false }//{ value: 2, done: false }
//{ value: 'end', done: true } //return 매서드의 인자를 value로 갖고 바로 종료된다.
//{ value: undefined, done: true } //그다음 next()는 실행되지 않는다.
//{ value: undefined, done: true }
function* fn() { //에러가 발생할경우를 보자
try {
yield 1;
yield 2;
yield 3;
return "finish";
} catch (error) {
console.log(error); //에러를 보여주기 위해 catch를 만들어준다.
}
}
const a = fn();
console.log(a.next());
console.log(a.next());
console.log(a.throw(new Error("err"))); // 이곳에서 throw()를 통해 에러를 발생시킨다.
console.log(a.next());
console.log(a.next());
{ value: 1, done: false }{ value: 2, done: false }
Error: err
at Object.<anonymous> (C:\Users\a\Desktop\연습연습연습연습\ex.js:488:21)
at Module._compile (node:internal/modules/cjs/loader:1376:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
{ value: undefined, done: true } //바로 완료되고 값을 반환 받지 못한다.
{ value: undefined, done: true }
{ value: undefined, done: true }
제네레이터를 이용하여 외부에서 값을 받는 용도로 써보자
function* fn() {
const num1 = yield "첫번째 숫자 입력하세요";
console.log(num1);
const num2 = yield "두번째 숫자 입력하세요";
console.log(num2);
return num1 + num2;
}
const a = fn();
console.log(a.next());
console.log(a.next(2)); //여기에 인자로 2넣고
console.log(a.next(3)); //인자로 3넣는다.
console.log(a.next());
//{ value: '첫번째 숫자 입력하세요', done: false } //클라언트에게 value값을 보여주게 하고 num1입력받는다.//2
//{ value: '두번째 숫자 입력하세요', done: false } // 클라이언트에게 value를 보여주고 num2를 입력받는다.
3
//{ value: 5, done: true } //합친결과를 value로 반환하고 종료된다.
//{ value: undefined, done: true }
function* fn() {
let index = 0;
while (true) { //while의 조건을 true로 하는것은 매우 위험하지만 generator에서는 단계적으로 작동하므로
yield index++; //문제가 없다.
}
}
const a = fn();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
//{ value: 0, done: false } index가 올라가므로 value가 계속 올라간다. 조건부로 return 문을 달아서 종료시킬수 있다.//{ value: 1, done: false }
//{ value: 2, done: false }
//{ value: 3, done: false }
제네레이터 안에 제네레이터가 올수 있다.
function* gen1() {
yield "w";
yield "o";
yield "r";
yield "l";
yield "d";
}
function* gen2() {
yield "Hello";
yield* gen1(); //이런식으로 사용해서 generator in generator를 할수 있다 반복가능 객체를 여기다 너도 된단다.
yield "!"; //뭔소린지
}
console.log(...gen2()); //Hello w o r l d ! 가 출력된다. 이때 스프레드 연산자는 generator의 단계 전부를 보여준다.
// 제네레이터는 써볼날이 올지 모르겄다 하하하
//오늘은 자바스크립트에서 어려운 개념을 다뤄봤는데 몇번 더 로직을 써보면서 이해 해봐야 겠다. 수고링