JavaScript 학습 --Item7 함수 및 함수 표현식

24023 단어

1. 함수 성명과 함수 표현식


ECMAScript에서 함수를 만드는 데 가장 자주 사용하는 두 가지 방법은 함수 표현식과 함수 성명이다. 이 두 가지 차이점은 약간 어지럽다. 왜냐하면 ECMA규범은 단지 한 가지만 명확하기 때문이다. 함수 성명은 반드시 표시자(Identifier)가 있어야 하고 함수 표현식은 이 표시자를 생략할 수 있다.
함수 선언:
function   ( : ){   }

함수 표현식:
function  ( )( : ){   }

따라서 함수 명칭을 성명하지 않으면 표현식이 틀림없지만 함수 명칭을 성명하면 함수 성명인지 함수 표현식인지 어떻게 판단할 수 있을까?ECMAScript는 상하문을 통해 구분됩니다. 만약functionfoo() {}가 값 표현식의 일부분이라면 함수 표현식입니다. 만약functionfoo() {}가 함수 안에 포함되거나 프로그램의 맨 위에 있으면 함수 성명입니다.
function foo(){} //  , 
var bar = function foo(){}; //  , 

new function bar(){}; //  , new 

(function(){
    function bar(){} //  , 
})();

표현식과 성명은 매우 미묘한 차이가 존재한다. 우선, 함수 성명은 어떤 표현식이 해석되고 값을 구하기 전에 해석되고 값을 구한다. 설령 당신의 성명이 코드의 마지막 줄에 있다 하더라도 같은 작용역 내의 첫 번째 표현식 전에 해석/값을 구한다. 아래의 예를 참고하면 함수 fn은alert 다음에 성명되지만,alert가 실행될 때 fn은 이미 정의가 있다.
alert(fn());

function fn() {
    return 'Hello world!';
}

또한 함수 성명은 조건 문장에서 사용할 수 있지만 표준화되지 않았다. 즉, 서로 다른 환경이 서로 다른 집행 결과를 얻을 수 있기 때문에 이런 상황에서 함수 표현식을 사용하는 것이 가장 좋다. 조건 문장에 블록급 작용역이라는 개념이 없기 때문이다.
//  !
//  first function, 

if (true) {
  function foo() {
    return 'first';
  }
}
else {
  function foo() {
    return 'second';
  }
}
foo();

//  , , 
var foo;
if (true) {
  foo = function() {
    return 'first';
  };
}
else {
  foo = function() {
    return 'second';
  };
}
foo();

함수 선언의 실제 규칙은 다음과 같습니다.
함수 성명은 프로그램이나 함수 안에만 나타날 수 있다.문법적으로 보면 블록(블록) ({...}) 에 나타날 수 없습니다. 예를 들어 if, while 또는 for 문장에 나타날 수 없습니다.블록 (블록) 에는 Statement 문장만 포함할 수 있고 함수 설명과 같은 원본 요소는 포함할 수 없기 때문이다.다른 한편, 규칙을 자세히 보면 유일하게 표현식을 블록 (블록) 에 나타낼 수 있는 상황은 표현식 문장의 일부로 하는 것이다.그러나 규범은 표현식 문장이 키워드function으로 시작할 수 없다고 명확하게 규정했다.이것은 사실상 함수 표현식 역시 Statement 문장이나 Block (블록) 에 나타날 수 없다는 것이다. (블록) 은 Statement 문장으로 구성되어 있기 때문이다.)

2. 명명 함수 표현식


명명 함수 표현식을 언급하자면 당연히 이름이 있어야 한다. 앞의 예는 varbar=functionfoo(){};바로 유효한 명명 함수 표현식이지만 한 가지 기억해야 할 것은 이 이름은 새로 정의된 함수 작용역에서만 유효하다는 것이다. 왜냐하면 규범에 따라 표시자가 외곽의 작용역에서만 유효하지 않다고 규정했기 때문이다.
var f = function foo(){
  return typeof foo; // function --->foo 
};
// foo 
typeof foo; // "undefined"
f(); // "function"

기왕 이렇게 요구하는 바에야 함수 표현식을 명명하면 도대체 무슨 소용이 있겠는가?왜 이름을 지어야 합니까?
우리가 처음에 말한 바와 같이 그 이름을 주는 것은 디버깅 과정을 더욱 편리하게 할 수 있기 때문이다. 디버깅을 할 때 호출 창고의 모든 항목에 자신의 이름이 있으면 디버깅 과정은 너무 시원하고 느낌이 다르기 때문이다.
tips: 여기서 작은 문제를 제기합니다. ES3에서 함수 표현식의 역할 영역 대상도 Object를 계승했습니다.prototype의 속성입니다.이것은 함수 표현식에 이름만 붙여도 Object가 지정된다는 것을 의미합니다.prototype의 모든 속성을 역할 영역에 도입합니다.결과는 의외일 수도 있다.
var constructor = function(){return null;} var f = function f(){ return construcor(); } f(); //{in ES3  }

이 프로그램은null이 생길 것 같지만, 사실은 새로운 대상이 생길 것이다.이름 함수 표현식이 역할 영역 내에서 Object를 상속하기 때문입니다.prototype.constructor (즉 Object의 구조 함수)with 문장과 같이 이 작용역은 Object로 인해 발생합니다.prototype의 동적 변화에 영향을 받습니다.다행히도 ES5는 이 오류를 수정했습니다.
이런 행위의 합리적인 해결 방법은 함수 표현식과 같은 이름의 국부 변수를 만들고 값을null로 부여하는 것이다.함수 표현식 설명을 잘못 올리지 않은 환경에서도 var 재성명 변수를 사용하면 변수 g가 귀속될 수 있습니다.변수 g를null로 설정하면 중복 함수가 쓰레기로 회수될 수 있습니다.
var f = function g(){
    return 17;
}
var g =null;

3. 디버거(호출 창고)의 명명 함수 표현식


방금 말했듯이, 명명 함수 표현식의 진정한 용도는 디버깅인데, 도대체 어떻게 쓰입니까?만약 함수에 이름이 있다면, 디버그는 디버그할 때 호출된 창고에 이름을 표시합니다.일부 디버거(Firebug)는 때때로 당신들의 함수에 이름을 지어주고 표시합니다. 이 함수를 응용하는 편리함과 같은 역할을 하게 합니다. 그러나 일반적인 상황에서 이 디버거들은 간단한 규칙만 설치하여 이름을 짓기 때문에 큰 가치가 없습니다. 예를 들어 함수 표현식을 명명하지 않아도 됩니다.
function foo(){
  return bar();
}
function bar(){
  return baz();
}
function baz(){
  debugger;
}
foo();

//  3 
//  debugger ,Firebug  
//  
baz
bar
foo
expr_test.html()

호출 창고의 정보를 보면 우리는 foo가 bar를 호출했고 bar가 baz를 호출했다는 것을 똑똑히 알 수 있다. (foo 자체는 expr_test.html 문서의 전역 작용 영역에서 호출되었다.) 그러나 방금 말한 Firebug가 익명 테이블식으로 이름을 짓는 기능도 있다.
function foo(){
  return bar();
}
var bar = function(){
  return baz();
}
function baz(){
  debugger;
}
foo();

// Call stack
baz
bar() // ? 
foo
expr_test.html()

그리고 함수 표현식이 약간 복잡할 때 디버거는 그렇게 똑똑하지 않다. 우리는 호출 창고에서 물음표를 볼 수 밖에 없다.
function foo(){
  return bar();
}
var bar = (function(){
  if (window.addEventListener) {
    return function(){
      return baz();
    };
  }
  else if (window.attachEvent) {
    return function() {
      return baz();
    };
  }
})();
function baz(){
  debugger;
}
foo();

// Call stack
baz
(?)() //  , (anonymous function)
foo
expr_test.html()

또한 함수를 여러 변수에 값을 부여할 때도 답답한 문제가 발생한다.
function foo(){
  return baz();
}
var bar = function(){
  debugger;
};
var baz = bar;
bar = function() { 
  alert('spoofed');
};
foo();

// Call stack:
bar()
foo
expr_test.html()

이때 호출 창고에 표시된 것은foo가bar를 호출한 것이지만 실제로는 그렇지 않다. 이런 문제가 있는 것은baz와alert('spoofed')를 포함하는 다른 함수가 인용 교환을 했기 때문이다.
결국 함수 표현식에 이름을 지어주는 것만이 가장 의뢰하는 방법이다. 즉, 명명 함수 표현식을 사용하는 것이다.우리는 위의 예를 다시 쓰기 위해 이름이 있는 표현식을 사용합니다. (즉시 호출된 표현식 블록에서 되돌아오는 두 함수의 이름은 모두 bar입니다.
function foo(){
  return bar();
}
var bar = (function(){
  if (window.addEventListener) {
    return function bar(){
      return baz();
    };
  }
  else if (window.attachEvent) {
    return function bar() {
      return baz();
    };
  }
})();
function baz(){
  debugger;
}
foo();

//  !
baz
bar
foo
expr_test.html()

오케이, 또 배웠지?

좋은 웹페이지 즐겨찾기