웹 프론트엔드 JS 튜토리얼 JavaScript 함수 매개변수 및 클로저에 대한 심층적인 이해

JavaScript 함수 매개변수 및 클로저에 대한 심층적인 이해

Dec 22, 2016 pm 01:46 PM
js 기능 매개변수 폐쇄

저는 최근 JavaScript 함수를 배우고 있습니다. JavaScript를 잘 배우고 싶다면 함수에 대한 깊은 이해가 있어야 합니다. 나는 학습 과정을 기사로 구성했는데, 첫째는 나 자신의 기능에 대한 이해를 깊게 하고, 둘째는 독자들에게 학습 방법과 우회 방법을 제공하기 위함이었습니다. 내용은 많지만, 작성자의 기능을 요약한 것입니다.

1. 함수 매개변수

1.1: 매개변수란 무엇인가요

1.2: 매개변수 생략

1.3: 기본 매개변수 값 ​​

1.4 : 매개변수 전달 방법

1.5: 같은 이름의 매개변수

1.6: 인수 객체

2. 클로저

2.1: 클로저 정의

 2.2: 즉시 호출되는 함수 표현식(IIFE, 즉시 호출되는 함수 표현식)

1. 함수 매개변수

1.1: 매개변수란 무엇인가

함수를 정의할 때 , 때로는 추가 데이터를 함수에 전달해야 하는 경우가 있습니다. 이 외부 데이터를 매개변수라고 합니다.

function keith(a){
return a+a;
}
console.log(keith(3)); //6
로그인 후 복사

위 코드에서는 매개변수 a가 keith 함수에 전달되고 a+a 표현식이 반환됩니다.

1.2: 매개변수 생략

함수 매개변수는 필수가 아니며, JavaScript 사양에서는 호출 시 전달되는 실제 매개변수를 생략하도록 허용합니다.

function keith(a, b, c) {
return a;
}
console.log(keith(1, 2, 3)); //1
console.log(keith(1)); //1
console.log(keith()); // 'undefined'
로그인 후 복사

위 코드에서 keith 함수는 세 개의 매개변수를 정의하지만, 호출 시 매개변수가 아무리 많이 전달되더라도 JavaScript에서는 오류를 보고하지 않습니다. 생략된 매개변수의 기본값은 정의되지 않습니다. 함수 정의와 함수 범위를 이해하는 사람은 함수의 길이 속성이 매개변수 수를 반환한다는 것을 알고 있습니다. 길이 속성은 실제 매개변수의 개수와는 아무런 관련이 없으며, 형식적 매개변수의 개수만 반환한다는 점에 유의해야 합니다.

(실제 매개변수: 호출 시 전달되는 매개변수. 형식 매개변수: 정의 시 전달되는 매개변수.)

그런데 앞의 요소만 생략하고 뒤의 요소를 그대로 유지할 수 있는 방법은 없습니다. 앞의 요소를 생략해야 하는 경우 undefound만 표시됩니다.

function keith(a, b) {
return a;
}
console.log(keith(, 1)); //SyntaxError: expected expression, got ','
console.log(keith(undefined, 2)); //'undefined'
로그인 후 복사

위 코드에서 첫 번째 매개변수가 생략되면 브라우저에서 오류를 보고합니다. 정의되지 않음이 첫 번째 매개변수로 전달되면 오류가 보고되지 않습니다.

1.3: 기본값

JavaScript에서는 함수 매개변수의 기본값이 정의되지 않습니다. 그러나 다른 기본값을 설정하는 것이 유용한 상황이 있습니다. 일반적인 전략은 매개변수 값이 정의되지 않았는지 함수 본문에서 테스트하고, 그렇다면 값을 할당하고, 그렇지 않으면 전달된 실제 매개변수의 값을 반환하는 것입니다.

function keith(a, b) {
(typeof b !== 'undefined') ? b = b: b = 1;
return a * b;
}
console.log(keith(15)); //15
console.log(keith(15, 2)) //30
로그인 후 복사

위 코드에서는 판단이 이루어집니다. 호출 시 b 매개변수가 전달되지 않으면 기본값은 1입니다.

ECMAScript 6부터 기본 매개변수가 정의되어 있습니다. 기본 매개변수를 사용하면 함수 본문의 검사가 더 이상 필요하지 않습니다.

function keith(a, b = 1) {
return a * b;
}
console.log(keith(15)); //15
console.log(keith(15, 2)) //30
로그인 후 복사

1.4: 매개변수 전달 방법

함수 매개변수를 전달하는 방법에는 두 가지가 있는데, 하나는 값으로 전달하는 것이고 다른 하나는 주소로 전달하는 것입니다.

함수 매개변수가 원시 데이터 유형(문자열, 숫자 값, 부울 값)인 경우 매개변수 전달 방법은 값을 기준으로 합니다. 즉, 함수 본문 내에서 매개변수 값을 수정해도 함수 외부에는 영향을 미치지 않습니다.

var a = 1;
function keith(num) {
num = 5;
}
keith(a);
console.log(a); //1
로그인 후 복사

위 코드에서 전역 변수 a는 원시형 값이고, keith 함수를 전달하는 방식은 값을 기준으로 합니다. 따라서 함수 내에서 a의 값은 원래 값의 복사본이므로 어떻게 수정하더라도 원래 값에는 영향을 주지 않습니다.

단, 함수 매개변수가 복합형 값(배열, 객체, 기타 함수)인 경우 전달 방식은 참조로 전달됩니다. 즉, 함수에 전달되는 것은 원래 값의 주소이므로 함수 내부의 매개변수를 수정하면 원래 값에 영향을 미치게 됩니다.

var arr = [2, 5];
function keith(Arr) {
Arr[0] = 3;
}
keith(arr);
console.log(arr[0]); //3
로그인 후 복사

위 코드에서 keith 함수에 전달되는 것은 매개변수 객체 arr의 주소입니다. 따라서 함수 내에서 arr의 첫 번째 값을 수정하면 원래 값에 영향을 미칩니다.

함수 내부에서 수정된 내용이 매개변수 개체의 특정 속성이 아니라 매개변수 전체를 대체하는 경우 원래 값에는 영향을 미치지 않는다는 점에 유의하세요.

var arr = [2, 3, 5];
function keith(Arr) {
Arr = [1, 2, 3];
}
keith(arr);
console.log(arr); // [2,3,5]
로그인 후 복사

위 코드에서 keith 함수 내에서 매개변수 개체 arr은 다른 값으로 완전히 대체됩니다. 이때 원래 값은 영향을 받지 않습니다. 이는 형식 매개변수(Arr)와 실제 매개변수 arr 사이에 할당 관계가 있기 때문입니다.

1.5: 동일한 이름의 매개변수

동일한 이름의 매개변수가 있는 경우 마지막 매개변수의 값이 제공되지 않으면 마지막에 나타나는 값을 가져옵니다. 정의되지 않은 상태가 됩니다.

function keith(a, a) {
return a;
}
console.log(keith(1, 3)); //3
console.log(keith(1)); //undefined
로그인 후 복사

같은 이름의 매개변수의 첫 번째 매개변수에 접근하려면 인수 객체를 사용하세요.

function keith(a, a) {
return arguments[0];
}
console.log(keith(2));  //2
로그인 후 복사

1.6 인수 객체

JavaScript의 모든 함수는 특수 변수 인수에 액세스할 수 있습니다. 이 변수는 이 함수에 전달된 모든 매개변수 목록을 유지합니다.

인수 개체에는 함수가 실행될 때 모든 매개 변수가 포함됩니다. 인수[0]은 첫 번째 매개 변수이고 인수[1]은 두 번째 매개 변수입니다. 이 객체는 함수 본문 내에서만 사용할 수 있습니다.

인수 개체의 길이 속성에 액세스하여 함수 호출에 포함되는 매개변수 수를 확인할 수 있습니다.

function keith(a, b, c) {
console.log(arguments[0]); //1
console.log(arguments[2]); //3
console.log(arguments.length); //4
}
keith(1, 2, 3, 4);
로그인 후 복사


인수 객체와 배열의 관계

arguments 对象不是一个数组(Array)。 尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype 继承,实际上它是一个类数组对象。因此,无法对 arguments 变量使用标准的数组方法,比如 push, pop 或者 slice。但是可以使用数组中的length属性。

通常使用如下方法把arguments对象转换为数组。

var arr = Array.prototype.slice.call(arguments);

2.闭包

2.1:闭包定义

要理解闭包,需要先理解全局作用域和局部作用域的区别。函数内部可以访问全局作用域下定义的全局变量,而函数外部却无法访问到函数内部定义(局部作用域)的局部变量。

var a = 1;
function keith() {
 return a;
 var b = 2;
 }
 console.log(keith()); //1
 console.log(b); //ReferenceError: b is not defined
로그인 후 복사

上面代码中,全局变量a可以在函数keith内部访问。可是局部变量b却无法在函数外部访问。

如果需要得到函数内部的局部变量,只有通过在函数的内部,再定义一个函数。

function keith(){
var a=1;
function rascal(){
return a;
}
return rascal;
}
var result=keith();
console.log(result()); //1
function keith(){
var a=1;
return function(){
return a;
};
}
var result=keith();
console.log(result()) //1
로그인 후 복사

上面代码中,两种写法相同,唯一的区别是内部函数是否是匿名函数。函数rascal就在函数keith内部,这时keith内部的所有局部变量,对rascal都是可见的。但是反过来就不行,rascal内部的局部变量,对keith就是不可见的。这就是JavaScript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。函数keith的返回值就是函数rascal,由于rascal可以读取keith的内部变量,所以就可以在外部获得keith的内部变量了。

闭包就是函数rascal,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如rascal记住了它诞生的环境keith,所以从rascal可以得到keith的内部变量。

闭包可以使得它诞生环境一直存在。看下面一个例子,闭包使得内部变量记住上一次调用时的运算结果。

function keith(num) {
return function() {
return num++;
};
}
var result = keith(2);
console.log(result()) //2
console.log(result()) //3
console.log(result()) //4
로그인 후 복사

上面代码中,参数num其实就相当于函数keith内部定义的局部变量。通过闭包,num的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包result使得函数keith的内部环境,一直存在。

通过以上的例子,总结一下闭包的特点:

1:在一个函数内部定义另外一个函数,并且返回内部函数或者立即执行内部函数。

2:内部函数可以读取外部函数定义的局部变量

3:让局部变量始终保存在内存中。也就是说,闭包可以使得它诞生环境一直存在。

闭包的另一个用处,是封装对象的私有属性和私有方法。

function Keith(name) {
var age;
function setAge(n) {
age = n;
}
function getAge() {
return age;
}
return {
name: name,
setAge: setAge,
getAge: getAge
};
}
var person = Keith('keith');
person.setAge(21);
console.log(person.name); // 'keith'
console.log(person.getAge()); //21
로그인 후 복사

2.2:立即调用的函数表达式(IIFE)

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

循环中的闭包

一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号

for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i); //10
}, 1000)
}
로그인 후 복사

上面代码中,不会符合我们的预期,输出数字0-9。而是会输出数字10十次。

当匿名函数被调用的时候,匿名函数保持着对全局变量 i 的引用,也就是说会记住i循环时执行的结果。此时for循环结束,i 的值被修改成了10。

为了得到想要的效果,避免引用错误,我们应该使用IIFE来在每次循环中创建全局变量 i 的拷贝。

for(var i = 0; i < 10; i++) {
 (function(e) {
 setTimeout(function() {
 console.log(e); //1,2,3,....,10
 }, 1000);
 })(i);
 }
로그인 후 복사

外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。


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

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

C++ 람다 표현식에서 클로저의 의미는 무엇입니까? C++ 람다 표현식에서 클로저의 의미는 무엇입니까? Apr 17, 2024 pm 06:15 PM

C++에서 클로저는 외부 변수에 액세스할 수 있는 람다 식입니다. 클로저를 생성하려면 람다 표현식에서 외부 변수를 캡처하세요. 클로저는 재사용성, 정보 숨기기, 지연 평가와 같은 이점을 제공합니다. 이는 클로저가 외부 변수가 파괴되더라도 여전히 접근할 수 있는 이벤트 핸들러와 같은 실제 상황에서 유용합니다.

C++ 람다 표현식에서 클로저를 구현하는 방법은 무엇입니까? C++ 람다 표현식에서 클로저를 구현하는 방법은 무엇입니까? Jun 01, 2024 pm 05:50 PM

C++ Lambda 표현식은 함수 범위 변수를 저장하고 함수에 액세스할 수 있도록 하는 클로저를 지원합니다. 구문은 [캡처 목록](매개변수)->return-type{function-body}입니다. 캡처 목록은 캡처할 변수를 정의합니다. [=]를 사용하여 모든 지역 변수를 값으로 캡처하고, [&]를 사용하여 모든 지역 변수를 참조로 캡처하거나, [변수1, 변수2,...]를 사용하여 특정 변수를 캡처할 수 있습니다. 람다 표현식은 캡처된 변수에만 액세스할 수 있지만 원래 값을 수정할 수는 없습니다.

C++ 함수 매개변수에 대한 자세한 설명: 무기한 매개변수 전달의 구현 방법, 장점 및 단점 C++ 함수 매개변수에 대한 자세한 설명: 무기한 매개변수 전달의 구현 방법, 장점 및 단점 Apr 28, 2024 am 09:48 AM

C++ 무기한 매개변수 전달: 다양한 추가 매개변수를 허용하는... 연산자를 통해 구현됩니다. 이점에는 유연성, 확장성 및 단순화된 코드가 포함되며, 단점에는 성능 오버헤드, 디버깅 어려움 및 유형 안전성이 포함됩니다. 일반적인 실제 예로는 다양한 수의 매개변수를 처리하기 위해 va_list를 사용하는 printf() 및 std::cout이 있습니다.

C++ 함수에서 클로저의 장점과 단점은 무엇입니까? C++ 함수에서 클로저의 장점과 단점은 무엇입니까? Apr 25, 2024 pm 01:33 PM

클로저는 외부 함수의 범위에 있는 변수에 액세스할 수 있는 중첩 함수입니다. 클로저의 장점에는 데이터 캡슐화, 상태 보존 및 유연성이 포함됩니다. 단점으로는 메모리 소비, 성능 영향, 디버깅 복잡성 등이 있습니다. 또한 클로저는 익명 함수를 생성하고 이를 콜백이나 인수로 다른 함수에 전달할 수 있습니다.

테스트에서 golang 함수 클로저의 역할 테스트에서 golang 함수 클로저의 역할 Apr 24, 2024 am 08:54 AM

Go 언어 함수 클로저는 단위 테스트에서 중요한 역할을 합니다. 값 캡처: 클로저는 외부 범위의 변수에 액세스할 수 있으므로 테스트 매개변수를 캡처하고 중첩된 함수에서 재사용할 수 있습니다. 테스트 코드 단순화: 클로저는 값을 캡처함으로써 각 루프에 대해 매개변수를 반복적으로 설정할 필요가 없으므로 테스트 코드를 단순화합니다. 가독성 향상: 클로저를 사용하여 테스트 로직을 구성하고 테스트 코드를 더 명확하고 읽기 쉽게 만듭니다.

함수 포인터와 클로저가 Golang 성능에 미치는 영향 함수 포인터와 클로저가 Golang 성능에 미치는 영향 Apr 15, 2024 am 10:36 AM

함수 포인터와 클로저가 Go 성능에 미치는 영향은 다음과 같습니다. 함수 포인터: 직접 호출보다 약간 느리지만 가독성과 재사용성이 향상됩니다. 클로저: 일반적으로 느리지만 데이터와 동작을 캡슐화합니다. 실제 사례: 함수 포인터는 정렬 알고리즘을 최적화할 수 있고 클로저는 이벤트 핸들러를 생성할 수 있지만 성능 저하를 가져옵니다.

Java에서는 클로저가 어떻게 구현됩니까? Java에서는 클로저가 어떻게 구현됩니까? May 03, 2024 pm 12:48 PM

Java의 클로저를 사용하면 외부 함수가 종료된 경우에도 내부 함수가 외부 범위 변수에 액세스할 수 있습니다. 익명의 내부 클래스를 통해 구현된 내부 클래스는 외부 클래스에 대한 참조를 보유하고 외부 변수를 활성 상태로 유지합니다. 클로저는 코드 유연성을 높이지만 익명 내부 클래스에 의한 외부 변수 참조는 해당 변수를 활성 상태로 유지하므로 메모리 누수의 위험을 인지해야 합니다.

C++ 함수 매개변수에 대한 자세한 설명: 나가는 메커니즘의 본질과 주의사항 C++ 함수 매개변수에 대한 자세한 설명: 나가는 메커니즘의 본질과 주의사항 Apr 27, 2024 pm 12:00 PM

C++에서 함수 매개변수를 전달하는 방법에는 두 가지가 있습니다. 즉, 값에 의한 호출(실제 매개변수에 영향을 주지 않음)과 참조에 의한 호출(실제 매개변수에 영향을 줌)입니다. 매개변수 전달은 참조나 포인터를 전달하여 이루어지며, 함수는 매개변수 참조나 포인터가 가리키는 변수를 수정하여 호출자에게 값을 전달할 수 있습니다. 사용 시 주의할 점: 나가는 매개변수는 명확하게 선언되어야 하며 하나의 실제 매개변수에만 해당할 수 있으며 함수 내의 지역 변수를 가리킬 수 없습니다. 포인터를 전달하여 호출할 때 와일드 포인터를 피하도록 주의하세요.

See all articles