誰もがたった 1 ~ 2 文でクロージャを暗唱できます。しかし、クロージャは初心者にとって10回の面接のうち8回は遭遇する可能性のある質問で、答えられなければ提案が与えられ、答えられれば得点は得られません。フロントエンド開発を始めても途中で挫折してしまうことを防ぐために、私が考える JS におけるクロージャについて話しましょう。
クロージャとは何ですか?
クロージャは、このスコープ内の変数が参照された後、字句スコープの外で自由にアクセスできる関数と字句環境です。関数が宣言されています。
クロージャは自由変数を参照する関数である、という別の言い方もあります。この自由変数は、関数が作成された環境から分離されている場合でも、関数とともに存在します。したがって、クロージャがコンテキストにバインドされた関数であることがよくありますが、おそらくそれが意味するところです。一度理解すると、それは実際には非常に単純なことであり、それほど奥深いものではないことがわかります。
以下の導入では、クロージャが関数とその関数が宣言される字句環境の組み合わせであるという説明の方がまだ好ましいので、この説明にも基づいて詳しく説明します。
クロージャーは実際にはコンピューターサイエンスの概念であり、JS に固有のものではありません。クロージャの概念は 1960 年代に登場し、クロージャを実装した最初のプログラミング言語は Scheme でした。 (Scheme が何なのかは聞かないでください。聞かれてもわかりません。この段落は Wiki からのコピーです。) その後、クロージャーは関数型プログラミング言語で広く使われるようになりました。
JS のクロージャー
さて、素晴らしいトリックを手に入れました。素手でクロージャーを引き出します。function sayHello(name) { let str = `Hello,${name}`; function say() { console.log(str); } return say; } let myHello = sayHello('abby'); myHello(); // Hello,abby
たとえば
例1: クロージャは必ずしも関数を返す必要はありません一般的なクロージャは関数を返しますが、クロージャは必ずしも返す、返す必要はありません関数は単にアクセスできるようにするだけですスコープ外の変数:let say; function sayHello(name) { let str = `Hello,${name}`; say = function() { console.log(str); } } let myHello = sayHello('abby'); say(); // Hello,abby
let get, up, down function setUp() { let number = 20 get = function() { console.log(number); } up = function() { number += 3 } down = function() { number -=2; } } setUp(); get(); // 20 up(); down(); get(); // 21
function newClosure() { let array = [1, 2]; return function(num) { array.push(num); console.log(`array:${array}`); } } let myClosure = newClosure(); let yourClosure = newClosure(); myClosure(3); // array:1,2,3 yourClosure(4); // array:1,2,4 myClosure(5); // array:1,2,3,5
function newClosure() { for(var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }) } } newClosure(); // 5个5
打印的结果大家也知道是5个5,因为 setTimeout 里面的函数保持对 i 的引用,在setTimeout的回调函数被执行的时候这个循环早已经执行完成,这里我之前在另一篇文章里面做过更深入的介绍:深入浅出Javascript事件循环机制(上)。
这里我要说的是我们如何才能得到我们想要的01234,在这里有两种做法。
一种是 创建一个新的闭包对象,这样每个闭包对象里面的变量就互不影响。例如下面的代码种每次 log(i)都会创建不同的闭包对象,所有的回调函数不会指向同一个环境。
function log(i) { return function() { console.log(i); } } function newClosure() { for(var i = 0; i < 5; i++) { setTimeout(log(i)); } } newClosure(); // 0 1 2 3 4
另一种做法就是使用自执行函数,外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。写法如下:
function newClosure() { for(var i = 0; i < 5; i++) { (function(e) { setTimeout(function() { console.log(e); }) })(i) } } newClosure(); // 0 1 2 3 4
看看,写这么多,多累是不是,还是let省事,所以赶紧拥抱 es6 吧。。。
好了,这次是真的结束了,我所理解的闭包大概就是这样了,如果理解有所偏差,欢迎指出,谁当初不是从颗白菜做起的呢,学习前端的小伙伴们可以看看哦!
关于闭包:
以上がJavaScript でのクロージャの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。