200805_TIL

oh_ji_0·2020년 8월 5일
1

TIL

목록 보기
8/61

Today I leared

  • Scope
  • Closure
  • Array [ Array methods ]
  • Private / Public
  • Class
  • Inheritance

[ Scope ]

  • 변수 접근 규칙에 따른 유효 범위 ( 변수와 그 값이 어디서 유효한지 판단하는 범위 )

  • 변수는 어떤 환경 내에서만 사용 가능하다.

  • 프로그래밍 언어는 모두 각각의 접근 규칙을 갖고 있다.

  • 자바스크립트는 함수가 선언되는 동시에 자신만의 스코프를 가진다

  • 선언도 되지 않는 변수를 조회하면 레퍼런스 에러가 나타난다

  • 스코프는 중첩이 가능하다

  • 글로벌 스코프는 최상단 스코프를 뜻한다

  • 글로벌 스코프에 선언된 전역 변수는 어디서든 접근이 가능하다.

  • 지역 변수는 함수 내에서 전역 변수보다 더 높은 우선 순위를 가진다.

[함수 스코프, 블록 스코프]

  • 블록: 중괄호로 시작하고 끝나는 단위

  • 블록스코프

    블록을 벗어나는 순간 변수에 접근할 수 없다

  • var 키워드

    함수 단위로 스코프를 가진다.

  • let 키워드

    블록 단위로 스코프를 가진다. 예측하기 쉬운 코드를 작성할 수 있다.

  • const 키워드

    값이 변하지 않는 변수, 상수를 정의할 때 사용한다

    블록스코프를 따른다.

    재할당을 하면 typeError 가 뜬다

  • window 객체

    글로벌 스코프에서 선언된 함수 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결

    let으로 선언한 변수나 함수는 window 객체에 담기지 않는다.

    전역 범위에 너무 많은 변수를 선언하지 않는다

  • 브라켓을 열고 let으로 변수를 설정하면 보다 더 안정적으로 예측 가능한 코드가 실행된다.

  • 선언 키워드가 없이 변수를 초기화하지 않는다.

    let과 var 는 재선언을 해도 오류가 출력되지 않는다.

    let 으로 선언한 것을 var 로 재선언 하거나, var 로 선언한것을 let 으로 재선언할 땐 에러가 출력된다.

    이런 실수를 방지하기 위해 Strict Mode를 사용한다. 'use strict'

    Strict 모드는 파일을 저장한 상태에서만 실행 가능하다.

//case 1
var x = originValue;
function outterScope(){
  var x = newValue;
  function innerScope(){
    x = newValue + 10;
  }
  innerScope();
}
outterScope();

//case 2
var x = originValue;
function outterScope(){
  x = newValue;
  function innerScope(){
    x = x + 10;
  }
  innerScope();
}
outterScope();
  • 함수 안의 함수에서 전역 변수에 대한 값을 재할당할 때와 지역변수가 선언될 때의 차이를 이해해야 한다.

  • case1 의 경우, 내부 함수에서 x 변수에 대한 값이 재할당되었지만 외부 함수에서의 x는 지역변수 이므로 전역변수에 값에 영향을 끼치지 못한다.

  • 그러나 case2 의 경우 외부함수에서의 x 는 전역변수 값에 재할당 한다.

var x = 0;
function outterScope(){
  x = 10;
  function innerScope(){
    x = x + 10;
    function innerInnerScope(){
      x = x +10;
    }
    innerInnerScope();
  }
  innerScope();
}
outterScope();
console.log(x); //30

//-----------------------------------------------------------------

var x = 0;
function outterScope(){
  x = 10;
  function innerScope(){
    x = x + 10;
    function innerInnerScope(){
      x = x +10;
    }
    innerInnerScope();
  }
  innerScope();
}
outterScope();
console.log(x); //30

//-----------------------------------------------------------------

var x = 0;
function outterScope(){
  x = 10;
  function innerScope(){
    x = x + 10;
    function innerInnerScope(){
      var x = x +10;
    }
    innerInnerScope();
  }
  innerScope();
}
outterScope(); 
console.log(x); //20
  • 내부의 내부 함수, 중첩 함수에서도 var 구문을 쓰지 않은 변수 할당은 전역 변수에 영향을 미친다

[ 클로저 ]

  • 외부 함수의 변수에 접근할 수 있는 내부 함수 또는 이런 작동 원리를 일컫는 용어.

  • 클로저 함수 안에서는 지역 변수 / 외부 함수의 변수 / 전역 변수의 접근이 전부 가능하다.

  • 함수 자체를 리턴할 수 있다.

  • 함수를 리턴하는 함수라고 다 클로저는 아니다. 단지 클로저의 일반적인 특성이 함수를 리턴할 뿐이다.

  • 함수명 자체를 리턴하면, 실행되지 않는 함수가 리턴된다.

  • 함수 표현식도 함수가 바로 실행된다.

  • 함수명()()

    함수명() 가 실행되고 리턴된 함수() 가 다시 실행된다.

  • 클로저 활용예시

    • 커링: 함수 하나가 n 개 인자를 받는 대신, n 개의 함수를 만들어 각각 인자를 받게하는 법.

      함수 생성 함수를 만들 수 있다.
      function adder(x){
        return function(y){
          return x + y;
        }
      }
      adder(x)(y);
      //함수생성. x 값을 고정해 사용할 수 있다.
      let addx = adder(x);
      addx(y); // x + y 

      클로저는 바깥에서 접근이 불가능하기 때문에 건드리지 않아야하는 코드, 수정해선 안되는 코드를 설정할 수 있고

      또한 클로저는 각각 독립적이기 때문에 서로의 값에 영향을 주지 않는다.

Koans Advanced

@@ Koans 자스민 유닛테스트 퀴즈를 풀며, 아직 안 나간 내용이지만 Array 메소드 심화 학습과 클래스와 상속에 대해서 미리 학습해보는 예습 시간을 가졌다.

새로 알게 되거나, 숙지해두면 좋을 내용을 다음과 같이 정리했다. 미리 구글링하며 찾아본 내용이라서, 또한 추측하면서 이해해 본 내용이라, 다소 맞지 않는 표현이 들어가 있을 가능성이 높다.

[ Array ]

  • arr.filter( 조건식 ) - immutable

    `조건식` 이 참인 것들만 반환.
    
    ```jsx
    const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
    const result = words.filter(word => word.length > 6);
    console.log(result);
    console.log(words);
    
    //(3) ["exuberant", "destruction", "present"]
    //(6) ["spray", "limit", "elite", "exuberant", "destruction", "present"]
    ```

     

  • arr.map() - immutable

    arr.map(callback(currentValue[, index[, array]])[, thisArg])

    배열의 각 요소에 대해 실행한 callback의 결과를 모은 새로운 배열.

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]

 

  • arr.reduce() - immutable

    arr.reduce(callback[, initialValue])

    callback(accumulator, currentValue, [currentIndex] , [array]);

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
  • 배열의 첫번째 요소(0번 인덱스)를 accumulator에 누적한 후 1번 인덱스부터 reducer를 거친다.

  • 만약 초깃값을 설정해주지 않을 경우, acc 0번째 인덱스를 초깃값으로 잡아버린다. 그리고 current 값을 1번째부터 돌린다. 그러므로 초깃값 설정에 주의해야한다.

  • 또한 reducer는 값을 반환해야 한다.

 

  • Object.value()

    전달된 파라미터 객체가 가지는 (열거 가능한) 속성의 값들로 이루어진 배열을 리턴

const object1 = {
  a: 'somestring',
  b: 42,
  c: false
};

console.log(Object.values(object1));
// expected output: Array ["somestring", 42, false]

 

  • arr.forEach() - immutable

    arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])

    결과값 리턴이 아닌 구문 실행 (reducer나 map과의 차이점)

const array1 = ['a', 'b', 'c'];

array1.forEach(element => console.log(element));

// expected output: "a"
// expected output: "b"
// expected output: "c"

 

  • arr.every() - immutable

    arr.every(callback[, thisArg])

    배열 안의 모든 요소가 주어진 판별 함수를 통과하는지 Boolean값 반환.

const isBelowThreshold = (currentValue) => currentValue < 40;

const array1 = [1, 30, 39, 29, 10, 13];

console.log(array1.every(isBelowThreshold));
// expected output: true

 

  • arr.some() - immutable

    arr.some(callback[, thisArg])

    배열 안의 하나의 요소라도 판별 함수를 통과하면 true 반환

const array = [1, 2, 3, 4, 5];

// checks whether an element is even
const even = (element) => element % 2 === 0;

console.log(array.some(even));
// expected output: true

  

[ private, public ]

  • 객체 선언 후, dot notation의 값 재할당. 값 변경 가능.

  • prototype로 만든 property 또한 값 변경 가능
    ( classFunction.prototype.funcName = value or function )

  • 클래스 내부의 프로퍼티를 재할당 할 수는 있지만, 클로저로 만든 프로퍼티 값을 바꾸는 것은 불가능하다.

 

[ class ]

  • 자바스크립트에는 클래스 개념이 없다. 대신 프로토타입이라는 것이 존재한다.

  • 자바스크립트는 클래스 기반 언어가 아닌 프로토타입 기반 언어다.

  • 클래스 기반 언어는 아니나 new 연산자를 사용하여 클래스를 흉내내어 사용 할 수 있다.

  • ECMA6 에선 class 문법이 추가되기도 했다.

  • class 선언

//클래스 선언
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

//클래스 표현식

// unnamed
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
//output : "Rectangle"

// named
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
//output: "Rectangle2"

 

[ Inheritance ]

  • Function.prototype.call()

    func.call(thisArg[, arg1[, arg2[, ...]]])

    주어진 this 값 및 각각 전달된 인수와 함께 함수를 호출

    call 외에도 bind(), apply() 등이 있다.

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"
  • 상속을 받으면 부모 클래스에서 필요한 인자값들을 담아서 call을 호출한다
  • 파라미터로 넣지 않은 속성값들도 상속된다

 

참고 - [JS 프로토타입] 프로토타입을 사용하여 상속하기

 

function SuperClass (name) {
  this.name = name;
}
SuperClass.prototype.say = function () {
  console.log(`I am ${this.name}`);
}

function SubClass (name) {
  SuperClass.call(this, name);
}

SubClass.prototype = Object.create(SuperClass.prototype);
//SubClass.prototype = new SuperClass();

SubClass.prototype.constructor = SubClass;

let sub1 = new SubClass('billy');
sub1.say() //I am billy
  • 새객체를 만들어 superClass의 프로토타입을 subClass 프로토타입에 할당

  • SubClass constructor를 SubClass로 지정

  • 같은 클래스 함수로 생성된 객체들은 prototype을 공유한다.

  • 상속할 때 자식 클래스의 prototype에 부모 클래스 함수를 통해 생성된 객체를 담아준다.

ChildClass.prototype = new ParentClass();
//ChildClass.prototype = Object.create(ParentClass.prototype);

 

Object.prototype.beget = function () {
  function F() { }
  F.prototype = this;
  console.log(this)
  return new F();
};

ChildClass.prototype = ParentClass.prototype.beget();
  • 위 코드에서 this는 Object를 가리킨다. 빈객체를 생성하여 Object의 프로토타입 객체를 담고 반환한다.

  • 부모 클래스에서 Object 프로토타입 메서드 beget 을 사용하여

    ParentClass.prototype.beget(); 을 실행하면 F 객체의 프로토타입 객체는 ParentClass 프로토타입 객체를 담게 되고, new 연산자 와 F 클래스 함수를 이용해 생성한 객체를 반환하므로 결국 childClass 프로토타입은 ParentClass 프로토타입 객체를 담게된다.

 

[참고]

상속과 프로토타입
[Javascript ] 프로토타입 이해하기

[Comment]

@@ 오늘은 정신없이 아침부터 Lesson, Socrative, Javascript Koans Advanced를 번갈아가면서 풀고, 정리했다.

스코프와 클로저의 개념을 배웠는데 독학때도 항상 여기부터 막혔던 기억이라 처음부터 살짝은 겁 먹고 들어갔던 것 같다. 그래도 socrative 진행하며 내가 모르는 부분을 살펴볼 수 있었다. 함수 안의 함수 내부에 변수를 선언할 때 전역 변수를 선언하면 정말 window 전역까지 그 값이 전달되는지, 하나의 함수 바깥으로만 값이 꺼내지는 게 아닌지 스스로 헷갈려하고 있었고, 비로소 스코프에 대해서 보다 더 잘 이해하게 된 것 같다.

클로저는 아직도 많이 헷갈린다. 그래도 외부 함수 값에 접근할 수 있는 내부함수. 이 개념만은 마음에 새겼다. 단순히 함수를 리턴하는 게 클로저 아닌가 해서 문제를 틀린 게 좀 아쉽지만, 틀려야 더 확실히 머리에 각인되긴 하는 것 같다.

Koans Advanced 시간엔 reduce 문제에 막혀서 정말 한참 헤맨 것 같다@@... 한 문제에 한 시간 넘게 메여있었는데, 역시 mdn 문서를 정독하며 살펴보는 게 가장 빠른 지름길 같다. 어쨌든 헤매면서 알게 된건, reduce 함수에 초깃값을 설정하지 않는 단순한 실수였고, reduce는 초깃값 설정하지 않으면 accumulator값에 배열 0번 요소를 담고, 1번 요소부터 currentValue 값을 대입한다는 거다. 예상한 값이 안나와서 콘솔로 계속 찍어 배열이 왜 1번부터 도는지 계속 고민하다가, 구글링으로 해답을 찾았다.

[참고]

배열의 reduce() 파헤치기 - https://sustainable-dev.tistory.com/38

profile
기본에 충실하고 싶습니다. #Front-end-developer

0개의 댓글