본문 바로가기
Be Smart/JAVA

[JavaScript] 자바스크립트 기본기 다지기

by 반월하 2022. 2. 25.
728x90

자바스크립트 타입

기본 타입

  1. Number - 실수, 부동소수점 64비트(double)
  2. String - 문자열
  3. Boolean - True, False
  4. undefined - 변수에 값이 할당되지 않을 때 인터프리터가 undefined 로 할당. 값이자 타입
  5. null - 개발자가 의도적으로 할당하는 값. typeof 값이 Object 로 반환. 따라서 === 로 확인
var nullCheck = null;
console.log(typeof nullCheck === null); // false
console.log(nullCheck === null); // true

참조 타입(객체 타입)

  1. Object
  2. Array - 배열도 객체로 취급
  3. Function - 함수도 객체로 취급

NaN (Not a Number)

수치 연산을 해서 정상적인 값을 얻지 못할 때 발생하는 에러

console.log(1 - 'hello'); // NaN

var foo = {
  name: 'foo',
  major: 'cs'
};
foo['full-name'] = 'ffoo';
console.log(foo['full-name']); // 'ffoo'
console.log(foo.full-name); // NaN, 프로퍼티명이 연산자를 포함할 경우

Delete 연산자

객체 프로퍼티를 삭제 하는 기능, 객체 삭제는 불가능

// 1. 객체 프로퍼티를 삭제
var foo = {
  name: 'foo',
  nickname: 'pangyo'
};

delete foo.nickname;
console.log(foo.nickname);
console.log(foo); // {name: "foo"}
// 2. delete 로 객체를 삭제할 경우 (변화 없음)
var foo = {
  name: 'foo',
  nickname: 'pangyo'
};

delete foo;
console.log(foo); // {name: "foo", nickname: "pangyo"}

객체의 모든 연산은 참조 값을 처리

값 비교시에 사용하는 ==를 적용한 예제를 보자.

var a = 10;
var b = 10;

var objA = {
  value: 100
};
var objB = {
  value: 100
};
var objC = objB;

console.log(a == b); // true
console.log(objA == objB); // false
console.log(objB == objC); // true

Array 랑 Object 구분 방법

var arr = [];
var obj = {};

arr.constructor.name; // "Array"
obj.constructor.name; // "Object"

delete & splice 연산자 in 배열

배열에서 delete를 사용하면 요소의 값만 undefined 로 변경하고, 해당 요소 index를 지우지는 않는다.

var arr = [1, 2, 3];
delete arr[1];
console.log(arr); // [1, undefined × 1, 3]

반대로 splice 는 해당 요소 전체를 아예 잘라내서 없앤다.

var arr = [1, 2, 3];
arr.splice(1, 1);
console.log(arr); // [1, 3]

 

typeof 연산자

각 데이터 타입에 대한 typeof 수행결과는 다음과 같다.

var num = 10;
var str = "a";
var boolean = true;
var obj = {};
var undefined;
var nullValue = null;
var arr = [];
function func() {};

console.log(typeof num); // number
console.log(typeof str); // string
console.log(typeof boolean); // boolean
console.log(typeof obj); // object
console.log(typeof undefined); // undefined
console.log(typeof nullValue); // object (null 은 object)
console.log(typeof arr); // object (배열도 object)
console.log(typeof func); // function
 
// 2. delete 로 객체를 삭제할 경우 (변화 없음)
var foo = {
  name: 'foo',
  nickname: 'pangyo'
};

delete foo;
console.log(foo); // {name: "foo", nickname: "pangyo"}
 
 

== 연산자와 === 연산자

== 와 === 의 가장 큰 차이점은 값 뿐만 아니라 타입까지 체크하느냐이다. 또한 == 는 수행시에 타입이 다를 경우 타입을 일치시켜 값을 비교하는 특징이 있다.

console.log(1 == '1'); // true
console.log(1 === '1'); // false
 
함수 호이스팅
add(2, 3); // add is not a function
var add = function (a, b) {
  return a + b;
};
add(4, 5);
 

위 코드의 실행 결과는 add is not a function 이다. 위 코드를 실행할 때 자바스크립트 엔진 관점에서 호이스팅을 적용하여 코드 순서를 변경해보면 아래의 결과가 된다.

var add;
add(2, 3);
add = function (a, b) {
  return a + b;
};
add(4, 5);
 

함수의 length 속성

function func1(a) { return a; }
function func2(a, b) { return a + b; }
function func3(a, b, c) { return a + b + c; }

console.log('func1 length : ' + func1.length); // func1 length : 1
console.log('func2 length : ' + func2.length); // func2 length : 2
console.log('func3 length : ' + func3.length); // func3 length : 3
 

내부 함수

함수의 내부에 정의한 함수

function parent() {
  var a = 10;
  var b = 20;

  function child() {
    var b = 30;
    console.log(a);
    console.log(b);
  }
  child();
}
parent(); // 10, 30
child(); // child is not defined
 

생성자 함수

일반 객체 선언과 다르게 여러 개의 객체를 찍어낼 수 있는 함수. 함수명 맨 앞 글자는 대문자, 호출 시에 new 사용.

function Developer(name, stack, city) {
  this.name = name;
  this.stack = stack;
  this.city = city;
}
var dev = new Developer('captain', 'web', 'pangyo');
var devops = new Developer('hulk', 'devops', 'seoul');
console.log(dev); // Developer {name: "captain", stack: "web", city: "pangyo"}
console.log(devops); // Developer {name: "hulk", stack: "devops", city: "seoul"}
 

위에서 생성한 dev 객체는 아래와 같이 constructor(생성자)가 Developer이다.

instaceof 를 활용한 생성자 함수 구분법

자바스크립트는 생성자 함수 형식이 별도로 없기에 기존 함수에 new만 붙여주면 생성자 함수 생성이 가능하다. 따라서, 생성자 함수가 아닌데 new 를 붙이는 경우를 대비해서 아래와 같은 기법을 적용할 수 있다. 대부분의 오픈소스 라이브러리에서 사용하는 패턴

function Func(arg) {
  // instanceof 로 생성자 함수임을 확인
  if (!(this instanceof arguments.callee)) // 'this instanceof 함수명' 도 가능
    return new Func(arg);
  this.value = arg || 0;
}

var a = new Func(100);
var b = Func(200);
console.log(a.value);
console.log(b.value);
 
prototype & constructor
function func() {
  return true;
}
console.log(func.prototype);
console.log(func.prototype.constructor);
 

프로토타입 체이닝

해당 함수에 존재하지 않는 속성, 메서드를 부모 객체(프로토타입 객체)를 찾음

var obj = {
  name: 'captain',
  printName: function () {
    console.log(this.name);
  }
};
obj.printName(); // 'captain'
obj.hasOwnProperty('name'); // true
obj.hasOwnProperty('city'); // false
 

obj에서 사용한 printName() 메서드는 obj에 선언되었기 때문에 사용이 가능하다. 하지만 hasOwnProperty() 메서드는 선언되지도 않았는데 사용할 수 있다. 왜냐면 obj의 프로토타입 객체가 Object이고, Object에 내장된 메서드가 hasOwnProperty() 이기 때문에, obj에서 프로토타입 객체의 hasOwnProperty() 를 호출한다.

참고로, 자바스크립트 모든 객체의 최상위 부모 객체는 Object 객체다.

Object, String, Number 프로토타입 객체 메서드 재정의

자바스크립트에서 기본으로 제공하는 Object, String, Number 등의 표준 객체에 사용자가 원하는 기능을 재정의하여 사용할 수 있다.

String.prototype.printText = function (text) {
  console.log("Print this text out " + text);
};
var name = "captain";
name.printText('pangyo'); // 'Print this text out pangyo'
 
즉시 실행 함수

함수를 정의함과 동시에 바로 실행하는 함수. 함수를 다시 호출할 수 없다는 특징이 있다. 따라서, 최초 한 번의 실행만 요구되는 초기화 코드에 적합하다. jQuery 와 같은 오픈소스 라이브러리들의 구조.

(function (name) {
  console.log('This is the immediate function : ' + name);
})('foo');
 
클로져

실행이 끝난 함수의 스코프를 참조할 수 있는 함수

function parent() {
  var a = 'Parent is done';
  function child() {
    console.log(a);
  }
  return child;
}
var closure = parent();
closure();
 

위 내부함수의 정의대로라면 parent 의 내부함수인 child() 는 외부에서 접근이 불가능하다. 하지만 return 값에 child 를 넘김으로써 외부에서도 child 를 호출할 수 있게 된다. 따라서, child() 에서 parent 의 값을 참고하고 있다면, child() 를 밖에서 호출함으로써 parent() 의 변수에 접근이 가능하게 된다. 이것이 클로져

map() 구현

// definition
Array.prototype.myMap = function(callback) {
    arr = [];
    for (var i = 0; i < this.length; i++)
        arr.push(callback(this[i], i, this));
    return arr;
};

//tests
var arrs = ['dic tanin', 'boo radley', 'hans gruber'];
var numbers2 = [1, 4, 9];

var goodT = arrs.myMap(function(n) {
    return n;
});

var squareRoot = numbers2.myMap(function(num) {
    return Math.sqrt(num);
});

console.log(goodT); // [ 'dic tanin', 'boo radley', 'hans gruber' ]
console.log(squareRoot); // [ 1, 2, 3 ]
 
실행 컨텍스트를 이해하기 위한 자바스크립트 동작과정
  1. 변수, 함수 선언, arguments 을 가진 활성 객체(Variable Object) 생성
  2. Scope Chain 생성 및 초기화
    • 변수 초기화 : 변수 값에 undefined 할당
  3. this 바인딩
  4. 코드 해석 및 실행
    • 변수 값 할당 : 변수에 실제 값 할당

변수 초기화 과정

  1. 변수 선언 - 변수를 활성 객체에 할당
  2. 변수 초기화 - 변수 값에 undefined 할당
  3. 변수 실제 값 할당 - 변수에 실제 값을 할당

실행 컨텍스트를 이해하기 위한 문제

비동기 실행 방식인 setTimeout 를 이용한 예제이다.

console.log("1");
function exec() {
  setTimeout(function() {
    console.log("2");
  }, 3000);
  setTimeout(function() {
    console.log("3");
  }, 0);
  console.log("4");
  setTimeout(function() {
    console.log(5);
  }, 1000);
}
console.log(exec());
// 위 코드 실행 결과 : 1, 4, 3, 5, 2
 
setTimeout 이 지연시간이 0 이라고 할지라도 실행 컨텍스트가 다르기 때문에 1,4 가 먼저 출력된다.

이번엔 for 문과 setTimeout 이다.

var i;
for (i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i); // 5, 5, 5, 5, 5
  }, 1000);
}
 
위 코드를 실행시켰을 때, 이 코드가 실행되는 메인 컨텍스트와 setTimeout 이 실행되는 컨텍스트가 다르기 때문에 일반 프로그래밍 지식 관점에서는 0,1,2,3,4 이라고 추측하겠지만, 실제로는 for 문의 실행이 모두 끝난 후에 setTimeout 의 콜백 함수가 실행되기 때문에 숫자 5가 다섯 번 출력된다.

arguments 객체

함수 호출시에 넘겨진 실제 인자 값을 가진 배열

// 아래 함수 정의에 포함된 인자 값은 2개
function add(a, b) {
  console.dir(arguments);
}
console.log(add(1)); // Arguments(1), 0: 1
console.log(add(1, 2)); // Arguments(2), 0: 1, 1: 2
console.log(add(1, 2, 3)); // Arguments(3), 0: 1, 1: 2, 2: 3
 
arguments 의 활용 : 메서드에 넘겨 받을 인자의 개수를 모를 때 유용
function sum() {
  for (var i = 0, result = 0; i < arguments.length; i++) {
    result += arguments[i];
  }
  return result;
}
console.log(sum(1,2,3)); // 6
console.log(sum(1,2,3,4,5,6)); // 21
 
참고: arguments는 length 속성과 `arguments[i]`와 같은 index를 지니지만 배열은 아니다. 이러한 객체를 배열과 비슷한 객체(array-like object)라고 한다.

apply() & call()

위에서 배운 arguments에 apply(), call()을 이용하여 실제 배열 메서드를 사용할 수 있다.

// apply() 적용 전
function sum() {
  console.log("arguments length : " + arguments.length);
  arguments.push(100); // Uncaught TypeError: arguments.push is not a function
  console.dir(arguments); // Arguments(3)
}
sum(1,2,3);

// apply() 적용 후
function sum() {
  var args1 = Array.apply(arguments);
  args1.push(100); // 0: 100
  console.dir(args1); // Array(1)

  var args2 = Array.prototype.slice.apply(arguments);
  args2.push(100); // 3: 100
  console.dir(args2); // Array(4)
}
sum(1,2,3);
 
함수명.apply(대상, 인자 배열);
 
apply(), call() 메서드는 결국 .apply()를 호출하는 함수를 실행하는 것 이다. 그리고 호출하는 함수의 인자 값에 apply() 로 넘긴 인자 배열을 넣어서 마지막 실행 결과만 대상에 연결한다라고 보면 되겠다.
function user(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName = lastName;
}
user.apply(window, ['pangyo', 'captain']); // user.call(window, 'John', 'Doe'); 와 같음

console.log(window.firstName); // 'pangyo'
console.log(window.lastName); // 'captain'
 
this 바인딩

일반적으로 함수 내부에서 this를 사용하면 전역 스코프(window)에 접근한다.

// 함수 선언식
var text = 'global';
function binding() {
  var text = 'local';
  console.log(this.text); // 'global'
  console.log(this); // Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …}
}
binding();

// 함수 표현식
var text = 'global';
var binding = function() {
  var text = 'local';
  console.log(this.text); // 'global'
  console.log(this); // Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …}
}
binding();
 
객체의 속성에서 함수를 선언하고 this를 사용하면 해당 객체를 접근한다.
var text = 'global';
var binding = {
  text: 'local',
  printText: function () {
    console.log(this.text); // 'local'
    console.log(this); // {text: "local", printText: ƒ}
  }
};
binding.printText();
 
함수의 내부함수에서 this를 사용하면 전역 객체(window)에 접근한다.
var text = 'global';
var binding = {
  text: 'local',
  printText: function () {
    console.log(this.text); // local
    var innerFunc = function () {
      console.log(this.text); // global
    };
    innerFunc();
  }
};
binding.printText();
 
스코프 체인을 이해하기 위한 예제

아래는 전역 스코프와 함수 스코프를 구분하면 된다.

// ex.1
var a = 1;
var b = 2;
function func() {
  var a = 10;
  var b = 20;
  console.log(a); // 10
  console.log(b); // 20
}
func();
console.log(a); // 1
console.log(b); // 2
 

아래는 내부함수 innerfunc 에서 외부함수인 func 의 변수에 접근하고 있다.

// ex.2
var a = 1;
function func() {
  var a = 2;
  function innerfunc() {
    return a;
  }
  console.log(innerfunc()); // 2
}
func();
 
아래 예제는 func1 의 실행 컨텍스트가 전역이라는 것에 주목한다.
// ex.3
var a = 1;
function func1() {
  return a;
}
function func2(func1) {
  var a = 2;
  console.log(func1()); // 1
}
func2(func1);
 
클로져 정의 및 코드 예시
  • 외부 함수의 실행이 종료되어 컨텍스트가 반환되더라도, 내부 함수로 종료된 외부 함수의 스코프(변수)에 접근이 가능한 기법:스코프 체이닝
  • 이미 생명주기가 끝난 외부 함수의 변수를 참조하는 함수
function func() {
  var a = 1;
  var cl = function () {
    console.log(a);
  };
  return cl
}
var result = func();
console.dir(result); // [[Scope]] 에서 Closure 함수임을 확인 가능
result();

일정한 형식을 가진 템플릿에서 입력된 값에 따라 다른 결과물을 내는 코드

var str = [
  'hello ',
  '',
  ' world'
];

function completeSentence(name) {
  str[1] = name;
  return str.join('');
}
completeSentence('js');
 

위 코드에 클로져를 적용하면

function completeSentence(name) {
  var str = [
    'hello ',
    '',
    ' world'
  ];
  return function () {
    str[1] = name;
    return str.join('');
  };
}
var result = completeSentence('js');
result();
 
위 함수를 좀 더 기능 단위로 분할해보면
function completeSentence(name) {
  var str = [
    'hello ',
    '',
    ' world'
  ];
  // 입력된 문자열로 문장을 완성하는 기능
  var complete = function () {
    str[1] = name;
    return str.join('');
  };
  // 문장 완성 기능을 클로져로 빼는 역할
  var closure = function () {
    return complete();
  };
  return closure;
}
var result = completeSentence('js');
result();
 
클로져 활용

클로져를 활용하여 Java나 기타 언어처럼 속성 및 메서드의 범위를 정할 수 있다.

// 클로져로 Java 클래스와 유사하게 모듈화한 예제
var Module = (function() {
    var privateProperty = 'foo';
    function privateMethod(args) {
      console.log('private method');
    }

    return {
        publicProperty: '',
        publicMethod: function(args) {
          console.log("public method");
        },
        privilegedMethod: function(args) {
          return privateMethod(args);
        }
    };
})();

Module.privilegedMethod();
참고자료
728x90

댓글