>웹 프론트엔드 >JS 튜토리얼 >JavaScript 시리즈에 대한 심층적 이해 (15) Functions_javascript 기술

JavaScript 시리즈에 대한 심층적 이해 (15) Functions_javascript 기술

WBOY
WBOY원래의
2016-05-16 17:54:261003검색

소개
이 장에서는 매우 일반적인 ECMAScript 객체인 함수에 중점을 둘 것입니다. 다양한 유형의 함수가 컨텍스트 변수 객체와 각 함수의 범위 체인에 어떤 영향을 미치는지 자세히 설명하고 이에 대한 답변을 제공합니다. 다음과 같은 질문이 있습니다. 아래에 선언된 함수 간에 차이점이 있나요? (그렇다면 차이점은 무엇입니까).
원문: http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/

코드 복사 코드는 다음과 같습니다.

var foo = function () {
...
}

일반적인 방법:
코드 복사 코드는 다음과 같습니다.

function foo() {
.. .
}

또는 다음 함수를 괄호로 묶은 이유는 무엇인가요?
코드 복사 코드는 다음과 같습니다.

(function () {
. ..
})();

구체적인 소개는 앞서 12장 Variable Objects와 14장 Scope Chain을 소개한 바 있습니다. 위의 두 장 세부 정보.

하지만 함수 유형부터 시작하여 하나씩 살펴봐야 합니다.

함수 유형
ECMAScript에는 세 가지 함수 유형이 있습니다. 함수 선언, 함수 표현식 및 함수 생성자로 생성된 함수입니다. 각각 고유한 특성이 있습니다.
함수 선언
함수 선언(FD로 약칭)은 다음과 같은 함수입니다.
특정 이름을 가지고
소스 코드에서 위치: 프로그램 수준(프로그램 level) 또는 다른 함수의 바디(FunctionBody)에서
컨텍스트 단계에서
영향 변수 객체
를 생성하고 다음과 같이 선언합니다
코드 복사 코드는 다음과 같습니다.

function exampleFunc() {
...
}

이 함수 유형의 주요 특징은 변수 객체(즉, 컨텍스트의 VO에 저장된 객체)에만 영향을 미친다는 것입니다. 이 기능은 또한 두 번째 중요한 점(변수 개체 속성의 결과)을 설명합니다. 이는 코드 실행 단계에서 이미 사용할 수 있습니다(코드 실행 전 컨텍스트 단계에 들어갈 때 VO에 FD가 이미 존재하기 때문입니다).
예(함수는 선언 전에 호출됨)
코드 복사 코드는 다음과 같습니다.

foo();
function foo() {
alert('foo')
}

또 다른 핵심 지식 포인트는 위의 두 번째 사항입니다. 정의 - —소스 코드에서 함수 선언의 위치:
코드 복사 코드는 다음과 같습니다.

// 함수 다음 위치에서 선언할 수 있습니다:
// 1) 전역 컨텍스트에서 직접
function globalFD() {
// 2) 또는 함수 본문 내에서 함수
function innerFD() {}
}

이 두 위치에서만 함수를 선언할 수 있습니다. 즉, 표현식 위치나 함수에서 함수를 정의할 수 없습니다. 코드 블록.
함수 선언을 대체하는 또 다른 방법은 함수 표현식으로, 이에 대한 설명은 다음과 같습니다.
함수 표현식
함수 표현식(약어로 FE)은 함수입니다.
소스에 포함되어야 합니다. code 표현식 위치에 나타납니다
선택적 이름이 있습니다
변수 객체에 영향을 주지 않습니다
코드 실행 단계에서 생성됩니다
이 함수 유형의 주요 특징은 항상 다음과 같이 표현된다는 것입니다. 소스 코드 유형 위치. 가장 간단한 예는 할당문입니다.
코드 복사 코드는 다음과 같습니다.

var foo = function () {
...
};

이 예에서는 foo 변수에 익명 함수 표현식을 할당하는 방법을 보여 주며, 이름을 사용하여 함수에 액세스할 수 있습니다. foo ——foo().
동시에 정의에 설명된 대로 함수 표현식에는 선택적 이름도 있을 수 있습니다.
코드 복사 코드 다음과 같습니다:

var foo = function _foo() {
...
}

외부 FE는 "foo" 변수를 통해 액세스된다는 점에 유의해야 합니다. - —foo(), 함수 내에서(예: 재귀 호출) "_foo"라는 이름을 사용할 수 있습니다.
FE에 이름이 있다면 FD와 구별하기 어려울 것 같아요. 그러나 정의를 이해하면 구별이 간단하고 명확합니다. FE는 항상 표현식 위치에 있습니다. 다음 예에서는 다양한 ECMAScript 표현식을 볼 수 있습니다.
// 괄호(그룹화 연산자)에는 표현식만 포함될 수 있습니다.
(function foo() {})
// 배열 초기화 내에서는 [function bar() {}];
// 쉼표는 표현식
1, function baz() {}
표현식에서만 작동할 수 있습니다. 수식 정의에 명시되어 있습니다. FE는 코드 실행 단계에서 생성되었으며 변수 개체에는 존재하지 않습니다. 예시 동작을 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.
// FE는 정의 단계 이전에는 사용할 수 없습니다. (코드 실행 단계에서 생성되기 때문입니다.)
alert(foo) // "foo"는 아닙니다. available Definition
(function foo() {});
// 변수 객체 VO에 없기 때문에 정의 단계 이후에는 사용할 수 없습니다.
alert(foo); 정의되지 않음
몇 가지 질문이 발생합니다. 왜 함수 표현식이 필요한가요? 대답은 분명합니다. 변수 객체를 "오염시키지 않는다"는 표현에 사용하는 것입니다. 가장 간단한 예는 함수를 다른 함수에 매개변수로 전달하는 것입니다.
function foo(callback) {
callback();
}
foo(function bar() {
alert('foo.bar');
}); >foo(function baz() {
alert('foo.baz');
});


위의 예에서 FE는 변수에 할당됩니다(즉, 매개변수), 함수는 표현식을 메모리에 저장하고 다음과 같이 변수 이름을 통해 표현식에 액세스합니다(변수는 변수 객체에 영향을 미치기 때문입니다).


var foo = function () {
alert('foo')
}; 🎜>

또 다른 예는 외부 컨텍스트에서 보조 데이터를 숨기기 위해 캡슐화 클로저를 만드는 것입니다(다음 예에서는 생성 직후 호출되는 FE를 사용합니다).



코드 복사
코드는 다음과 같습니다. var foo = {} (function 초기화() {
var x = 10; foo.bar = function () {
alert(x);
})(); // 10; 🎜>alert( x); // "x"는 정의되지 않았습니다


foo.bar 함수가 ([[Scope]] 속성을 통해) 내부 변수 "x"에 액세스하는 것을 볼 수 있습니다. 기능 초기화. 동시에 "x"는 외부에서 직접 액세스할 수 없습니다. 많은 라이브러리에서 이 전략은 "개인" 데이터를 생성하고 보조 엔터티를 숨기는 데 사용됩니다. 이 모드에서는 초기화된 FE의 이름은 일반적으로 무시됩니다.




코드 복사

코드는 다음과 같습니다.
(function () { // 초기화 범위 })() 또 다른 예는 코드 실행 단계에서 조건문을 통해 FE를 생성하는 것입니다. 변수 객체 VO를 오염시키지 마십시오.



코드 복사

코드는 다음과 같습니다.
괄호에 대한 질문
기사 시작 부분에서 언급한 질문에 대해 다시 대답해 보겠습니다. "함수를 호출한 직후에 함수를 괄호로 묶어야 하는 이유는 무엇입니까? 만들어졌나요?", 답은: 표현문의 제한은 이렇습니다.
표준에 따르면 표현식 문은 코드 블록과 구별하기 어렵기 때문에 중괄호 {로 시작할 수 없습니다. 마찬가지로 함수 선언과 구별하기 어렵기 때문에 함수 키워드로 시작할 수 없습니다. 즉, 생성 직후 즉시 실행되는 함수를 정의하면 다음과 같이 호출됩니다.


function () {
...
} ();
// 이름이 있어도
function foo() {
...
}();
위의 두 가지 정의에 대해 함수 선언을 사용했는데, 해석할 때 해석기가 오류를 보고하지만 이유는 다양할 수 있습니다.
글로벌 코드(즉, 프로그램 수준)에 정의된 경우 함수 키워드로 시작하므로 인터프리터는 이를 함수 선언으로 간주합니다. 첫 번째 예에서는 함수 선언으로 인해 SyntaxError가 발생합니다. 이름이 없습니다(앞서 함수 선언에는 이름이 있어야 한다고 언급했습니다).
두 번째 예에서는 정상적으로 생성된 foo라는 함수 선언이 있지만 여전히 구문 오류(표현식 없이 그룹화 연산자 오류)가 발생합니다. 이는 실제로 함수 호출에 사용되는 괄호가 아니라 함수 선언 뒤의 그룹화 연산자입니다. 따라서 다음 코드를 선언하면:
코드를 복사합니다. 코드는 다음과 같습니다.

// "foo"는 컨텍스트에 들어갈 때 생성되는 함수 선언입니다.
alert(foo); // Function
function foo(x) {
alert(x)}(1) ; // 이것은 함수 호출이 아닌 그룹화 연산자입니다!
foo(10); // 실제 함수 호출이고 결과는 10입니다.

선언하면 2개의 객체가 생성되므로 위 코드에는 문제가 없습니다. , 1로 그룹화 연산을 수행하면 위의 예는 다음 코드로 이해될 수 있습니다.

코드 복사 코드는 다음과 같습니다.
// 함수 선언
function foo(x) {
alert(x)
}
// 표현식 1을 포함하는 그룹화 연산자

(1)

코드 복사 코드는 다음과 같습니다.
// 또 다른 연산자에는 함수 표현식
(function () {});
// 이 연산자에는 "foo" 표현식도 포함되어 있습니다.
("foo")// 잠깐


다음 코드를 정의하면(정의에 명령문이 포함됨) 정의가 모호하고 오류가 보고될 수 있습니다.
if (true) function foo () {alert (1)}
사양에 따르면 위의 코드는 잘못되었지만(표현식 문은 function 키워드로 시작할 수 없음) 다음 예에서는 오류를 보고하지 않습니다. 이유를 생각해 보세요.
통역사에게 함수 선언 직후에 호출하고 싶다고 말하면 대답은 매우 명확합니다. 함수 선언이 아닌 함수 표현식을 선언해야 하며 표현식을 만드는 가장 쉬운 방법은 그룹화를 사용하면 됩니다. 연산자 괄호 안에 들어가는 내용은 항상 표현식이므로 인터프리터가 이를 해석할 때 모호함이 없습니다. 이 함수는 코드 실행 단계에서 생성되어 즉시 실행된 다음 자동으로 삭제됩니다(참조가 없는 경우).


(function foo(x) {
alert( x);
})(1); // 이것은 그룹화 연산자가 아닌 호출입니다.


위 코드는 표현식을 괄호로 묶는 것입니다. 통과 (1) 전화로 이동합니다.
즉시 실행되는 다음 함수의 경우 함수가 이미 표현식의 위치에 있고 파서는 실행 중에 생성되어야 하는 FE를 처리하고 있음을 알고 있기 때문에 주변 괄호가 필요하지 않습니다. 함수 실행 단계이므로 함수가 생성되면 함수가 즉시 호출됩니다.


var foo = {
bar: function (x ) {
return x % 2 != 0 ? '예' : '아니요'
}(1)
alert(foo.bar); '


보시다시피 foo.bar는 함수가 아니라 문자열입니다. 여기서 함수는 조건부 매개변수를 기반으로 이 속성을 초기화하는 데만 사용됩니다. 즉시 생성되고 호출됩니다.
따라서 "괄호에 대하여" 질문에 대한 완전한 답변은 다음과 같습니다. 함수가 표현식 위치에 없는 경우 그룹화 연산자 괄호가 필요합니다. 즉, 함수를 수동으로 FE로 변환합니다.
파서가 FE를 다루고 있다는 것을 알고 있다면 괄호를 사용할 필요가 없습니다.
중괄호 외에도 다음 형식을 사용하여 함수를 FE 유형으로 변환할 수도 있습니다. 예:



코드 복사 코드 다음과 같습니다:

// 1이라는 점에 유의하세요. 다음 명령문은
1, function () {
alert('익명 함수가 호출됩니다')
}(); // 또는 This
!function () {
alert('ECMAScript')
}()
// 기타 수동 변환 형식
...

하지만 이 예에서는 괄호가 가장 간결한 방법입니다.
그런데 함수 설명을 둘러싼 그룹 표현식에는 호출 괄호가 없을 수도 있고 호출 괄호가 포함될 수도 있습니다. 즉, 다음 두 표현식은 모두 올바른 FE입니다.
확장 구현: 함수 문
다음 코드는 함수 문에 따라 실행되어서는 안 됩니다.

코드 복사 코드는 다음과 같습니다.
if (true) {
function foo() {
alert(0)
}
} else {
function foo() {
alert(1);
}
}
foo() // 실제로 다른 환경에서 테스트하면 결과가 다릅니다

여기서 이 구문 구조는 표준에 따라 일반적으로 부정확하다는 점에 유의해야 합니다. 함수 선언(FD)이 코드 블록에 나타날 수 없다는 점도 기억하기 때문입니다(여기서는 if 및 else가 코드 조각을 포함함). . 우리는 FD가 프로그램 레벨(프로그램 레벨) 또는 다른 기능 본문의 두 위치에만 나타난다고 말한 적이 있습니다.
코드 블록에는 명령문만 포함되어 있으므로 이는 올바르지 않습니다. 함수가 블록에 나타날 수 있는 유일한 위치는 이러한 명령문 중 하나, 즉 위에서 이미 설명한 표현식 명령문입니다. 그러나 정의상 중괄호(코드 블록과 다르기 때문에)나 함수 키워드(FD와 다르기 때문에)로 시작할 수 없습니다.
그러나 표준 오류 처리 장에서는 프로그램 구문의 확장된 구현을 허용합니다. 그러한 확장 중 하나는 코드 블록에 나타나는 기능입니다. 이 예에서는 기존의 모든 실행이 예외를 발생시키지 않고 처리합니다. 그러나 그들은 모두 자신의 길을 가지고 있습니다.
if-else 분기문의 등장은 동적 선택을 의미합니다. 즉, 논리적으로는 코드 실행 단계에서 동적으로 생성되는 함수 표현식(FE)이어야 합니다. 그러나 대부분의 구현에서는 컨텍스트 단계에 들어갈 때 단순히 함수 선언(FD)을 생성하고 마지막으로 선언된 함수를 사용합니다. 즉, foo 함수는 "1"을 표시하고 실제로 else 분기는 실행되지 않습니다.
그러나 SpiderMonkey(및 TraceMonkey)는 이 상황을 두 가지 방식으로 처리합니다. 한편으로는 함수를 선언으로 처리하지 않지만(즉, 코드 실행 단계 중 조건에 따라 함수가 생성됨) 반면에 괄호가 없기 때문에(역시 구문 분석 오류 - "FD와 다름") 호출할 수 없으므로 실제로는 함수 표현식이 아니며 변수 개체에 저장됩니다.
저는 개인적으로 SpiderMonkey가 자체 기능 중간 유형(FE FD)을 분할하여 이 예에서 올바르게 작동한다고 생각합니다. 이러한 함수는 조건에 따라 적시에 생성됩니다. FE와 달리 외부에서 호출할 수 있는 FD와 마찬가지로 SpiderMonkey는 이 구문 확장 함수 문(FS로 약칭)을 호출합니다.
명명된 함수 표현식의 특징
함수 표현식 FE에 이름이 있으면(명명된 함수 표현식이라고 하며 약칭 NFE) 중요한 기능이 나타납니다. 정의(위의 예에서 보았듯이)에서 우리는 함수 표현식이 컨텍스트의 변수 객체에 영향을 미치지 않는다는 것을 알고 있습니다. 즉, 함수가 선언되기 전에 이름으로 함수를 호출할 수 없고 선언된 후에 호출할 수도 없습니다. . 그러나 FE는 재귀 호출에서 이름으로 자신을 호출할 수 있습니다.

코드 복사 코드는 다음과 같습니다.
(function foo(bar) {
if ( bar) {
return;
}
foo(true); // "foo"를 사용할 수 있습니다
})()// 외부적으로는 사용할 수 없습니다
foo(); // "foo"는 정의되지 않았습니다


"foo"는 어디에 저장되어 있나요? foo의 활성 개체에서? 아니요, foo에는 "foo"가 정의되어 있지 않기 때문입니다. 컨텍스트의 상위 변수 객체에 foo를 생성하시겠습니까? 아니요, 정의에 따르면 FE는 VO(변수 객체)에 영향을 주지 않습니다. 외부에서 foo를 호출할 때 실제로 이를 볼 수 있습니다. 그래서 어디?
핵심 사항은 다음과 같습니다. 인터프리터가 코드 실행 단계에서 명명된 FE를 만나면 FE가 생성되기 전에 보조 특정 객체를 생성하여 현재 범위 체인 앞에 추가합니다. 그런 다음 FE를 생성합니다. 이 시점에서 (4장 범위 체인에서 배웠듯이) 함수는 [[Scope]] 속성(이 함수 컨텍스트를 생성한 범위 체인)을 획득합니다. 그 후에는 FE의 이름이 고유한 속성으로 특정 개체에 추가됩니다. 이 속성의 값은 FE에 대한 참조입니다. 마지막 단계는 상위 범위 체인에서 특정 개체를 제거하는 것입니다. 이 알고리즘을 의사코드로 살펴보겠습니다.
코드 복사 코드는 다음과 같습니다.

SpecialObject = {};
specialObject Scope;
foo = new FunctionExpression;
foo.[[Scope]] = Scope; // {DontDelete}, ReadOnly }
delete Scope[0]; // 정의된 특수 객체 SpecialObject를 범위 체인에서 삭제

따라서 이 이름은 함수 외부에서 사용할 수 없습니다(부모에 없기 때문). 범위 체인) 그러나 특정 개체는 이름을 사용할 수 있는 함수의 [[범위]]에 이미 저장되어 있습니다.
그러나 일부 구현(예: Rhino)에서는 이 선택적 이름을 특정 개체가 아닌 FE의 활성화 개체에 저장한다는 점에 유의해야 합니다. Microsoft의 구현은 FE 규칙을 완전히 깨뜨리고 상위 변수 개체에 이름을 유지하여 외부에서 함수에 액세스할 수 있도록 합니다.
NFE와 SpiderMonkey
NFE와 SpiderMonkey의 차이점을 살펴보겠습니다. SpiderMonkey의 일부 버전에는 버그로 처리될 수 있는 특정 개체와 관련된 속성이 있습니다(표준에 따르면 모두 버그임에도 불구하고). 그런 식으로 구현되었지만 ECMAScript 표준의 버그와 비슷합니다). 이는 식별자의 구문 분석 메커니즘과 관련이 있습니다. 범위 체인 분석은 2차원입니다. 식별자 구문 분석에서는 범위 체인에 있는 각 개체의 프로토타입 체인도 고려됩니다.
Object.prototype에 속성을 정의하고 "존재하지 않는" 변수를 참조하는 경우. 우리는 이 실행 메커니즘을 볼 수 있습니다. 따라서 아래 "x" 구문 분석 예제에서는 전역 개체에 도달하지만 "x"를 찾지 못합니다. 그러나 SpiderMonkey에서는 전역 개체가 Object.prototype의 속성을 상속하므로 "x"도 구문 분석할 수 있습니다.

코드 복사 코드는 다음과 같습니다.
Object.prototype.x = 10; 🎜>( function () {
alert(x); // 10
})();


활성 객체에는 프로토타입이 없습니다. 동일한 시작 조건에 따라 위의 예에서는 내부 함수의 이러한 동작을 볼 수 없습니다. 지역 변수 "x"를 정의하고 내부 함수(FD 또는 익명 FE)를 정의하는 경우 내부 함수에서 "x"를 참조하세요. 그런 다음 이 변수는 Object.prototype 대신 상위 함수 컨텍스트(즉, 해결되어야 하는 위치)에서 해결됩니다.


Object.prototype.x = 10; 🎜>function foo() {
var x = 20;
// 함수 선언
function bar() {
alert(x);
bar() / / 20, foo의 변수 객체 AO에서 쿼리
// 익명 함수 표현식의 경우에도 마찬가지입니다
(function () {
alert(x); // 20, 변수 객체에서도 쿼리 AO of foo
})();
}
foo();


그러나 일부 구현에는 활성 객체에 대한 프로토타입을 설정하는 예외가 있습니다. 따라서 Blackberry 구현에서는 위 예의 "x"가 "10"으로 해석됩니다. 즉, foo의 값이 Object.prototype에서 발견되었으므로 foo의 활성 개체에 도달하지 않습니다.
AO(bar FD 또는 익명 FE) -> no ->
AO(bar FD 또는 익명 FE).[[Prototype]] -> yes - 10
SpiderMonkey에서는 동일합니다. 상황은 FE라는 특정 개체에서 완전히 볼 수 있습니다. 이 특정 개체는 (표준에 따라) 일반 개체입니다 - "new Object() 표현식과 유사"하므로 Object.prototype의 속성을 상속해야 합니다. 이는 SpiderMonkey(버전 1.7+) 실행에서 볼 수 있는 것과 정확히 같습니다. 나머지 실행(새로운 TraceMonkey 포함)에서는 특정 개체에 대한 프로토타입을 설정하지 않습니다.




코드 복사
코드는 다음과 같습니다.

function foo() {
var x = 10;
(function bar() {
alert(x); // 20, 10 이상이 아니고 foo의 활성 객체가 아닙니다. 체인에서 얻은
// "x"가 검색됩니다:
// AO(bar) -> __specialObject(bar) -> no
// __specialObject(bar).[[ 프로토타입]] - 예: 20
})();
}
Object.prototype.x = 20;
foo()

NFE 및 Jscript
현재 IE 브라우저(JScript 5.8 - IE8까지)에 내장된 JScript 실행에는 함수 표현식(NFE)과 관련된 많은 버그가 있습니다. 이러한 버그는 모두 ECMA-262-3 표준과 완전히 모순됩니다. 일부는 심각한 오류를 일으킬 수 있습니다.
우선, 이 예제의 JScript는 FE의 주요 규칙을 어겼습니다. 함수 이름으로 변수 개체에 저장하면 안 됩니다. 선택적 FE 이름은 특정 개체에 저장되어야 하며 함수 자체 내에서만 액세스할 수 있어야 합니다(다른 곳에서는 액세스할 수 없음). 그러나 IE는 이를 상위 변수 객체에 직접 저장합니다. 또한 명명된 FE는 JScript에서 함수 선언(FD)으로 처리됩니다. 즉, 컨텍스트에 진입하는 단계에서 생성되어 소스코드에서 정의하기 전에 접근이 가능하다.
코드 복사 코드는 다음과 같습니다.

// FE는 변수 객체에 표시됩니다.
testNFE ();
(function testNFE() {
alert('testNFE');
})
// FE는 정의가 완료된 후에도 표시됩니다
/ / 함수 선언과 같습니다.
testNFE();

보시다시피 이는 규칙을 완전히 어깁니다.
두 번째로, 명명된 FE를 선언의 변수에 할당하면 JScript는 두 개의 서로 다른 함수 개체를 만듭니다. 논리적으로(특히 NFE 외부에서는 해당 이름에 전혀 액세스하면 안 됨) 이 동작의 이름을 지정하기는 어렵습니다.
코드 복사 코드는 다음과 같습니다.

var foo = function bar() {
alert('foo');
};
alert(typeof bar); // "function",
// 흥미로운 점은
alert(foo === bar); // false!
foo.x = 10;
alert(bar.x); // 정의되지 않음
// 그러나 실행하면 결과는 같습니다.
foo(); "
bar (); // "foo"

다시 보니 엉망이네요.
그러나 NFE를 변수 할당과 별도로 설명하고(예: 그룹 연산자를 통해) 이를 변수에 할당하고 동일성을 확인하면 결과가 true라는 점에 유의하는 것이 중요합니다. 객체였습니다.
코드 복사 코드는 다음과 같습니다.

(function bar() {});
var foo = bar;
alert(foo === bar); // true
foo.x = 10
alert(bar.x); >
이 시점에서는 해석의 여지가 있습니다. 사실상 두 개의 개체가 다시 생성되지만 실제로는 하나로 남아 있습니다. 다시 생각해 보면 여기서 NFE는 FD로 취급되며 컨텍스트 단계에 들어갈 때 FD 막대가 생성됩니다. 이후 코드 실행 단계에서 저장되지 않는 두 번째 개체인 FE(함수 표현) 막대가 생성됩니다. 이에 따라 FE바에 대한 언급은 없어 제거됩니다. 이렇게 하면 객체(FD bar)가 하나만 있고 해당 참조는 변수 foo에 할당됩니다.
셋째, Arguments.callee를 통해 함수를 간접적으로 참조한다는 점에서는 활성화된 객체의 이름을 의미합니다(정확히 말하면 여기에는 두 개의 함수 객체가 있습니다.


var foo = function bar() {
alert([
arguments. callee === foo,
arguments.callee === bar
])
foo(); // [true, false]
bar(); [false, true]


넷째, JScript는 NFE를 일반 FD처럼 취급하며 조건식 규칙을 따르지 않습니다. 즉, FD처럼 컨텍스트에 들어갈 때 NFE가 생성됩니다. 코드의 마지막 정의가 사용됩니다.

코드 복사
코드는 다음과 같습니다. var foo = function. () { alert(1); };
if (false) {
foo = function bar() {
alert(2)
}
바(); // 2
foo() // 1


이러한 행동은 "논리적으로" 설명될 수도 있습니다. 컨텍스트 단계에 들어가면 마지막으로 만난 FD 막대가 생성되는데, 이는 Alert(2)를 포함하는 함수입니다. 그 후, 코드 실행 단계에서 새로운 함수인 FE bar가 생성되고 이에 대한 참조가 변수 foo에 할당됩니다. foo를 활성화하면 경고(1)가 생성됩니다. 논리는 명확하지만 IE 버그로 인해 "논리적으로"라는 단어 주위에 따옴표를 붙였습니다. 실행이 분명히 중단되고 JScript 버그에 의존하기 때문입니다.
JScript의 다섯 번째 버그는 정규화되지 않은 식별자(즉, var 키워드 없이)에 할당하여 생성되는 전역 개체의 속성 생성과 관련이 있습니다. 여기서 NFE는 FD로 처리되므로 함수 이름이 동일한 경우 변수 개체에 저장되고 정규화되지 않은 식별자(즉, 변수가 아니라 전역 개체의 일반 속성)가 할당됩니다. unqualified는 동일한 식별자를 가지므로 속성은 전역이 아닙니다.
코드 복사 코드는 다음과 같습니다.

(function () {
/ / var가 필요하지 않습니다. 그렇다면 현재 컨텍스트의 변수가 아닙니다.
// 전역 개체의 속성입니다.
foo = function foo() {}
})(); 🎜>// 그러나 익명 함수 외부에서는 foo라는 이름을 사용할 수 없습니다.
alert(typeof foo); // 정의되지 않음

"논리"는 이미 명확합니다. context 단계에서 함수는 foo가 익명 함수의 로컬 컨텍스트의 활성 객체를 획득했다고 선언합니다. 코드 실행 단계에서 foo라는 이름은 이미 AO에 존재합니다. 즉, 지역 변수로 처리됩니다. 따라서 할당 작업에서는 ECMA-262-3의 로직에 따라 전역 객체의 새로운 속성을 생성하는 것이 아니라, AO에 이미 존재하는 foo 속성을 간단히 업데이트합니다.
함수 생성자를 통해 생성된 함수
이런 함수 객체도 고유한 특성을 갖고 있으므로 FD, FE와 구분합니다. 주요 특징은 이 함수의 [[Scope]] 속성에 전역 개체만 포함된다는 것입니다.

코드 복사 코드는 다음과 같습니다. 다음:
var x = 10;
function foo() {
var x = 20;
var y = 30; 경고(x) ; 경고(y);');
bar(); // 10, "y"는 정의되지 않았습니다
}


] 함수 표시줄 속성에 foo 컨텍스트의 Ao가 포함되어 있지 않습니다. 변수 'y'에 액세스할 수 없으며 변수 'x'는 전역 개체에서 가져옵니다. 그런데 Function 생성자는 new 키워드와 함께 또는 없이 사용될 수 있으므로 이러한 변형은 동일합니다.

이 기능의 다른 기능은
Equated Grammar Productions

Joined Objects와 관련이 있습니다. 사양은 이러한 메커니즘을 최적화 권장 사항으로 제공합니다(그러나 구현에서는 최적화를 사용하지 않을 수 있습니다). 예를 들어 함수 루프 내에서 100개 요소의 배열이 있는 경우 실행 시 결합된 개체 메커니즘을 사용할 수 있습니다. 결과적으로 배열의 모든 요소에 대해 하나의 함수 개체만 사용할 수 있습니다.

var a = [];
for (var k = 0; k < 100; k ) {
a[k] = function () {} // 결합된 객체를 사용할 수 있습니다
}


그러나 함수 생성자를 통해 생성된 함수는 연결되지 않습니다.


코드 복사 코드는 다음과 같습니다. var a = [];
for (var k = 0; k < 100; k ) {
a[k] = Function('') // 항상 100개의 다른 함수가 있습니다
}


결합된 객체와 관련된 또 다른 예:



코드 복사function bar(z) {
return z * z
}

return bar
}

var; x = foo();
var y = foo()


여기서의 구현에는 함수(내부 [[Scope]] 속성 포함)가 근본적으로 구별할 수 없기 때문에 객체 x와 객체 y(동일한 객체 사용)를 연결할 수 있는 권한도 있습니다. 따라서 함수 생성자를 통해 생성된 함수에는 항상 더 많은 메모리 리소스가 필요합니다.
함수 생성 알고리즘
아래 의사코드는 함수 생성 알고리즘을 설명합니다(공용체 개체와 관련된 단계 제외). 이러한 설명은 ECMAScript의 함수 개체에 대한 자세한 내용을 이해하는 데 도움이 됩니다. 이 알고리즘은 모든 함수 유형에 적합합니다.
코드 복사 코드는 다음과 같습니다.

F = new NativeObject()

// 속성 [[Class]]는 "Function"
F.[[Class]] = "Function"

// 함수 객체의 프로토타입은 Function <의 프로토타입입니다. 🎜>F.[[ Prototype]] = Function.prototype

// 함수 자체가 사용됩니다
// 표현식 F를 호출할 때 [[Call]]을 활성화하고
// new 실행 컨텍스트
F.[[Call]] = <함수 참조>

// 객체의 일반 생성자에서 컴파일
// [[Construct]] new를 통해 활성화 키워드
// 새 객체에 메모리를 할당합니다
// 그런 다음 F.[[Call]]을 호출하여 새로 생성된 객체를 초기화합니다.
F.[[Construct]] = InternalConstructor

// 현재 실행 컨텍스트 범위 체인
// 예를 들어 F의 컨텍스트를 생성합니다
F.[[Scope]] = activeContext.Scope
// new를 통해 함수가 생성되는 경우 Function(...),
// 그러면
F.[[Scope]] = globalContext.Scope

// 전달된 매개변수 개수
F.length = countParameters

// F 객체 생성의 프로토타입
__objectPrototype = new Object();
__objectPrototype.constructor = F // {DontEnum}, 루프에서 열거할 수 없음 x
F.prototype = __objectPrototype

return F

F.[[Prototype]]은 함수(생성자)의 프로토타입이고, F.prototype은 이에 의해 생성된 객체의 프로토타입이라는 점에 유의하세요. 함수(용어가 종종 혼동되기 때문에 일부 기사에서는 F.prototype을 "생성자 프로토타입"이라고 부르는데 이는 올바르지 않습니다).


결론 글이 좀 깁니다. 하지만 다음 장에서 객체와 프로토타입에 대한 함수에 대해 계속 논의하면서 의견에 있는 질문에 다시 답변해 드리겠습니다.


기타 참고자료
    13. —
  • 기능 정의; 15.3 —
  • 함수 개체
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.