>웹 프론트엔드 >JS 튜토리얼 >실제 매개변수, 형식 매개변수 및 js 함수의 클로저에 대한 이해

실제 매개변수, 형식 매개변수 및 js 함수의 클로저에 대한 이해

不言
不言원래의
2018-07-16 14:45:252047검색

이 글은 js 함수의 실제 매개변수, 형식 매개변수 및 클로저에 대한 이해를 주로 소개합니다. 이제 필요한 친구들이 참고할 수 있도록 공유하겠습니다.

선택적 형식 매개변수

if(a === undefined) a = [];

에 해당합니다.

a = a || [];
이 두 문장은 완전히 동일합니다. 단, 후자는 사전에 a를 선언해야 합니다.

매개변수가 전달되지 않으면 나머지는 정의되지 않은 상태로 채워집니다.
선택적 형식 매개변수: 주석을 통해 /
선택사항/ 강조 매개변수는 선택사항이며 끝에 배치해야 합니다. 그렇지 않으면 null 또는 정의되지 않음을 자리 표시자로 사용하여

가변 길이 실제 매개변수 목록

callee 및 호출자

callee는 현재 실행 중인 함수를 참조합니다.

호출자는 참조합니다. 현재 함수를 실행 중인 함수

객체 속성을 실제 매개변수로 사용

>
> function e(o){
...   return o.Object;
... }
undefined
> e;
[Function: e]
> var a = {Object:33};
undefined
> e(a);
33
>

함수를 값으로

함수를 다른 함수에 값으로 전달할 수 있음

사용자 정의 함수 속성

함수 속성을 사용자 정의할 수 있음

o.a = 3;
function o() {
  return o.a;
}

실제 매개변수, 형식 매개변수 및 js 함수의 클로저에 대한 이해

네임스페이스 함수

함수에 선언된 변수는 함수 본문 전체(중첩 함수 내부 포함)에서 볼 수 있으며 함수 외부에서는 볼 수 없습니다. 함수 내에서 선언되지 않은 변수는 전역 변수입니다. 이는 js 프로그램 전체에서 볼 수 있습니다. js에서는 코드 블록 내에서만 표시되는 변수를 선언할 수 없습니다. 따라서 임시 네임스페이스로 사용할 함수를 정의하는 것이 간단한 경우가 많습니다. 이 네임스페이스 내에 정의된 변수는 전역 네임스페이스를 오염시키지 않습니다.

변수가 전역적으로 정의되면 변수 오염이 발생하여 전역 변수가 오염되고(음, 이는 동적 언어의 함정입니다) 알 수 없는 오류가 발생하기 때문입니다. 그러므로 함수에 변수를 배치하고 호출하게 되면 전역 공간이 오염되고 변수 충돌이 발생하게 됩니다(특히 브라우저 환경에서는 알 수 없는 다양한 오류가 발생하기 쉽기 때문에 이렇게 해야 합니다)

함수를 직접 호출합니다. 함수를 정의한 후

(
  function() {
    return 333;
  }()
);

()를 추가하는 것이 필요합니다. 왜냐하면 ()를 추가하지 않으면 js 인터프리터는 이를 함수 선언으로 간주하고 js 인터프리터는 함수 선언에 따라 함수를 해석하지 않기 때문입니다. 익명 함수 선언 생성을 허용하므로 오류가 보고됩니다.

()를 추가하면 함수 표현식이 되고, js 인터프리터가 실행되어 익명 함수 표현식이 생성됩니다.

Closure

드디어 클로저에 도달했습니다. (심각 포인트 Σ( ° △ °|||)︴)

(가장 어려운 부분이고, 함수형 프로그래밍의 기초이기도 하고, js를 잘 배울 수 있는지를 가늠하는 가장 중요한 부분이기도 합니다... 의 물론 es6에도 짜증나는 화살표 기능이 있습니다)
클로저는 함수형 프로그래밍의 중요한 기반입니다
다른 언어와 마찬가지로 js도 어휘 범위를 채택합니다. 즉, 함수의 실행은 변수의 범위에 따라 달라지며 범위는 다음과 같습니다. 함수가 정의되면 호출에 의해 결정되지 않습니다. 즉, js 함수 개체의 내부 상태는 함수의 코드 논리를 포함할 뿐만 아니라 현재 범위 체인(해당 범위)을 참조해야 합니다. 변수가 전달되면 변수의 스코프 체인 검색 시 함수 상단까지 위쪽으로 검색) 스코프 체인을 통해 함수 객체끼리 서로 연관될 수 있으며,
함수 본문 내부의 변수는 함수 스코프에 저장할 수 있습니다
, 즉 클로저 아주 오래된 용어입니다. 함수 변수는 범위 체인 내에 숨겨질 수 있으므로 함수가 변수를 래핑하는 것처럼 보입니다.

스코프 체인 정의 방법

스코프 체인은 js 함수가 호출될 때마다 새 객체가 생성되어 해당 지역 변수를 저장합니다. , 바인딩된 개체는 범위 체인에서 삭제됩니다. 중첩된 함수가 없고 바인딩된 개체를 가리키는 참조가 없으면 js 인터프리터의 가비지 수집 메커니즘에 의해 수시로 재활용됩니다. 중첩된 함수가 정의된 경우 각 중첩 함수는 범위 체인에 해당하고 이 범위 체인은 변수 바인딩 개체를 가리킵니다. 이러한 중첩 함수 개체가 외부 함수에 저장되면 해당 함수가 가리키는 변수 바인딩 개체처럼 가비지 수집되거나, 함수가 중첩 함수를 정의하고 이를 반환 값으로 반환하거나, 특정 속성에 저장된 경우, 이 중첩된 함수를 가리키는 외부 참조가 있습니다. 즉, 이 함수는 가비지 수집으로 처리되지 않으며 해당 변수에 바인딩된 개체는 가비지 수집으로 처리되지 않습니다.

함수 실행 후 해당 스코프 체인은 삭제되지 않습니다. 더 이상 참조가 없을 때만 삭제됩니다.

실제 매개변수, 형식 매개변수 및 js 함수의 클로저에 대한 이해스택에 대한 지침

원본 스택

스택 상단의 창

다음 js 스크립트를 실행하세요

function a() {
  function f() {
    return 333;
  }
  return f;
}
a()();

栈顶 a → window
开始调用,执行到return
发现需要调用f
继续加栈
栈顶 f → a → window
执行完f弹出f
继续执行a,执行完毕弹出a
最后全部执行完毕弹出window

算了文字解释太无力,直接上代码

var scope = "global scope"; // 一个全局变量
function checkscope() 
{
  
  var scope = "local scope";  // 定义一个局部变量
  
  function f() 
  {
    return scope; // 返回变量作用域中的scope的值
  }
  
  return f(); // 返回这个函数
}

调用一下这个函数

checkscope();
"local scope"

接着这样执行

var scope = "global scope"; // 一个全局变量
function checkscope() 
{
  
  var scope = "local scope";  // 定义一个局部变量
  
  function f() 
  {
    return scope; // 返回变量作用域中的scope的值
  }
  
  return f; // 返回这个函数
}

继续调用函数

checkscope()();
"local scope"

闭包有什么用

先看一个函数uniqueInteger()使用这个函数能够跟踪上次的返回值

var uniqueInteger = (
  function() {
    var count = 0;
    return function() {return count++}
  }()
);

这样子就使用闭包

uniqueInteger();
0
uniqueInteger();
1

每次返回是其上一次的值,并随便直接将值加1
至于为什么要这样写,如果不使用闭包,那么恶意代码就可以随便的将计数器重置了。。

uniqueInteger.count = 0;
function uniqueInteger() {
  return uniqueInteger.count++;
}

类似这样的,完全可以做到直接通过赋值,将其count的值重置。
而如果使用闭包,没有办法进行修改,为私有状态,也不会导致其一个页面内变量的冲突,或者是其覆盖。

立即调用的函数

var a = (function c(){
  var a = 1;
  a++;
  console.log('已经执行');
  return function b(){return a++};
}())

额,我大概解释一下这段代码。
首先呢,解释最外层的圆括号,因为如果没有圆括号,则这个是一个赋值语句,将一个匿名函数赋值给变量a,实际上是在内存中完成了栈中变量a指向匿名函数存储的空间的地址,如果有圆括号,实际上是告诉js解释器这是一个语句,需要js执行,消除了其function带来的影响。(ps;貌似火狐上不加也可以,也可以正常的运行)执行和引用的关系下方有。
然后呢,最后的圆括号,代表着其执行这个函数,因为js解析器将()解析为调用前方的函数名称,类似于运算符吧。但是实际上并不是运算符,因为能往其内传值,注意,这点是将其执行的结果保存在堆中,并完成其指向
其后,当直接输入a;,实际上执行并完成了一次调用,其返回值为函数b,将函数b完成一次引用,即变量a引用函数b,由于其存在引用关系,即栈中变量a保存的为其函数a的返回结果,(因为其不是不是对象,如果写a()()表示将函数a调用后返回的对象保存在栈中,然后将栈中的内容再次调用,由于是保存,并不存在其应用关系,所以执行完毕后直接垃圾回收)由于其保存的是函数b的作用域链,而函数b的作用域链是继承自函数a的作用域链,但是由于函数a的作用域链并没有引用导致其执行完后被垃圾回收(当不在有变量指向的时候)。所以呢,函数其值是在函数b中进行保存,如果修改函数c此时函数c并不会影响到函数b中的保存,因为其函数c的变量列表已被销毁,
最后,继续讨论起嵌套函数的引用,由于其父函数已被销毁,但是嵌套函数被引用,(注意:因为其父已经没有,所以是另开辟一块新的堆空间,用于存储其函数c的返回结果,注意是返回结果,而不是函数b)此时另外指定变量保存其结果,无论指定多少个变量保存其结果,都是新的空间的执行,没有任何的干扰,详细了解看下面,继续讨论

  1. ps;如果是()()则代表其会被其垃圾回收

  2. ps 还需要注意一点点的是由于其引用的是result的值,并不是其

最后,这样就能完成其变量保存在函数中,貌似叫做记忆?

所以呢,借助堆和栈就很好的能理解了闭包

再继续看代码

function count() {
  var n = 0;
  return {
    count: function() { return n++; },
    reset: function() { n = 0; }
  };
}
var c = count(); var d = count();
undefined

在分别执行一下下

c.count();
0
d.count();
0
c.count();
1
d.count();
1
c.reset();
undefined
c.count();
0
d.count();
2

这一点体现了其互不影响性,表明其由于其父被回收,导致其子分别开创了一块在堆中新的内存空间,并完成其指向,互相不干扰。
其作用域链互不干扰

使用getter和setter完成其闭包

function count(n) {
  return {
    get count() { return n++; },
    set count(m) { 
      if ( m >= n)
        n = m;
      else
        throw new Error( '请输入一个正确的值' );
    },
  };
}

这个就不用解释啦,很简单啦

同一个作用域链中定义两个闭包

function test1() {
  val = value = 111;
  this.test = function() { return value - 1; };
  this.test2 = function() { return value + 1; };
  
}

这同样是两个作用链域
不过这样写需要先执行其o.test1(),因为其方法在其函数内部,必须先执行一下,完成其方法的添加,否则会报错,

ee.test is not a function

提示找不到这个方法,
因为执行

ee.test1 = test1;
function test1()

只是简单的进行赋值,并不能进行查看,所以导致其无法使用
所以嘛,要先执行一遍,让其方法添加进去

ee.test1();
undefined
ee.test();
110
ee.test2();
112

这就是两个闭包,这两个闭包互相平行,同时继承于其父,但是又不受其父影响,很神奇吧,(@ο@)

叮 又发现一个莫名奇妙的东东 https://us.leancloud.cn 貌似目前水平能看懂一些了

关于this的问题

this在父闭包显示的即为使用该方法的对象。
但是子就不一定了。

function test1() {
  val = value = 111;
  this.test = function() { return this.x - 1; };
  this.test2 = function() { return this.x + 1; };
}

执行一下

ee.test();
4443

这就尴尬了。
好吧。只能说是一般不这样用
一般这样写

var self = this;

将其值保存进一个self中

相关推荐:

js的函数声明和函数表达式的分析

如何使用JS求数组差集的方法

위 내용은 실제 매개변수, 형식 매개변수 및 js 함수의 클로저에 대한 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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