카테고리 없음

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);
  });
};
 
async function order() {
  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의 단계 전부를 보여준다.

// 제네레이터는 써볼날이 올지 모르겄다 하하하

//오늘은 자바스크립트에서 어려운 개념을 다뤄봤는데 몇번 더 로직을 써보면서 이해 해봐야 겠다. 수고링