지적 코딩을 위한 매우 얕은 지식

Javascript/Js

JS 함수(3)

toy2508 2022. 7. 2. 22:34
반응형

1. 값인 함수

   - 자바스크립트의 함수는 값으로써 변수에 할당될 수 있고 객체프로퍼티,
     배열 요소에 저장될 수 있으며, 다른 함수에 인자로 전달이 될 수 있음

 

   - 함수는 프로퍼티를 가질 수 있음

// 값인 함수 --------------------------------------------------
// 함수 정의
function add(x,y) { return x + y;}
function subtract(x, y) { return x - y; }
function multiply(x,y) { return x * y; }
function divide(x, y) { return x / y; }

// 앞에서 정의한 함수 중 하나를 인자로 받아
// 그 함수에 다른 두 인자를 전달해 호출하는 함수
function operate(operator, operand1, operand2) {
	return operator(operand1, operand2);
}
// 이 함수를 사용해 (2+3) + (4*5) 같은 값을 계산할 수 있음
let i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));

// 객체 리터럴로 만든다.
const operators = {
	add: (x,y) => x+y,
    subtract: (x,y) => x-y,
    multiply: (x,y) => x*y,
    divide: (x,y) => x/y,
    pow: Math.pow //미리 정의한 함수 사용 가능
}

// 매개변수 연산자 이름을 통해 객체에서 연산자를 찾고
// 매개변수 피연산자를 찾은 연산자를 통해 계산
function operate2(operation, operand1, operand2) {
	if (typeof operators[operation] == "function") {
    	return operators[operation](operand1, operand2);
    } 
    else throw "unknown operator";
}

operate2("add","hello", operate2("add", " ", "world")) // => "hello world"
operate2("pow", 10, 2)  // => 100

// 함수 프로퍼티 직접 정의 -----------------------------------------
// 객체 프로퍼티
// 함수 객체의 counter 프로퍼티를 초기화
uniqueInteger.counter = 0;

// 이 함수는 호출할 때마다 다른 정수를 반환
// 자신의 프로퍼티를 사용해 어떤 값을 반환할지 판단
function uniqueInteger() {
	return uniqueInteger.counter++; // counter 프로퍼티를 반환하고 증가
}

uniqueInteger() // => 0
uniqueInteger() // => 1

// 배열 프로퍼티
// 팩토리얼을 계산하고 그 결과를 함수 잔체의 프로퍼티를 캐시함
function factorial(n) {
	if(Number.isInteger(n) && n > 0) {           // 양의 정수만 사용
    	if (!(n in factorial) {                  // 캐시된 결과가 없다면
        	factorial[n] = n * factorial(n-1);   // 계산하고 캐시에 저장
        }
        return factorial[n];                     // 캐시된 결과를 반환
    } else {
    	return NaN;                              
    }
}

factorial[1] = 1; // 캐시 초기화
factorial(6) // => 720
factorial[5] // => 120; 이 값은 이미 캐시에 존재

    }
}

 

 

2. 네임스페스인 함수

- 코드의 정의한 변수가 다른 프로그램과 충동할 수 있음으로 특정 프로그램
  에만 사용할 수 있도록 변수를 함수에 정의하는 방법이 있음

 

 function chunkNamespace() {
 	// 코드가 여기 존재함, 코드에 정의한 변수는 모두함수의 로컬 변수이므로
    // 전역 네임스페이스를 어지럽히는 일은 없음
 }
 
 chunkNamespace(); // 단, 이 함수 호출은 잊지 말아야함
 
 // IIFE(즉시 호출하는 함수 표현식)
 (function() { // chunkNamespace() 함수를 익명 표현식으로 변경
 	// 코드가 존재함
 }()); // 함수 리터럴을 종료하고 즉시 호출

 

3. 클로저

- 자바스크립트는 어휘적 스코프(lexial scope)를 사용

- 어휘적 스코프 : 호출 시점의 스코프라 아니라 자신이 정의된 시점의 변수 스코프를
                            사용하여 실행
- 함수가 중첩되어 있는 상황에서 외부 함수가가 내부 함수보다 오래 유지되면, 외부 함수 밖에서

  내부함수가 호출되더라도 외부 함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저(closure)라고 함 

// 클로저 예제 --------------------------------------------------
// 내부 함수를 통해 외부 함수의 로컬 변수값을 반환
let scope = "global scope";  // 전역 변수
function checkscope() {
	let scope = "local scope"; // 로컬 변수
    function f() {return scope; } 이 스코프에 있는 값 반환
}

console.log(checkscope()())  // => "local scope"


// 비공개 상태 사용(클로저 활용) --------------------------------
let uniqueInteger = (function() {
	let counter = 0;
	return function() {return counter++;};
}())

uniqueInteger() // => 0
uniqueInteger() // => 1

// uniqueInteger는 내부함수가 할당되고
// uniqueInteger는 실행할때 마다 외부함수의 
// counter 지역 변수를 증가시킨다


// 여러개의 함수를 객체에 묶어 반환 --------------------------------
function counter() {
	let n = 0;
    return {
    	count: function() {return n++; }
        reset: function() {n = 0; }
    };
}

let c = counter(), d = counter(); // 카운터 두개 생성
c.count()  // => 0
d.count()  // => 0
c.reset()  // reset, count는 n 상태 공유
c.count()  // => 0 : c는 리셋
d.count()  // => 1 : d는 리셋되지 않음

// 선언된 두개 c, d의 카운터는 서로에게 영향을 주지 않음

// 클로저를 활용한 세터, 게터--------------------------------------
function counter(n) {
	return {
    	// 프로퍼티 게터 메서드를 비공개 카운터 변수를 반환하고 증가
        get count() { return n++; },
        // 프로퍼티 세터는 n 값의 감소를 허용하지 않음
        set count(m) {
        	if (m > n) n = m;
            else throw Error("카운트는 더 큰 값으로만 바꿀 수 있음")
        }
    };
}

let c = counter(1000);
c.count // => 1000
c.count // => 1001
c.count = 2000
c.count // => 2000
c.count = 2000; // Error: 카운트는 더 큰 값만 바꿀 수 있음


// 클로저를 사용한 비공개 프로퍼티 접근자 메서드--------------------
// 이 함수는 지정된 이름의 프로퍼티에 대한 프로퍼티 접근자 메서들 객체 o에 추가
// 메서드 이름은 get<name>과 set<name>으로 지정
// 판별 함수가 제공됐다면 세터 메서드는 인자를 저장하기 전에 판별 함수를 사용해
// 유효성을 테스트한다. 판별 함수가 false 반환하면 세터 메서드가 예외를 일으키다.

// 이 함수의 독특한 점은 게터와 세터 메서드가 조작하는 프로퍼티 값이
// 객체 o에 저장되지 않는다는 점이다. 값은 이 함수의 로컬 변수에만 저장된다.
// 게터와 세터 메서드는 함수에 로컬로 정의됐다면 로컬 변수에 접근할 수 있다.
// 따라서 값은 두 접근자 메서드에서만 사용할 수 있으며, 세터 메서드를 통하지 않고서는
// 값을 수정하거나 저장 할 수 없음

function addPrivateProperty(o, name, predicate) {
	let value; //프로퍼티 값
    
    // 게터 메서드는 단순히 그 값을 반환
    o[`get${name}`] = function() {return value;};
    // 세터 메서드는 판별 함수의 판단에 따라 값을 저정하거나 예외를 발생시킴
    o[`set${name}`] = function(v) {
    	if (predicate && !predicate(v)) {
        	throw new TypeError(`set${name}: invalid value ${v}`);
        } else {
        	value = v;
        }
    };
}

let o = {};
addPrivateProperty(o, "Name", x => typeof x === "string");
o.setName("Frank")  // 프로퍼티의 값을 지정
o.getName()  // => "Frank"
o.setName(0);   // TypeError: 올바르지 않은 타입을 사용


// 클로저 사용시 주의할 점 ------------------------------------------
function constFuncs() {
	let funcs = [];
    for(var i=0; i<10; i++) {
    	funcs[i] = () => i;
    }
    return funcs
}

let funcs = constFuncs();
funcs[5]()  // => 10; 
// i의 값을 클로저 함수가 공유하고 있기 때문에
// i의 마지막값이 나옴(var로 선언하면 함수 전체에 i값이 존재하게됨)
// for문의 i를 var 선언하지 않고 const, let 선언시 해결
// const, let은 블록스코프여서 for문 안에서만 유효함

 

반응형

'Javascript > Js' 카테고리의 다른 글

JS 클래스(1)  (0) 2022.07.09
JS 함수(4)  (0) 2022.07.07
JS 함수(2)  (0) 2022.06.26
JS 함수(1)  (0) 2022.06.25
JS 배열(3)  (0) 2022.06.18