fn(); //이미 문자열 유형이므로 호출할 수 없습니다.
fn = function(){ console.info('fn');};//fn을 호출하려면 함수 표현식을 사용하여 fn에 할당해야 합니다.
console.info(typeof)를 호출할 수 있습니다. gn);//문자열
변수 선언이 앞이든 뒤이든 상관없이 선언이 승격되면 함수 선언이 우선한다는 것을 알 수 있습니다. 그러나 선언이 승격된 후에는 변수 초기화를 수행해야 하므로 함수 선언이 우선 적용됩니다. 더 이상 초기화되지 않으므로(승격 중에 함수 유형이 구문 분석됨) 나중에 출력할 때 String 유형이 됩니다.
위의 3번째 줄에 함수를 정의한 뒤 7번째 줄에서 바로 호출했는데 작동하지 않네요! 전역 네임스페이스를 깨끗하게 유지하는 것의 중요성을 이해해야 합니다. 그렇지 않으면 "코드에 함수를 명확하게 정의했지만 호출할 수 없습니다."와 같은 문제가 발생할 수 있습니다. 가능하다면 함수 표현식을 사용하여 정의하는 것이 가장 좋습니다. 물론 이렇게 하면 다른 사람의 코드가 손상될 위험이 있습니다.
또 다른 질문이 있습니다. 변수 선언이 승격될 때가 아니라 초기화 중에 변수 유형이 변경되는지 어떻게 알 수 있나요? 아래 코드를 보세요:
보시다시피 The 승격된 유형은 함수이고 초기화 코드가 없으므로 최종 유형은 변경되지 않았습니다.
함수 선언 및 함수 표현식과 관련하여 주의할 점이 하나 더 있습니다.
ECMAScript 사양에서 명명된 함수 표현식의 식별자는 내부 범위에 속하고, 함수 선언의 식별자는 정의 범위에 속합니다.
전역 범위 외에도 함수 범위에는 함수의 매개변수도 선언 승격 경쟁에 참여합니다. 먼저 명확하게 짚고 넘어가야 할 점은 함수가 정의될 때는 함수 범위가 존재하지 않는다는 것입니다. 함수가 실제로 호출될 때만 함수 범위가 존재한다는 것입니다.
function fn(inner){
console.info(inner);// param
console.info(other);// undefine
var inner = 'inner'; other = '기타';
console.info(inner);// 내부
console.info(other);// other
}
fn('param')
// 매개변수 및 내부 함수, 내부 함수가 우선
function gn(inner){
console.info(inner);// inner() function
console.info(inner()); // 정의되지 않음
function inner(){
return other;
}
var other = 'other'
console.info(inner);// inner() 함수
console.info(inner ());// 기타
}
gn('param')
위 출력을 통해 우선순위를 얻습니다: 내부 함수 선언 > 함수 매개변수 > 내부 변수 선언.
여기서의 프로세스 중 하나는 다음과 같습니다. 먼저 내부 함수 선언을 승격하고 함수 이름의 유형을 함수 유형으로 설정한 다음 함수 매개변수를 구문 분석하고 실제 매개변수 값을 전달합니다. in은 형식 매개변수에 할당되며, 마지막으로 내부 변수 선언 승격은 초기화 없이 선언만 승격합니다. 중복된 이름이 있는 경우 우선순위가 같은 이름은 이전 이름을 덮어쓰고 우선순위가 다른 이름은 덮어쓰지 않습니다. 우선순위가 높은 항목은 구문 분석되었으며, 우선순위가 낮은 항목은 더 이상 구문 분석되지 않습니다.
설명하자면, 이는 출력 결과를 기반으로 한 추론일 뿐이며, 배경 구현의 경우 단계가 완전히 반대이고 각 단계가 이전 단계의 결과를 포함하거나 심지어는 다음 단계에서 시작될 수도 있습니다. 그런 다음 우선순위를 정하여 필요한지 여부를 결정합니다. 물론 효율성 측면에서 보면 커버리지가 더 좋다고 판단한 프로세스입니다. 또한 전역 범위는 실제로 함수 매개변수가 없는 함수 범위의 단순화된 버전입니다.
여기서는 포괄적인 예를 제시하지는 않겠습니다. 이전 기본 문법 기사와 함께 이 기사를 읽으면 더 나은 결과를 얻을 수 있습니다. 우선순위와 적용 범위에 관해서도 아래에서 논의할 질문으로 이어집니다.
4. 함수 오버로딩
함수는 객체이고, 함수 이름은 함수 객체를 가리키는 참조형 변수이므로 우리가 할 수 없습니다. 일반적인 객체지향 언어와 동일합니다. 오버로딩 구현:
function fn(a){
return a;
}
function fn(a,b){
return a b; info(fn(1)); // NaN
console.info(fn(1,2));// 3
왜 8행이 NaN을 출력하는지 궁금하지 마세요. 함수 이름은 단지 변수일 뿐이며 두 함수 선언은 순서대로 구문 분석됩니다. 이 변수가 최종적으로 가리키는 함수는 두 번째 함수이며, 8행에서는 함수 내에서 1개의 매개변수만 전달합니다. , 1에 더해지면 결과는 NaN이 됩니다. 함수 표현식으로 대체하면 이해하기 쉬울 수 있습니다. 당연히 나중에 할당하면 이전 할당을 덮어쓰게 됩니다.
fn = function (a,b){ return a b; }
그렇다면 ECMAScript에서 오버로딩을 어떻게 구현할까요? 간단한 데이터 유형 래퍼 객체(Boolean, Number, String)를 생성자로 사용하여 객체를 생성하고 변환 함수로 사용하여 데이터 유형을 변환할 수 있다는 점을 기억하세요. 사실, 우리는 이전 기사에서 이 오버로딩에 대해 논의했습니다:
(1) 이 방법의 일반적인 형식은 다음과 같습니다:
if(this instanceof fn)
{
/ / 함수 1
}else
{
// 함수 2
}
}
이 방법은 가능하지만 효과는 분명히 제한적입니다. 오버로드는 두 번만 가능하며 생성자만 오버로드할 수 있습니다. 물론, apply()나 call() 또는 심지어 ES5의 새로운 바인딩()을 결합하여 함수 내에서 this 값을 동적으로 바인딩하여 오버로드를 확장할 수 있지만 이는 이미 함수의 내부 속성을 기반으로 한 오버로드를 의미합니다.
(2) 오버로드
var length =args.length;
if(0 == length)//리터럴을 왼쪽에 배치하는 것은 Java에서 가져온 습관입니다. 왜냐하면 비교 연산자가 If 할당으로 작성되기 때문입니다. 연산자(0=길이)가 없으면 컴파일러에서 오류 메시지를 표시합니다. 이 방법이 익숙하지 않으시다면 양해 부탁드립니다.
{
return 0;
}else if(1 == length)
{
return 인수[0]
} else{
return ( 인수[0]) ( 인수[1]);
}
}
console.info(fn());//0
콘솔. info(fn(1));//1
console.info(fn(true));//1
console.info(fn(1,2));//3
콘솔. info(fn('1','2'));//3
여기서는 함수의 내부 속성 인수를 사용하여 오버로딩을 구현합니다. 물론 내부적으로 오버로드하는 방법은 여러 가지가 있으며, typeof, instanceof 등의 연산자를 결합하여 원하는 기능을 구현할 수도 있습니다. 내부 속성 인수는 정확히 무엇입니까? 이것이 제가 아래에서 이야기할 내용입니다.
5. 함수 내부 속성 인수
쉽게 말하면 함수 내부 속성은 함수 본문 내에서만 접근할 수 있는 속성입니다. 함수가 호출될 때 액세스됩니다. 따라서 함수가 호출될 때만 함수의 내부 속성이 구문 분석됩니다. 각 호출에는 해당 구문 분석이 있으므로 동적 특성이 있습니다. 이러한 속성에는 this 및 인수가 포함됩니다. 먼저 인수를 살펴보고 다음 기사에서 이에 대해 이야기하겠습니다.
(1) 함수 정의에 있는 매개변수 목록을 형식 매개변수라고 하며, 함수 호출 시 실제로 전달되는 매개변수를 실제 매개변수라고 합니다. 일반적으로 C 계열 언어에서는 함수를 호출할 때 실제 매개변수가 형식 매개변수와 일치해야 함을 요구하지만, ECMAScript에서는 정의할 때 둘 사이에 제한이 없으며 이를 전달할 때 전달하면 됩니다. 실제 매개변수 2개를 입력하세요. 실제 매개변수 3개를 전달할 수도 있고, 실제 매개변수 1개만 전달할 수도 있고, 매개변수 없이 전달할 수도 있습니다. 이 기능은 오버로딩을 구현하기 위해 함수의 내부 속성을 사용하는 기초입니다.
(2) 형식 매개변수는 동일한 이름을 가질 수도 있지만 실제로 전달되면 나중에 값이 형식 매개변수의 값으로 사용됩니다(이 경우 인수를 사용하여 이전 실제 매개변수):
function gn(a ,a){
console.info(a);
console.info(arguments[0])
console.info(arguments[1]); ,2);//2, 1,2
gn(1);//undefine, 1, undefine
이는 실제로 이 기사 앞부분의 명령문 승격에 대한 결론으로 설명할 수 있습니다. : 동일한 우선순위를 가진 나중 항목이 이전 항목을 덮어쓰고, 함수 매개변수를 구문 분석할 때 값도 동시에 구문 분석됩니다. 물론 이런 방식은 보안에 매우 문제가 되기 때문에 ES5의 strict 모드에서는 이름이 중복되는 형식 매개변수를 금지한다.
(3) 실제 매개변수의 값은 형식 매개변수로 받아들여지는데, 실제 매개변수와 형식 매개변수가 일치하지 않는다면 어떻게 될까요? 정답은 인수를 사용하여 저장하는 것입니다. 실제로 실제 매개변수와 형식 매개변수가 일치하더라도 인수 개체는 여전히 존재하며 실제 매개변수를 수락한 형식 매개변수와 동기화됩니다. 이해를 돕기 위해 이 문장을 다듬어 보겠습니다.
•arguments는 배열과 유사한 객체이며 인수 요소는 인수[0], 인수[1] 등 배열 요소와 마찬가지로 대괄호와 인덱스를 통해 액세스할 수 있습니다.
•arguments는 Object에서 상속된 속성 및 메서드(일부 메서드는 재정의됨) 외에도 길이, 호출 수신자 및 호출자와 같은 자체 속성도 포함합니다. 실제 매개변수의 수(정식 매개변수의 개수? 즉 함수 속성 길이), 호출 수신자는 현재 함수 객체를 나타내며, 호출자는 함수 속성 호출자와 구별하기 위해 정의될 뿐이며 그 값은 정의되지 않습니다.
•arguments는 배열과 유사한 객체이지만 실제 배열 객체는 아닙니다. 인수에 대해 배열 객체 메서드를 직접 호출할 수는 없습니다. 이를 호출하려면 먼저 Array.prototype.slice.call을 사용하면 됩니다. (인수)를 사용하여 배열로 변환합니다.
•인수는 함수가 호출될 때 전달된 실제 매개변수를 저장합니다. 0번째 요소는 첫 번째 실제 매개변수를 저장하고, 첫 번째 요소는 두 번째 실제 매개변수를 저장합니다.
•arguments는 실제 매개변수 값을 저장하고, 형식 매개변수는 둘 사이에 동기화 관계가 있으므로 다른 하나도 이에 따라 수정됩니다.
•인수와 형식 매개변수 간의 동기화는 형식 매개변수가 실제로 실제 매개변수를 수신하는 경우에만 존재합니다. 실제 매개변수를 수신하지 않는 형식 매개변수의 경우 이러한 동기화 관계가 없습니다.
Arguments 객체는 매우 강력하지만 성능면에서 어느 정도 손실이 있기 때문에 필요하지 않으면 형식 매개변수를 우선적으로 사용하는 것이 좋습니다.
코드 복사 코드는 다음과 같습니다.
fn(0,-1);
function fn(para1,para2,para3,para4){
console.info(fn.length);//4, 형식 매개변수 수
console.info(arguments.length);//2, 실제 매개변수 수
console.info(arguments.callee === fn);//true, 호출 수신자 객체가 fn 자체를 가리킴
console.info(arguments.caller);//정의되지 않음
console.info(arguments.constructor);//Array()가 아닌 Object()
try{
arguments.sort();/ /Array-like 결국 배열이 아니고 배열 메서드를 직접 호출할 수 없습니다.
}catch(e){
console.info(e);//TypeError
}
var arr = Array.prototype.slice .call(arguments);//먼저 배열로 변환
console.info(arr.sort());//[-1,0], 이미 정렬됨
console.info( para1);//0
arguments[0] = 1;
console.info(para1);//1, 인수[0]을 수정하면 형식도 동시에 수정됩니다. para1
console.info(arguments[1]);//-1
para2 = 2
console.info(arguments[1]);//2, 형식 매개변수 수정 para2는 동시에 인수[1]
console.info(para3);//정의되지 않음, 전달된 실제 매개변수가 없는 형식 매개변수는 정의되지 않음
arguments[2] =
console입니다. info(arguments[2]);// 3
console.info(para3);//정의되지 않음, 실제 매개변수를 허용하지 않는 형식 매개변수는 동기화 관계가 없습니다
console.info(arguments[3 ]);//정의되지 않음, 실제 매개변수가 전달되지 않음, 값이 정의되지 않음
para4 = 4
console.info(para4);//4
console.info(arguments[3]) ;//정의되지 않은 실제 매개변수가 전달되어 동기화되지 않습니다
}
테스트 후 인수와 형식 매개변수 간의 동기화는 양방향이지만 " JavaScript 고급 프로그래밍(3판)"은 단방향이라고 말합니다. 형식 매개변수를 수정해도 인수는 변경되지 않습니다. 이는 원본 책의 또 다른 버그일 수도 있고 FireFox가 사양을 확장했을 수도 있습니다. 그러나 이는 고전적이라 할지라도 여전히 버그의 가능성이 있으며 모든 것이 실제 작동되어야 한다는 것을 알려줍니다.
//팩토리얼 찾기함수 팩토리얼 (숫자) {
if(숫자 {
return 1;
}else{
return num * 계승(숫자 - 1)
>}
var fn =factorial;
factorial = null;
try{
fn(2);//factorial은 함수 내에서 재귀적으로 호출되고 계승에는 null 값이 할당되었기 때문입니다. , 예외 발생
}catch(e){
console.info(e);//TypeError
}
//피보나치 수열
function fibonacci(num){
if(1 == 숫자 || 2 == 숫자){
return 1;
}else{
return 인수.callee(숫자 - 1) Argument.callee(숫자 - 2);
}
}
var gn = fibonacci;
fibonacci = null;
console.info(gn(9));//34는args.callee를 사용하여 함수 객체를 구현하고 함수명 Decoupling 하면 정상적으로 실행 가능
재귀 알고리즘은 매우 간단하지만 실행 중인 스택을 유지해야 하기 때문에 효율성이 그다지 좋지 않습니다. 재귀 최적화와 관련하여 매우 유용한 알고리즘도 많이 있으므로 여기서는 깊이 다루지 않겠습니다.
코드 복사
코드는 다음과 같습니다.