[javascript] javascript boot camp 16기 다섯번째 수업

2020-09-02

자바스크립트 부트캠프 16기 다섯번째 수업

코로나시대때문에 비대면강의를 진행한다ㅠㅠ zoom으로 비대면강의 진행

객체 기본부터 https://fastcampus-js-bootcamp.herokuapp.com/grammar/object

메소드

  function calcArea(r) {
    return r * r * Math.PI;
  }

  let circle = {
    radius: 200,
    backgroundColor: 'blue',
    area: function() {
      return this.radius * this.radius * Math.PI;
    }
  };

  let area1 = calcArea(circle.radius);
  let area2 = circle.area();

  console.log(area1, area2);

어디서 표현력의 차이가 있을까?

calcArea(); 은 어떤 원의 크기든 구할 수 있는 방법 circle.area(); 객체가 자기자신의 면적을 스스로 구하는 방법임

calcRectArea(rect.width, rect.height); 사용하는 사람의 관점 - 사각형의 면적을 구하기 위해 사전에 calcRectArea라는 함수의 개념을 알아야함.

객체의 메소드 방법 (circle.area() / rect.area())은 사용자관점에서 스스로 면적을 구하는 공식을 손쉽게 사용할 수 있다.

getter, setter

의도하지않은 값이 들어왔을때 자바스크립트에서 막을 방법이 없음 circle.radius = ‘100’;

객체 내부에서는 함수로 작성하고 바깥에서 변수처럼 사용할 수 있는게 getter, setter

let circle = {
  A: 10,
  get a(){
    return this.A;
  }, // 2. 함수 앞에 get 
  set b(x){
    if(typeof x !== number) return;
    this.A = x;
  }, // 4. 함수 앞에 set
  _radius: 200,
  backgroundColor: 'blue',
  get area() {
    return this.radius * this.radius * Math.PI;
  },
  get radius() {
    return this._radius;
  },
  set radius(r) {
    if (typeof r === 'number') {
      this._radius = r;
    }
  }
};

let area = circle.area;

circle.radius = '100';

console.log(circle);

circle.a * 10; // 1. 사용자한테 함순지 변순지 안알려주고싶고.. 그럼 앞에 get 하면댐
circle.b = 10; // 3. 넣을때 set.. 

값을 바깥쪽에서 읽을 때 get

왜 이렇게 쓰는걸까? 꼭 숫자를 들어가야할때.

내부용일때는 앞에 _를 붙힘 getter 와 setter는 앞에 _가 없음 자바스크립트가 _radius 라는 속성값을 외부에 노출안되게할수있는 방법이 없어서 잘 안쓴다고 한다~ 저장소에 접근하는 방법을 막을 수 없어서 ㅎㅎ 딱히 그렇게 많이 쓰이지 않습니다.

지금은 앞에 #붙히는 방법 등으로 연구개발하고 있는데 내년 등재 예상

값의 참조 & 복사


A = 10; // 복사
B = {}; // 참조 {}은 300의 위치값에 있다. 300값 참조하기

a = {x: 10}; // 500번재의 위치값의 객체 참조
b = a; // 복사

b.x = 50; // console.log(50)
a.x; // 50 나옴 왜냐면 같은 500번째의 객체를 참조하고있기때문에.. 객체의 양은 늘어나지 않음.

기본형 값은 값이 이동할 때 복사된다 객체는 값의 이동시 복사되지 않고 참조된다. (상단에 표기한 300, 500번째는 예시..)


// 복사

let x1 = 10;
let y1 = 10;

let x2 = x1;
let y2 = y1;

console.log(x1, x2);

x2 = 100;

console.log(x1, x2);

// x1, x2는 서로 연관이 없고 각각 개별의 값. 복사돼기때문에

// 참조

let x1 = { value: 10 };
let y1 = { value: 10 };

let x2 = x1;
let y2 = y1;

x2.value = 100;

console.log(x1);

객체는 아무리 변수를 많이 옮겨다녀도 하나만 존재한다.

function double(v, o) {
  v = v * 2;
  o.value = o.value * 2;
}

let p1 = 100;
let p2 = { value: 100 };

double(p1, p2);

console.log(p1, p2);

// p1은 복사가 일어났고
// p2는 참조가 일어남

function double(v, o) {
  v = v * 2;
  o = {value: 10};
}

let p1 = 100;
let p2 = { value: 100 };

double(p1, p2);

console.log(p1, p2);

// 위치값이 들어가는데 o의 새로 만든 객체는 안바껴용

라이프사이클

어떤 자원을 만들었을때 더이상 쓰이지않을 때 제거하는 매카니즘 필요 (왜냐? 컴퓨터의 자원은, 저장공간은 유한하기 때문에 최대한 적은 저장공간을 활용하여 효율적으로 코드를 짜야한다)

자바스크립트에서는 자원을 생성하고, 사용하고, 소멸하는 유효범위를 스코프라고 이해하면 됌

자바스크립트 5.0까지는 스코프를 만들어내는 방법은 함수의 블록만이 스코프를 만들었다. 함수의 블록은 너무 넓어서 유효범위를 좀 더 작게 잡을 필요가 있어 만든게 블록 스코프 조건문이나 반복문의 블록들도 스코프를 생성함 (local scope)

함수스코프와 블록스코프는 지역스코프라고 함 특정한 지점에서 생성하고 소멸시키는..

전역스코프는 프로그램이 생성돼고 꺼질때 언제 어디서나 있는 스코프~

스코프의 특징 1. 중첩이 가능하다.

var title = '전역 스코프';

function displayTitle() {
  var title = '함수 스코프';

  if (title) {
    let title = '블럭 스코프';

    console.log(title);
  } // 조건문안의 블럭 스코프도 유효해졌음
  
  console.log(title);
}

displayTitle();
console.log(title);

기존에 함수 스코프가 있었는데 블럭 스코프는 왜 생겼는지!?

var days = ['','','']; // 배열을 순회할 때 위치값이 필요..
// var라는 키워드는 함수스코프 또는 전역스코프에 변수를 만드는 명령어

for(var i=0; i<days.length; i++) {
  console.log(days[i]); // var i 는 전역스코프에 만들게 됌. 
}

console.log(i); // 그래서 i가 전역스코프에 존재하게됌. 자원을 효과적으로 쓸 수 없음.

for(let j=0; j<days.length; j++) {
  console.log(days[j]); 
  // var의 전역스코프에 뿌려지는 매카니즘 방지하고 자원을 효과적으로 관리할 수 있음
}

console.log(j); // let 이라는 명령어는 블록스코프에 유효범위가 존재함
function displayPerson() {
  console.log(`name : ${name}`);
  console.log(`age : ${age}`);
  
  var name = '홍길동'; // undefined 
  // 함수스코프가 만들어진 다음에 함수안에 있는 코드를 스캔한 담에
  // var로 만드는 변수가 있으면 var name; 이렇게 만들어버림 (호이스팅 = 끌어올림)

  let age = 25;
  // 
}

displayPerson();

var와 let, 호이스팅 개념은 확실히 알고있어야함 반복문내에서 호이스팅

ㄴ> 숙제로 하면 좋을듯

클로저

function makeUnit(unit) { // 생성시 생성시점
  return function(value) {
    return `${value}${unit}`;
  }
} // 참조 가능한 알려진 변수 = 스코프

let px = makeUnit('px');
let em = makeUnit('em');

console.log(px(11));
console.log(px(24));
console.log(em(32));

예외적으로 함수는 생성시 생성시점에 참조 가능한 알려진 변수들을 캡쳐하는 능력을 가집니다. 매개변수 unit에 들어온 값(‘px’)을 캡쳐해서 function(value){ return ${value}${unit}; } 라는 공간이 새로 생성(클로저)돼서 호출할 수 있음

클로저는 충분히 숙지하지않으면 미묘한 동작들을 만들어낼 수 있다.. (버그) 프레임워크에는 이미 다 탑재돼있어서 사실 진짜 클로저를 실무적으로 쓸일이 없다보니깐 잘모르지만 굉장히 중요한 스펙 중 하나

내부에 실제 존재하는 값을 아무리 _로 써도 접근할 수 있기때문에.. 클로저를 쓰면 private 객체를 만들 수 있음

클로저를 만드는 유일한 방법 (선제조건)

  • 함수생성.

라이브러리 만들때는 클로저가 필요한데 웹앱, 비즈니스 어플리케이션 만들때는 고급테크닉이라 자주는 안씀

컨텍스트

this객체는 컨텍스트 객체라고 한다.

  1. this는 실행 환경을 소유한 소유자 객체에 연결(바인딩)된다.

  2. 전역 실행 환경에서 this는 전역 객체(브라우저인 경우 window)를 연결한다.

  this;
  // 전역객체 window
  1. ★★ 객체 내의 this는 소유자 객체가 확인될 때 소유자 객체를 연결한다.
let counter = {
  count: 0,
  increment() {
    this.count++;

    // 이 this는 "소유자 객체가 확인될 때"
    // increment() 메소드를 소유하고 있는 객체는 counter.
    // 소유자 확인은 실행하는 순간에 함.
    // counter의 increment() 메소드를 호출할 때 소유자 확인을 함.
  }
};

counter.increment();

console.log(counter.count); // 일때는 1이 됌
// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

// 변수를 만들어서담을때
let inc = counter.increment;

counter.increment();
inc(); // 실행하는 시점에 소유자가 등장하지않기때문에, 소유자를 모르니 count++ 안댐
// 즉, 실행 맥락상 this가 달라진다..
// 콜백함수를 실행해야할 때 이런 방법을 사용해서 할수밖에 없다

console.log(counter.count);
// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
function foo(fn){
  fn();
}

foo(counter.increment);
// 이런식으로하면 this를 잊어버리는 일이 비일비재하다.
// 그래서 this를 원하는대로 조작하거나 보정하는 방법을 배워야한다.
  1. new 연산자로 실행된 함수 안에서 this는 새로운 생성 객체를 연결한다.

  2. 화살표 함수 내에서 this는 화살표 함수 실행 맥락을 연결한다.

  3. call, apply로 호출된 함수 안에서 this는 call, apply의 첫 번째 인자로 지정된 객체로 연결된다.
    • 자바스크립트에서 함수호출하는 방법
  4. inc();

  5. inc.call();

  6. inc.apply();
// this를 내가 원하는 형태로 조작하는 방법 함수명.call() / 함수명.apply()
counter.increment();
let inc = counter.increment;
let yourCounter = {count: 10};

inc();
inc.call(counter); // 이렇게 첫번째 인자에
inc.apply(yourCounter); // this를 내가 원하는 객체로 지정할 수 있다.

console.log(counter.count);

// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

let counter = {count: 0};
let yourCounter = {count: 10};
let arg = [10, 20];

function increment(){
  this.count++;
}

function increment02(x,y){
  this.count = this.count + 1 + x + y;
}

increment.call(counter);
increment.call(yourCounter);

increment02.call(counter, 10, 20); // 하드코딩
increment02.apply(counter, [10, 20]); // 배열이면 코드가 아니고 외부로부터 데이터를 받을 때.. 

increment02.apply(counter, arg); // 이렇게 외부로부터 배열형태로 받게돼면 전달하는 방식이 apply 

//call 과 apply는 똑같음.
//전달인자의 형태에 따라 다름
  1. bind로 컨텍스트를 명시한 함수 내의 this는 명시한 컨텍스트로 고정된다.
counter.increment = counter.increment.bind(counter);
// increment 메소드에 this가 가르치고있는 객체를 counter로 고정하고싶으면 bind로 묶음

counter.increment();
let inc = counter.increment;

inc();
inc();
inc();

console.log(counter.count);

6과 7 명시적으로 바꾸는거 암묵적으로 바꾸는건 1~5.. 케이스바이케이스로 this가 달라진다. 예외가 많고 경우의 수가 많아서 복잡함