Function | PoiemaWeb

함수란 어떤 특정 작업을 수행하기 위해 필요한 일련의 구문들을 그룹화하기 위한 개념이다. 만일 스크립트의 다른 부분에서도 동일한 작업을 반복적으로 수행해야 한다면 (동일한 구문을 계속

poiemaweb.com

 

// 함수 선언문
function square(number) {
  return number * number;
}

 

// 함수 표현식
var square = function(number) {
  return number * number;
};

 

함수 표현식 방식으로 정의한 함수는 함수명을 생략할 수 있다. 이러한 함수를 익명 함수(anonymous function)이라 한다. 함수 표현식에서는 함수명을 생략하는 것이 일반적이다.

 

// 기명 함수 표현식(named function expression)
var foo = function multiply(a, b) {
  return a * b;
};

// 익명 함수 표현식(anonymous function expression)
var bar = function(a, b) {
  return a * b;
};

console.log(foo(10, 5)); // 50
console.log(multiply(10, 5)); // Uncaught ReferenceError: multiply is not defined

 

함수는 일급객체이기 때문에 변수에 할당할 수 있는데 이 변수는 함수명이 아니라 할당된 함수를 가리키는 참조값을 저장하게 된다. 함수 호출시 함수명이 아니라 함수를 가리키는 변수명을 사용하여야 한다.

 

var foo = function(a, b) {
  return a * b;
};

var bar = foo;

console.log(foo(10, 10)); // 100
console.log(bar(10, 10)); // 100

변수 bar와 변수 foo는 동일한 익명 함수의 참조값을 갖는다.

 

함수 선언문으로 정의한 함수 square의 경우, 함수명으로 호출할 수 있었는데 이는 자바스크립트 엔진에 의해 아래와 같은 함수 표현식으로 형태가 변경되었기 때문이다.

 

var square = function square(number) {
  return number * number;
};

 

함수명과 함수 참조값을 가진 변수명이 일치하므로 함수명으로 호출되는 듯 보이지만 사실은 변수명으로 호출된 것이다.

결국 함수 선언문도 함수 표현식과 동일하게 함수 리터럴 방식으로 정의되는 것이다.

 

 

 Function 생성자 함수

함수 표현식으로 함수를 정의할 때 함수 리터럴 방식을 사용한다. 함수 선언문도 내부적으로 자바스크립트 엔진이 기명 함수 표현식으로 변환하므로 결국 함수 리터럴 방식을 사용한다.

따라서 함수 선언문과 함수 표현식은 모두 함수 리터럴 방식으로 함수를 정의하는데 이것은 결국 내장 함수 Function 생성자 함수로 함수를 생성하는 것을 단순화시킨 short-hand(축약법)이다.

Function 생성자 함수는 Function.prototype.constructor 프로퍼티로 접근할 수 있다.

Function 생성자 함수로 함수를 생성하는 문법은 다음과 같다.

 

new Function(arg1, arg2, ... argN, functionBody)

 

var square = new Function('number', 'return number * number');
console.log(square(10)); // 100

Function 생성자 함수로 함수를 생성하는 방식은 일반적으로 사용하지 않는다.

출처 : 모던 자바스크립트 딥다이브

 

 

언매니지드 언어 vs 매니지드 언어

프로그래밍 언어는 메모리 관리 방식에 따라 언매니지드 언어와 매니지드 언어로 분류할 수 있다. 

 

C언어 같은 언매니지드 언어는 개발자가 명시적으로 메모리를 할당하고 해제하기 위해 malloc()와 free() 같은 저수준 메모리 제어 기능을 제공한다. 언매니지드 언어는 메모리 제어를 개발자가 주도할 수 있으므로 개발자의 역량에 따라 최적의 성능을 확보할 수 있지만 그 반대의 경우 치명적 오류를 생산할 가능성도 있다.

 

자바스크립트 같은 매니지드 언어는 메모리의 할당 및 해제를 위한 메모리 관리 기능을 언어 차원에서 담당하고 개발자의 직접적인 메모리 제어를 허용하지 않는다. 즉 개발자가 명시적으로 메모리를 할당하고 해제할 수 없다. 더 이상 사용하지 않는 메모리의 해제는 가비지 콜렉터가 수행하며, 이 또한 개발자가 관여할 수 없다. 매니지드 언어는 개발자의 역량에 의존하는 부분이 상대적으로 작아져 어느 정도 일정한 생산성을 확보할 수 있다는 장점이 있지만 성능 면에서는 어느 정도의 손실은 감수할 수밖에 없다.

 

가비지 콜렉터

가비지 콜렉터는 애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여 더 이상 사용되지 않는 메모리를 해제하는 기능을 말한다. 더 이상 사용되지 않는 메모리란 간단히 말하자면 어떤 식별자도 참조하지 않는 메모리 공간을 의미한다. 자바스크립트는 가비지 콜렉터를 내장하고 있는 매니지드 언어로서 가비지 콜렉터를 통해 메모리 누수를 방지한다. 

 

 

재할당 변수와 가비지 콜렉터

 

var이나 let 키워드를 사용해 선언한 변수에 값을 재할당하면 변수의 값은 이전 값에서 재할당한 값으로 변경된다. 

이때, 이전 값이 저장되어 있던 메모리 공간을 지우고 그 메모리 공간에 재할당 값을 새롭게 저장하는 것이 아니라,

 

이전 값이 저장되어 있던 메모리 공간도 그대로 놔둔 채,

새로운 메모리 공간을 확보하고 그 메모리 공간에 재할당 값을 저장한다.

 

즉, 재할당된 var의 경우 초기화값인 undefined와 처음 할당값은 어떠한 변수도 값으로 갖고 있지 않는 상태가 될 것이다.

다시 말해 어떤 식별자와도 연결되어 있지 않은 값들이 발생한다.

이렇게 발생한 더 이상 필요하지 않는 값들은 아무도 사용하고 있지 않으니 해제하는 것이 바람직하다.

 

이렇게 불필요한 값들은 가비지 콜렉터에 의해 메모리에서 자동 해제된다.

단, 메모리에서 언제 해제될지는 예측할 수 없다. 

 

undefined vs null

undefined는 개발자가 의도적으로 할당하기 위한 값이 아니라 자바스크립트 엔진이 변수를 초기화할 때 사용하는 값이다. 변수를 참조했을 때 undefined가 반환된다면 참조한 변수가 선언 이후 값이 할당된 적이 없는, 즉 초기화되지 않은 변수라는 것을 간파할 수 있다.

자바스크립트 엔진이 변수를 초기화하는데 사용하는 undefined를 개발자가 의도적으로 변수에 할당한다면 undefined의 본래 취지와 어긋날뿐더러 혼란을 줄 수 있으므로 권장하지 않는다.

 

변수에 값이 없다는 것을 명시하고 싶을 때는 undefined가 아니라 null을 할당한다.

 

undefined 

undefined의 값은 undefined가 유일하다. var로 선언한 변수는 암묵적으로 undefined로 초기화된다. 다시 말해, 변수 선언에 의해 확보된 메모리 공간을 처음 할당이 이뤄질 때까지 빈 상태(대부분 비어있지 않고 쓰레게 값이 들어있다)로 내버려 두지 않고, 자바스크립트 엔진이 undefined로 초기화한다. 따라서 변수를 선언한 이후 값을 할당하지 않은 변수를 참조하면 undefined가 반환된다.

 

 

null

null타입의 값은 null이 유일하다. 프로그래밍 언어에서 null은 변수에 값이 없다는 것을 의도적으로 명시(의도적 부재)할 때 사용한다. 변수에 null을 할당하는 것은 변수가 이전에 참조하던 값을 더 이상 참조하지 않겠다는 것이다. 이는 이전에 할당되어 있던 값에 대한 참조를 명시적으로 제거하는 것을 의미하며, 자바스크립트 엔진은 누구도 참조하지 않는 메모리 공간에 대해 가비지 콜렉션을 수행할 것이다.

 

js의 선언과 정의

다른 프로그래밍 언어에서는 선언과 정의를 엄격하게 구분해서 사용하는 경우가 있다.

예를 들어, C에서 선언과 정의는 "실제로 메모리 주소를 할당하는가"로 구분한다. 단순히 컴파일러에게 식별자의 존재만 알리는 것은 선언이고, 실제로 컴파일러가 변수를 생성해서 식별자와 메모리 주소가 연결되면 정의로 구분한다. 자바스크립트의 경우 변수를 선언하면 암묵적으로 정의가 이루어지기 때문에 선언과 정의의 구분이 모호하다.

 

ECMAScript 사양에서 변수는 '선언하다'라고 표현하고, 함수는 '정의하다'라고 표현한다.

학습 출처 : 모던 자바스크립트 딥다이브

 

1. 변수 선언

 

2. (변수 선언에 의해) 메모리 공간 확보

 

3. 자바스크립트 엔진이 확보된 메모리 공간에 undefined라는 값을 암묵적으로 할당, 초기화

 

* undefined는 js에서 제공하는 원시타입의 값(primitive value)이다.

 

자바스크립트 엔진의 변수 선언
- 선언 단계 : 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알림
- 초기화 단계 : 값을 저장하기 위한 메모리 공간을 확보하고,
암묵적으로 undefined를 할당해 초기화

 

* 변수 이름을 비롯한 모든 식별자는 실행 컨텍스트(execution context)에 등록된다.

실행 컨텍스트(execution context)는 js 엔진이 소스코드를 평가하고 실행하기 위해 필요한 환경을 제공하고,

코드의 실행 결과를 실제로 관리하는 영역이다. 자바스크립트 엔진은 실행 컨텍스트를 통해 식별자와 스코프를 관리한다.

변수 이름과 변수 값은 실행 컨텍스트 내에 키/값 형식인 객체로 등록되어 관리된다.

 

선언하지 않은 식별자에 접근하면 참조에러(ReferenceError)발생, 식별자를 통해 값을 참조하려 했지만 자바스크립트 엔진에 등록된 식별자를 찾을 수 없을 때 발생하는 에러

 

console.log(score); // undefined

var score;  // ① 변수 선언
score = 80; // ② 값의 할당

console.log(score); // 80

첫 줄에서 undefined이 출력되는 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점, 런타임이 아니라 그 이전 단계에서 먼저 실행되기 때문이다.

 

자바스크립트 엔진은 소스 코드를 한줄씩 순차적으로 실행하기에 앞서 먼저 소스코드의 평가 과정을 거치면서 소스코드를 실행하기 위한 준비를 한다. 이때 소스 코드 실행을 위한 준비 단계인 소스코드의 평가 과정에서 자바스크립트 엔진은 변수 선언을 포함한 모든 선언문(변수 선언문, 함수 선언문 등)을 소스 코드에서 찾아내 먼저 실행한다. 그리고 소스 코드의 평가 과정이 끝나면 비로소 변수 선언을 포함한 모든 선언문을 제외하고 소스코드를 한줄씩 순차적으로 실행한다.

 

위의 예제도 변수 선언(선언 단계와 초기화 단계)이 소스코드가 순차적으로 실행되는 런타임 이전 단계에서 먼저 실행된다는 증거이다. 이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅이라한다.

 

사실 변수 선언뿐 아니라 var, let, const, function, function*, class 키워드를 사용해서 선언하는 모든 식별자(변수, 함수, 클래스 등)는 호이스팅된다. 모든 선언문은 런타임 이전 단계에서 먼저 실행되기 때문이다.

console.log(score); // undefined

score = 80; // 값의 할당
var score;  // 변수 선언

console.log(score); // 80

주의할 점은 변수 선언고 값의 할당 실행 시점이 다르다는 것이다. 변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행되지만 값의 할당은 소스코드가 순차적으로 실행된느 시점인 런타임에 실행된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

생성자 함수에 의한 객체 생성

자바스크립트의 생성자 함수에 의한 객체 생성에 관한 내용 정리

velog.io

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

this - JavaScript | MDN

JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다.

developer.mozilla.org

 

대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정

즉,함수 선언이 아닌 호출하는 방법에 따라 달라지므로, 함수가 호출될 때 마다 다를 수 있다.

 

주의사항 : 화살표 함수는 부모 스코프(선언 기준)

 

ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입

ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했다(렉시컬 컨텍스트안의 this값을 유지).

 

예시 1 : this의 값을 한 문맥에서 다른 문맥으로 넘기려면 다음 예시와 같이 call()이나 apply()를 사용

// call 또는 apply의 첫 번째 인자로 객체가 전달될 수 있으며 this가 그 객체에 묶임
var obj = {a: 'Custom'};

// 변수를 선언하고 변수에 프로퍼티로 전역 window를 할당
var a = 'Global';
//단 let a일 경우, undefined

function whatsThis() {
  return this.a;  // 함수 호출 방식에 따라 값이 달라짐
}

whatsThis();          // this는 'Global'. 함수 내에서 설정되지 않았으므로 global/window 객체로 초기값을 설정한다.
whatsThis.call(obj);  // this는 'Custom'. 함수 내에서 obj로 설정한다.
whatsThis.apply(obj); // this는 'Custom'. 함수 내에서 obj로 설정한다.

 

예시 2

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {a: 1, b: 3};

// 첫 번째 인자는 'this'로 사용할 객체이고,
// 이어지는 인자들은 함수 호출에서 인수로 전달된다.
add.call(o, 5, 7); // 16

// 첫 번째 인자는 'this'로 사용할 객체이고,
// 두 번째 인자는 함수 호출에서 인수로 사용될 멤버들이 위치한 배열이다.
add.apply(o, [10, 20]); // 34

 

솔직히 이 부분은 잘 와닿지 않다가... 뚫어져라 보니 이해됨

 

function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7);     // [object Number]
bar.call('foo'); // [object String]
bar.call(undefined); // [object global]

 

 

 

Object.prototype.toString() - JavaScript | MDN

The toString() 은 문자열을 반환하는 object의 대표적인 방법이다

developer.mozilla.org

toString() 메서드는 Object에서 비롯된 모든 객체에 상속

var o = new Object();
o.toString(); // returns [object Object]

 

The toString() 은 문자열을 반환하는 object의 대표적인 방법이다

 

function Dog(name) {
  this.name = name;
}

const dog1 = new Dog('Gabby');

Dog.prototype.toString = function dogToString() {
  return `${this.name}`;
};

console.log(dog1.toString());
// expected output: "Gabby"

toString()으로 객체 클래스 검사

var toString = Object.prototype.toString;

toString.call(new Date);    // [object Date]
toString.call(new String);  // [object String]
toString.call(Math);        // [object Math]

// Since JavaScript 1.8.5
toString.call(undefined);   // [object Undefined]
toString.call(null);        // [object Null]

화살표 함수

화살표 함수에서 this는 자신을 감싼 정적 범위입니다. 전역 코드에서는 전역 객체를 가리킵니다.

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

참고: 화살표 함수를 call(), bind(), apply()를 사용해 호출할 때 this의 값을 정해주더라도 무시합니다.

// 객체로서 메서드 호출
var obj = {func: foo};
console.log(obj.func() === globalObject); // true

// call을 사용한 this 설정 시도
console.log(foo.call(obj) === globalObject); // true

// bind를 사용한 this 설정 시도
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

어떤 방법을 사용하든 foo this는 생성 시점의 것으로 설정됩니다(위 예시에서는 global 객체). 다른 함수 내에서 생성된 화살표 함수에도 동일하게 적용됩니다. this는 싸여진 렉시컬 컨텍스트의 것으로 유지됩니다.

 

// this를 반환하는 메소드 bar를 갖는 obj를 생성합니다.
// 반환된 함수는 화살표 함수로 생성되었으므로,
// this는 감싸진 함수의 this로 영구적으로 묶입니다.
// bar의 값은 호출에서 설정될 수 있으며, 이는 반환된 함수의 값을 설정하는 것입니다.
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

// obj의 메소드로써 bar를 호출하고, this를 obj로 설정
// 반환된 함수로의 참조를 fn에 할당
var fn = obj.bar();

// this 설정 없이 fn을 호출하면,
// 기본값으로 global 객체 또는 엄격 모드에서는 undefined
console.log(fn() === obj); // true

// 호출 없이 obj의 메소드를 참조한다면 주의하세요.
var fn2 = obj.bar;
// 화살표 함수의 this를 bar 메소드 내부에서 호출하면
// fn2의 this를 따르므로 window를 반환할것입니다.
console.log(fn2()() == window); // true

위 예시에서, obj.bar에 할당된 함수(함수bar라고 지칭)는 화살표 함수로 생성된 다른 함수(익명 함수 B)를 반환

 

결과로써 함수 B가 호출될 때 B의 this는 영구적으로 obj.bar(함수bar)의 this로 설정됩니다.

반환됨 함수(함수 B)가 호출될 때, this는 항상 초기에 설정된 값일 것입니다.

 

위 코드 예시에서,

함수 B의 this는 함수 A의 this obj로 설정되므로,

일반적으로 this undefined나 global 객체로 설정하는 방식으로 호출할 때도

(또는 이전 예시에서처럼 global 실행 컨텍스트에서 다른 방법을 사용할 때에도)

obj의 설정은 유지됩니다.  (->??? 무슨 소리인지 잘 모르겠음)

 

 

객체의 메서드로서

함수를 어떤 객체의 메서드로 호출하면 this의 값은 그 객체를 사용합니다.

다음 예제에서 o.f()를 실행할 때 o 객체가 함수 내부의 this와 연결됩니다.

 

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

 

<메서드>

js에서 사용할 수 있는 모든 값은 프로퍼티 값으로 사용할 수 있다.

js의 함수는 객체(일급 객체)다. 따라서 함수는 값으로 취급할 수 있기 때문에 프로퍼티 값으로 사용할 수 있다.

프로퍼티 값이 함수일 경우 일반 함수와 구분하기 위해 메서드라고 부른다.

즉, 메서드는 객체에 묶여 있는 함수를 의미한다. 

 

 

ES5 객체리터럴 문법

const obj1 = {
    song:'노동요',
    sayHi: function(){console.log(this.song);}
}

obj1.sayHi(); //노동요

(1) 객체 안에서 프로퍼티는 쉼표로 연결됨

(2) ES6 이후로 function 키워드를 아래와 같이 생략할 수 있게됨

const obj1 = {
    song:'노동요',
    sayHi(){console.log(this.song);}
}

obj1.sayHi();//노동요

(3) 주의사항 : 아래와 같이 함수를 정의하는 형식을 쓸 수 없음

const obj1 = {
    song:'노동요',
    function sayHi(){console.log(this.song);}
}

 

--이어서 작성 예정--

 

 

 

 

 

 

 

 

 

 

생성자 함수 내에서 설정하는 프로퍼티랑 동일 이름으로 게터 세터에 지정해주면 다음과 같은 문제가 발생한다.

 

 

그러므로 게터 세터를 사용할 때 내부 프로퍼티임을 암시하는 _ 를 붙여 차별점을 제시하거나,

 

 

 

생성자 함수에 없는 프로퍼티에 게터 세터를 달아주는 식으로 사용해야 한다.

 

 

Object prototypes - Web 개발 학습하기 | MDN

Javascript에서는 객체를 상속하기 위하여 프로토타입이라는 방식을 사용합니다. 본 문서에서는 프로토타입 체인이 동작하는 방식을 설명하고 이미 존재하는 생성자에 메소드를 추가하기 위해

developer.mozilla.org

 

자바스크립트는 프로토타입 기반 언어이다

 

ECMA6 이전의 자바스크립트에서 클래스가 없었다.

객체 지향언어에서 클래스는 빠질 수 없는 개념이기 때문에 JS에서 클래스의 역할을 대신 흉내낼 수 있는 장치들이 필요했다.

프로토타입은 JS가 객체 지향에서 클래스가 해내는 ‘상속’을 흉내내는 방식으로 사용하게 된다.

 

영어사전

prototype

  • 원형(原型), 원본

자바스크립트에서는 객체의 원형인 프로토타입을 이용한 클로닝(Cloning: 복사)과 객체 특성을 확장,연결해 나가는 방식을 통해 새로운 객체를 생성해 낸다.

 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다
자바스크립트의 모든 객체는 자신을 생성한 객체 원형에 대한 숨겨진 연결을 갖는다.
이때 자기 자신을 생성하기 위해 사용된 객체원형을 프로토타입이란 한다. 
프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고
그 상위 프로토타입 객체도 마찬가지
이를 
프로토타입 체인(prototype chain)이라 부르며
다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간
정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라
객체의 생성자의 
prototype이라는 속성에 정의되어 있다.



1

 

2

 

 

 

1.  [[Prototype]]

 

 

 

모든 객체는 내부에 숨겨진 [[Prototype]]:Object 를 가지고 있다.

객체에서 공통적으로 쓰인느 함수들을 Object라는 프로토타입에 만들어둠으로써

손쉽게 객체 간 상속을 할 수 있는 것이다.

 

 

함수 객체의 예를 들자면,

함수를 정의하면 함수만 생성되는 것이 아니라 Prototype Object도 같이 생성된다.

 

 

 

생성된 함수는 prototype이라는 속성을 통해, (위 캡처에서 Foo.prototype)

기본적인 속성으로 constructor와 prototype 속성을 가지고 있는 Prototype Object에 접근할 수 있다.

constructor은 같이 생성되었던 함수 자신을 가리키고 있다.

 

 

 

프로미스 객체 내부에 숨겨진 내부에 숨겨진 [[Prototype]] : Object

 

객체 내부에 숨겨진 [[Prototype]] : Object(속내용 상동)

 

 

2.   prototype

 

 

[[Prototype]]은 외부에서 직접 접근이 불가하기 때문에,

__proto__ 나 prototype 으로 접근해야한다.

모든 함수 객체의 Constructor는 prototype 프로퍼티를 가지고 있다.

 

 

 

 

 

3.   __proto__

 

__proto__ 는 객체를 만들어내기 위해 사용된 객체원형에 대한 숨겨진 연결이다.

 

 

즉, mimi 클래스의 인스턴스인 미미를 통해 콘솔창에서

미미.__proto__ === mimi.prototype을 치면

true로 평가된다.

 

 

 

한 눈에 보기 좋게 표시

 

 

 

Foo()로써 만들어진 모든 객체(f1, f2)는

결국 Foo.prototype 객체와 내부적으로 [[Prototype]]링크로 연결된다.
결국 f1 ,f2Foo 는 상호 연결된 두개의 객체가 된다.

 

 

 

 

 

최종

 

 

+ Recent posts