💻/BOOK

Inside JavaScript - 내용 정리(1)

김씨리 2021. 1. 27. 23:40

 

1. 자바스크립트 기본 개요

 

활용 범위 : 웹, 서버, 애플리케이션

핵심 개념 :

1) boolean, number, string, null, undefined 제외 나머지는 모두 객체. (boolean, number, string은 객체처럼 다룰 수도 있음)
2) 함수도 객체로 취급. First class object.
3) 모든 객체는 숨겨진 링크인 프로토타입 가짐. 해당 객체를 생성한 생성자의 프로토타입 객체를 가리킴.
4) 자신만의 유효 범위를 갖는 실행 컨텍스트를 만들고 그 안에서 실행이 이루어짐. 이 과정에서 클로저 구현 가능.

 

 

 

 

3. 자바스크립트 데이터 타입과 연산자

 

기본 타입 :

  •  number, string, boolean, null, undefined
  • 그 자체가 하나의 값을 나타냄.
  • 정수, 실수 구분없이 하나의 숫자형 number만 존재. 모든 숫자를 64비트 부동 소수점 형태로 저장. C의 double과 비슷. 나눗셈 시 주의!!
  • '', "" 구분 없음. 한 번 정의된 문자열은 변하지 않음. 수정 불가.
  • null VS. undefined. 기본적으로 값이 할당 안된 변수는 undefined 타입이고, undefined 타입의 변수는 그 자체의 값도 undefined. undefined는 타입이자 값.
  • null은 명시적으로 값이 비어있음을 나타내는 데 사용. null 타입 변수의 typeof 결과는 object로 틀리게 나옴. 그래서 typeof 대신 ===로 확인 필요.
  • 기본 타입에서 표준 메소드 부르는 경우에는 처리 순간에 객체로 변환된 후, 타입별 표준 메소드 호출. (toExponential(), charAt() 등)

 

 

참조 타입(객체 타입) :

  • 기본 타입 제외한 모든 값은 객체. 배열, 함수, 정규표현식 등도 자바스크립트 객체로 표현됨.
  • key:value 형태. 해시와 유사.
  • 참조 타입인 객체는 여러 개의 Properties을 포함 가능. 각 Property는 기본 타입의 값 포함하거나, 다른 객체를 가리킬 수도 있음. Property 성질에 따라 함수로 포함할 수 있고, 이를 Method라고 부름.
  • Java처럼 클래스 개념 없음. 객체 생성 방법; 1) Object() 객체 생성자 함수 2) 객체 리터럴 3) 생성자 함수
  • Property 접근 방법; 1) 대괄호([]) 접근법 2) 마침표(.) 접근법
  • [] 연산자 내에 숫자가 사용될 경우, toString()을 통해 해당 숫자를 자동으로 문자열 형태로 바꿔줌.
  • 삭제 시에는 delete 연산자로 객체의 property만 삭제 가능. 객체 자체는 삭제 못함.
  • call by reference 방식으로 동작. call by value처럼 복사된 값이 아닌 객체의 참조값이 전달됨.

 

 

프로토타입 :

  • 객체지향의 상속 개념과 같이 마치 자신의 것처럼 쓸 수 있는 부모 객체.
  • 모든 객체는 자신의 프로토타입을 가리키는 [[Prototype]]라는 숨겨진 property를 가짐.
  • .toString(), .valueOf() 등과 같은 모든 객체에서 호출 가능한 기본 내장 메소드가 포함되어 있다.

 

 

배열 :

  • C, Java와 달리 크기 지정 안해도 되고 어떤 위치에 어느 타입의 데이터를 저장하더라도 에러가 발생하지 않음.
  • 배열 리터럴은 대괄호([]) 사용.
  • 객체 리터럴처럼 property 이름과 값을 key:value로 표기하지 않고, 각 요소의 값만 포함. 위치 인덱스값으로 접근.
  • 모든 배열은 length property가 있음. 가장 큰 인덱스값 + 1
  • .length 값이 100이라 해도 실제 메모리는 그만큼 할당되진 않음.
  • 임의로 length 값을 변경하면 길이 증가 시 뒤쪽에 undefined로 늘어나고, 길이 감소 시 뒤쪽부터 벗어나는 값을 삭제.
  • 배열과 객체의 차이점
    • 객체는 length property가 존재하지 않기 때문에 undefined로 출력.
    • 객체는 push()와 같은 표준 배열 메소드를 사용할 수 없음. 배열과 객체의 프로토타입 객체가 서로 다르기 때문.
    • 객체는 for in 문으로 property 열거. 배열은 for in 문을 쓰면 불필요한 property가 출력될 수 있으므로 되도록 for문 사용.
  • length property는 배열 원소의 가장 큰 인덱스가 변했을 경우만 변경됨. 아래 코드의 경우 color, name은 배열의 모든 properties를 출력했을 때에 확인 가능.
  var arr = ['zero', 'one', 'two'];
  console.log(arr.length); //3

  arr.color = 'blue';
  arr.name = 'number_array';
  console.log(arr.length); //3

  arr[3] = 'red';
  console.log(arr.length); //4

 

 

  • delete로 배열의 요소를 삭제할 수 있지만 그 자리가 빠지는 게 아니라 undefined가 할당. 완전히 삭제할 경우 splice() 메소드를 사용.
  • Array() 생성자 함수
    • 호출할 때 인자가 1개이고 숫자일 때, 그 숫자를 length로 갖는 빈 배열 생성. 그 외에는 인자들을 요소로 갖는 배열 생성.
var foo = new Array(3);
console.log(foo); //[undefined, undefined, undefined]
console.log(foo.length); //3

var bar = new Array(1, 2, 3);
console.log(bar); //[1, 2, 3]
console.log(bar.length); //3
  • 유사 배열 객체
    • 배열의 length property와 같은 property가 일반 객체에 있으면 유사 배열 객체라고 함.
    • 객체임에도, apply() 메소드를 사용하면 표준 배열 메소드 사용이 가능.

 

 

연산자 :

  • ==(동등), ===(일치). == 연산자는 비교하려는 피연산자의 타입이 다를 경우 타입 변환을 거치고 비교한다. 반면 === 연산자는 타입이 다를 경우 타입 변경 없음.
console.log(1 == '1'); //true
console.log(1 === '1'); //false

 

  • !! 연산자는 피연산자를 boolean값으로 변환.
console.log(!!0); //false
console.log(!!'string'); //true
console.log(!!true); //true
console.log(!!{}); //true
console.log(!!undefined); //false
console.log(!!null); //false

 

 

 

 

 

4. 함수와 프로토타입 체이닝

 

함수 생성하는 3가지 방식:

  • 함수 선언문. 함수명 정의 필수. 함수 리터럴 형태와 같다.
function add(x, y){
  return x+y;
}
console.log(add(3, 4)); //7

 

  • 함수 표현식. 함수 리터럴로 하나의 함수를 만들고, 이를 변수에 할당.
    • add가 참조하는 덧셈 함수는 익명 함수. 반대로 이름이 포함된 함수 표현식은 기명 함수 표현식.
var add = function (x, y){
  return x+y;
};
var plus = add;

console.log(add(3, 4)); //7  
console.log(plus(5, 6)); //11

//기명 함수 표현식  
var add = function sum(x, y){  
return x+y;  
};

console.log(add(3, 4)); //7  
console.log(sum(5, 6)); //error

 

  • Function() 생성자 함수. 기본 내장 생성자 함수 Function()으로부터 생성된 객체가 함수.
var add = new Function('x', 'y', 'return x+y');
console.log(add(3, 4)); //7

 

함수 호이스팅

1. 함수 선언문 형태로 정의한 함수는 그 앞에서 함수를 호출하더라도 동작함. 즉, 함수 선언문 형태로 정의한 함수의 유효 범위는 코드의 맨 처음부터 시작함.
2. 함수 표현식 형태로 정의한 함수는 함수 생성 후 코드부터 동작함.
3. 원인 : 변수 생성과 초기화 작업의 분리.

 

 

함수 객체 :

  • 함수도 객체다. 코드 실행뿐 아니라, 일반 객체처럼 properties 가질 수 있음.
    • [[Code]] 내부 property에 함수 코드가 저장되고, . 연산자로 다른 property를 만들고 값을 저장할 수 있음.
  • First Class Object(일급 객체). 일반 객체처럼 값으로 취급됨.
    • 변수나 property 값으로 할당 가능.
    • 함수 인자로 전달 가능.
    • 리턴값으로 활용 가능.
var foo = 100;
var bar = function() { return 100; };
console.log(bar()); //100

var obj = {};  
obj.baz = function() { return 200; }  
console.log(obj.baz()); //200

 

  • 함수는 객체지만 일반 객체와 다르게 추가로 함수 객체만의 표준 property 존재.
    • name property : 함수의 이름. 익명 함수라면 빈 문자열.
    • caller property : 자신을 호출한 함수. 없으면 null.
    • argument property : 함수를 호출할 때 전달된 인자값. 없으면 null.
    • __proto__ property([[Prototype]]) : 함수 객체의 부모 역할을 하는 프로토타입 객체를 Function.prototype 객체라고 함. 이것도 함수 객체라서 위의 properties 가짐.
      • Function.prototype 객체는 모든 함수들의 부모 역할을 하는 프로토타입 객체. 따라서 모든 함수는 여기 있는 properties, methods 를 상속받아 사용 가능.constructor, toString(), apply(thisArg, argArray), call(thisArg, [, arg1 [,arg2, ]]), bind(thisArg, [, arg1 [, arg2, ]])

        참조 블로그

    • length property : 함수 실행시 parameter 개수.
    • prototype property : 내부 property인 [[Prototype]]과 혼동하지 말 것!
      • [[Prototype]]은 부모 역할을 하는 프로토타입 객체를 가리킴
      • prototype property는 이 함수가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할을 하는 프로토타입 객체를 가리킴
      • 함수 생성 시 만들어지며, constructor property 하나만 있는 프로토타입 객체를 가리킨다. 이 constructor property는 자신과 연결된 함수를 가리킨다.
      • 즉, 함수 생성 시, 함수 자신과 연결된 프로토타입 객체를 동시에 생성하며 prototype - constructor property로 서로를 참조!

 

 

함수의 다양한 형태 :

  • 콜백 함수 : 개발자가 등록해두면, 특정 이벤트 발생 혹은 특정 시점 도달 시 시스템에서 호출되는 함수.
    • 익명 함수의 대표적 용도.
    • 특정 함수의 인자로 넘겨서 코드 내부에서 호출되는 함수 또한 콜백 함수가 될 수 있음.
    • 이벤트 핸들러에 콜백 함수가 등록했다면 이벤트 발생할 때마다 실행됨.
  • 즉시 실행 함수 : 함수를 정의함과 동시에 바로 실행하는 함수.
    • 익명 함수 응용.
    • 함수 이름이 있든 없든 상관없음.
    • 같은 함수를 다시 호출 불가. 최초 한 번의 실행만을 필요로 하는 초기화 코드 부분에 사용 적합.
    • 자바스크립트에서는 변수 선언 시, 함수 안에서 선언해도 var를 안 붙이면 global. 변수 유효 범위, 함수 유효 범위 컨트롤 가능.
(function (name){
  console.log('immediate function ->'+name);
})('foo');

//immediate function -> foo

 

 

  • 내부 함수 : 함수 내부에 정의된 함수.
    • 클로저 생성하거나, 부모 함수 코드에서 외부에서의 접근을 막고 독립적인 헬퍼 함수를 구현하는 용도 등으로 사용.
    • 내부 함수에서는 자신을 둘러싼 부모 함수의 변수에 접근 가능.
    • 내부 함수는 일반적으로 자신이 정의된 부모 함수 내부에서만 호출 가능.
    • 부모 함수가 내부 함수를 외부로 return하면 부모 함수 밖에서도 내부 함수 호출이 가능.
function parent(){
  var a = 100;
  var b = 200;

  function child(){
    var b = 300;
    console.log(a);
    console.log(b);
  }

  child();
}

parent();  
child();

//100  
//300  
//error (밖의 child() 호출 결과)

 

  • 함수를 리턴하는 함수
var self = function(){
  console.log('a');
  return function(){
    console.log('b')
  }
}
self = self(); //a (처음 self()가 호출되면서 'a'가 출력되고, self 함수 변수에 리턴값인 함수를 넘김)
self(); //b (위에서 self 함수 변수가 가리키는 함수가 새로운 함수로 변경되고 'b' 출력)

 

 

함수 호출과 this :

  • arguments 객체
    • 함수 형식에 인자를 맞춰 넘기지 않아도 에러 X. 모자라면 에러 대신 undefined로 여김. 인자가 많으면 초과된 인자는 무시.
    • 런타임 시에 호출된 인자의 개수를 확인하고 그에 따라 동작을 다르게 해주는 것이 arguments 객체.
    • 실제 배열이 아닌 유사 배열 객체.
    • 함수 호출 시 넘겨진 인자(배열 형태), length property, callee property 로 구성.
  • 객체의 메소드 호출할 때 this 바인딩
    • this는 자신을 호출한 객체에 바인딩 된다.
var myObject = {
  name : 'foo',
  sayName : function(){
    console.log(this.name);
  }
};

var otherObject = {
  name : 'bar'
};

otherObject.sayName = myObject.sayName;

myObject.sayName(); //foo
otherObject.sayName(); //bar

 

 

  • 함수를 호출할 때 this 바인딩
    • this는 전역 객체인 window에 바인딩됨.
var test = 'This is test';
console.log(window.test); //This is test

var sayFoo = function(){  
  console.log(this.test);  
};

sayFoo(); //This is test

 

 

  • 내부 함수에서 this 이용할 때 주의!
var value = 100;

var myObject = {
  value: 1,
  func1: function(){
    this.value += 1;
    console.log('func1() called. this.value : '+this.value);
    
    func2 = function(){
      this.value += 1;
      console.log('func2() called. this.value : '+this.value);

      func3 = function(){
        this.value += 1;
        console.log('func3() called. this.value : '+this.value);
      }

      func3(); //func3() 내부 함수 호출
    }

    func2(); //func2() 내부 함수 호출
  }
};

myObject.func1(); //func1() 메소드 호출

 

  • 차례대로 func1(), func2(), func3()이 호출되고 출력되는 this.value가 2, 3, 4 일 것 같지만 X!

 

func1() called. this.value : 2
func1() called. this.value : 101
func1() called. this.value : 102

 

  • 이유 : 내부 함수 호출 패턴을 정의해 놓지 않기 때문
  • 내부 함수의 this는 전역 객체(window)에 바인딩.
  • 2, 3, 4 로 출력되게 하려면 부모 함수의 this를 내부 함수가 접근 가능한 다른 변수에 저장.(아래 코드)
var value = 100;

var myObject = {
  value: 1,
  func1: function(){
    var that = this;
    this.value += 1;
    console.log('func1() called. this.value : '+this.value);

    func2 = function(){
      that.value += 1;
      console.log('func2() called. this.value : '+that.value);

      func3 = function(){
        that.value += 1;
        console.log('func3() called. this.value : '+that.value);
      }

      func3();
    }
    func2();
  }
};

myObject.func1();

 

  • 생성자 함수를 호출할 때 this 바인딩
    • 자바스크립트에서는 기존 함수에 new 연산자를 붙여서 호출하면 생성자 함수로 동작.
    • 혼동을 막기 위해 함수 이름 첫 문자를 대문자로 쓰기 권장.
생성자 함수가 동작하는 방식

1. 빈 객체 생성 및 this 바인딩.
  - 생성자 함수가 새로 생성하는 빈 객체는 this로 바인딩 된다. 이후 생성자 함수 코드 내부에서 사용된 this는 이 객체를 가리킨다. (사실 완전히 빈 객체는 아니고, 자신의 부모인 프로토타입 객체와 연결되어 있음)
2. this를 통한 property 생성
  - 생성된 빈 객체에 동적으로 property나 메소드를 생성할 수 있다.
3. 생성된 객체 리턴
  - 리턴문이 없으면 this로 바인딩된 새 객체가 리턴된다.
  - this말고 다른 객체를 반환 시, 생성자 함수를 호출했다 하더라도 this 아닌 해당 객체가 리턴된다.

 

var Person = function(name){
  //함수 코드 실행 전
  this.name = name;
  //함수 리턴
}; 

var foo = new Person('foo'); //new로 호출하면, Person()은 생성자 함수로 동작 
console.log(foo.name); //foo

    • 객체 리터럴 방식은 같은 형태의 객체 재생성 불가, 생성자 함수 방식은 같은 형태의 서로 다른 객체 생성 가능.
    • new를 붙이지 않고 생성자 함수 호출하면 오류 발생.
      • 일반 함수 호출의 경우, this가 window 전역 객체에 바인딩
      • 생성자 함수 호출의 경우, this는 새로 생성되는 빈 객체에 바인딩
    • call과 apply 메소드를 이용한 명시적인 this 바인딩
      • call()과 apply()는 모든 함수의 부모 객체인 Function.prototype 객체의 메소드.
      • apply()를 호출하는 주체는 함수이고, 본질적인 기능도 함수 호출.
function.apply(thisArg, argArray)

 

  • 첫 번째 인자 thisArg : apply()를 호출한 함수 내부에서 사용한 this에 바인딩할 객체를 가리킴. (명시적 바인딩)
  • 두 번째 인자 argArray : 함수를 호출할 때 넘길 인자들의 배열을 가리킴.
  • 즉, argArray 를 자신을 호출한 함수의 인자로 사용하되, 이 함수 내부에서 사용된 this는 thisArg 객체로 바인딩해서 함수 호출
function Person(name, age, gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
}

var foo = {};

Person.apply(foo, ['foo', 30, 'man']);

 

 

  • call() 메소드도 apply()와 기능이 같지만 apply()가 argArray로 넘긴 인자들을 각각 하나의 인자로 넘긴다.
  • arguments 객체와 같은 유사 배열 객체에서 배열 메소드 사용하는 경우 많이 사용!
function myFunction(){
  console.dir(arguments);

  //arguments.shift(); 에러발생(arguments는 유사 배열 객체이므로)

  var args = Array.prototype.slice.apply(arguments);
  console.dir(args);
}

myFunction(1,2,3);

 

  • Array.prototype.slice.apply(arguments)의 의미는, Array.prototype.slice() 메소드를 호출하고, 이때 this는 arguments 객체로 바인딩 해라.
  • arguments는 유사 배열 객체라 표준 배열 메소드인 slice() 못 쓰지만, 마치 자신의 메소드인 것처럼 쓸 수 있음.