ホームページ >ウェブフロントエンド >jsチュートリアル >JS関数の実パラメータ、仮パラメータ、クロージャの理解

JS関数の実パラメータ、仮パラメータ、クロージャの理解

不言
不言オリジナル
2018-07-16 14:45:252019ブラウズ

この記事では主に、js 関数の実パラメータ、仮パラメータ、クロージャの理解を紹介します。必要な友達に参考にしてもらいます

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

と同等です。

a = a || [];
これら 2 つの文は、後者が事前に を宣言する必要があることを除いて、完全に同等です

パラメータが渡されない場合、残りは未定義で埋められます
オプションの仮パラメータ: コメント /
optional/ を通して強調しますパラメータはオプションであり、最後に配置する必要があります。それ以外の場合は、null または未定義が、

可変長の実パラメータリスト

呼び出し先と呼び出し元に渡すプレースホルダーとして使用されます

呼び出し先は、現在の実行される関数

呼び出し元を参照します現在関数を実行している関数を参照します

オブジェクト属性を実際のパラメータとして使用

>
> 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インタープリターはそれが関数宣言であると認識し、関数宣言に従って関数が解釈されます。 not 匿名関数宣言の作成を許可するため、エラーが報告されます。

()を追加すると関数式となり、jsインタープリターが実行されて匿名関数式が作成されます

クロージャー

ついにクロージャに到達しました。 (重大な点Σ(° △ °|||)︴)

(ここが一番難しい部分であり、関数型プログラミングの基礎であり、jsをしっかり学べるかどうかの最も重要な部分です...もちろん、es6煩わしい矢印関数もあります)
クロージャは関数型プログラミングの重要な基礎です
他の言語と同様、js は字句スコープを採用しています。つまり、関数の実行は変数のスコープに依存し、スコープは関数が実行されるときです。つまり、js 関数オブジェクトの内部状態には、関数のコード ロジックが含まれるだけでなく、現在のスコープ チェーンも参照する必要があります (変数のスコープが渡されます)。変数のスコープチェーンを下方向に検索します。検索する場合は、関数の先頭まで上方向に検索します) 関数オブジェクトはスコープチェーンを通じて相互に関連付けることができ、
関数本体内の変数は関数スコープに保存できます
、つまり、クロージャ非常に古い用語で、関数の変数はスコープチェーン内に隠すことができるため、関数が変数をラップしているように見えます。

スコープチェーンの定義方法

スコープチェーンは、js関数が呼び出されるたびに、そのローカル変数を保存するために新しいオブジェクトが作成され、関数がReturnした場合、このオブジェクトがスコープチェーンに追加されます。ネストされた関数がなく、バインドされたオブジェクトを指す参照がない場合、バインドされたオブジェクトはスコープ チェーンから削除され、JS インタープリターのガベージ コレクション メカニズムによって時々リサイクルされます。入れ子関数が定義されている場合、各入れ子関数はスコープ チェーンに対応し、このスコープ チェーンは変数バインドされたオブジェクトを指します。これらの入れ子関数オブジェクトが外側の関数に保存されている場合、関数が入れ子関数を定義して戻り値として返す場合、または特定の属性に格納されている場合、オブジェクトが指す変数バインディング オブジェクトと同様にガベージ コレクションされます。この入れ子関数を指す外部参照が存在します。つまり、この関数はガベージ コレクションとして扱われず、その変数にバインドされたオブジェクトもガベージ コレクションとして扱われません。

関数の実行後、関連するスコープチェーンは削除されません。

スタックに関する説明JS関数の実パラメータ、仮パラメータ、クロージャの理解

上部のウィンドウ。 stack

以下の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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。