js クロージャをさらに理解することができます (詳細)
この記事の内容は、js クロージャを (詳細に) 理解するのに役立ちます。必要な方は参考にしていただければ幸いです。
翻訳者: クロージャについてはよく議論されているので、クロージャを理解していなくても、JS について知っていると言うのは恥ずかしいですが、この記事を読んだとき、私の目は輝きました。また、クロージャについても理解することができ、クラスとプロトタイプ チェーンについてもある程度の知識が得られました。これは 2012 年の記事で、少し早いものですが、内容は非常に明確です。読者に新たな理解をもたらすことができれば幸いです。
クロージャは、JavaScript 言語のやや複雑で誤解されている機能です。簡単に言えば、クロージャはメソッド (関数) と、メソッドが作成されたときの環境への参照を含むオブジェクトです。クロージャを完全に理解するには、js の 2 つの機能も理解する必要があります。1 つはファーストクラス関数、もう 1 つは内部関数です。
第一級関数
js では、メソッドは他のデータ型に簡単に変換できるため、第一級市民です。たとえば、第 1 レベルのメソッドをオンザフライで構築し、変数に割り当てることができます。他のメソッドに渡したり、他のメソッドを通じて返すこともできます。これらの基準を満たすことに加えて、メソッドには独自のプロパティとメソッドもあります。
次の例を通じて、第 1 レベルのメソッドの機能を見てみましょう。
var foo = function() { alert("Hello World!"); }; var bar = function(arg) { return arg; }; bar(foo)();
内部メソッド/内部関数
内部メソッドまたはネストされたメソッドは、外部メソッドが呼び出されるたびに、内部メソッドのインスタンスが作成されます。次の例は、内部メソッドの使用を反映しています。add メソッドは外部メソッドであり、doAdd は内部メソッドです。
function add(value1, value2) { function doAdd(operand1, operand2) { return operand1 + operand2; } return doAdd(value1, value2); } var foo = add(1, 2); // foo equals 3
この例の重要な特徴は、内部メソッドが外部メソッドのスコープを取得することです。これは、内部メソッドが外部メソッドの変数、パラメーターなどを使用できることを意味します。この例では、add() のパラメーター value1 および value2 が doAdd() の operand1 および operand2 パラメーターに渡されます。ただし、doAdd は value1 と value2 を直接取得できるため、これは必須ではありません。したがって、上の例を次のように書くこともできます。
function add(value1, value2) { function doAdd() { return value1 + value2; } return doAdd(); } var foo = add(1, 2); // foo equals 3
クロージャの作成
内部メソッドは外部メソッドのスコープを取得し、クロージャを形成します。典型的なシナリオは、外部関数が内部メソッドを返し、外部環境への参照を保持し、スコープ内のすべての変数を保存するというものです。
次の例は、クロージャを作成して使用する方法を示しています。
function add(value1) { return function doAdd(value2) { return value1 + value2; }; } var increment = add(1); var foo = increment(2); // foo equals 3
説明:
add は内部メソッド doAdd を返し、doAdd は add のパラメータを呼び出し、クロージャが作成されます。
value1 は add メソッドのローカル変数であり、doAdd の非ローカル変数です (非ローカル変数とは、変数が関数本体にも関数本体にも存在しないことを意味します)グローバル世界)、value2 は doAdd のローカル変数です。
add(1) が呼び出されると、クロージャが作成され、クロージャの参照環境に格納されます。value1 は 1 にバインドされ、バインドされた 1 は と等価です。この機能の「ブロッキング」が「クロージャ」の名前の由来でもあります。
increment(2) が呼び出されると、クロージャ関数に入ります。つまり、value1 が 1 である doAdd が呼び出されるため、クロージャは本質的に次の関数とみなすことができます。
##
function increment(value2) { return 1 + value2; }
#クロージャを使用するのはどのような場合ですか?
クロージャは多くの機能を実現できます。たとえば、コールバック関数を指定されたパラメータにバインドします。あなたの生活と成長を容易にする 2 つのシナリオについて話しましょう。- タイマーとの連携
<!DOCTYPE html> <html lang="en"> <head> <title>Closures</title> <meta charset="UTF-8" /> <script> window.addEventListener("load", function() { window.setInterval(showMessage, 1000, "some message<br />"); }); function showMessage(message) { document.getElementById("message").innerHTML += message; } </script> </head> <body> <span id="message"></span> </body> </html>
window.addEventListener("load", function() { var showMessage = getClosure("some message<br />"); window.setInterval(showMessage, 1000); }); function getClosure(message) { function showMessage() { document.getElementById("message").innerHTML += message; } return showMessage; }
ほとんどのオブジェクト指向プログラミング言語は、オブジェクトのプライベート プロパティをサポートしています。ただし、js は純粋なオブジェクト指向言語ではないため、プライベート プロパティの概念はありません。ただし、クロージャを通じてプライベート プロパティをシミュレートできます。クロージャには、クロージャが作成された環境への参照が含まれていることに注意してください。この参照は現在のスコープには含まれていないため、この参照は本質的にはプライベート プロパティです。
次の例を見てください (翻訳者: コードのテキスト説明は省略します):
function Person(name) { this._name = name; this.getName = function() { return this._name; }; }
var person = new Person("Colin"); person._name = "Tom"; // person.getName() now returns "Tom"
没有人愿意不经同意就被别人改名字,为了阻止这种情况的发生,通过闭包让_name字段变成私有。看如下代码,注意这里的_name是Person构造器的本地变量,而不是对象的属性,闭包形成了,因为外层方法Person对外暴露了一个内部方法getName。
function Person(name) { var _name = name;// 注:区别在这里 this.getName = function() { return _name; }; }
现在,当getName被调用,能够保证返回的是最初传入类构造器的值。我们依然可以为对象添加新的_name属性,但这并不影响闭包getName最初绑定的值,下面的代码证明,_name字段,事实私有。
var person = new Person("Colin"); person._name = "Tom"; // person._name is "Tom" but person.getName() returns "Colin"
什么时候不要用闭包?
正确理解闭包如何工作何时使用非常重要,而理解什么时候不应该用它也同样重要。过度使用闭包会导致脚本执行变慢并消耗额外内存。由于闭包太容易创建了,所以很容易发生你都不知道怎么回事,就已经创建了闭包的情况。本节我们说几种场景要注意避免闭包的产生。
1.循环中
循环中创建出闭包会导致结果异常。下例中,页面上有三个按钮,分别点击弹出不同的话术。然而实际运行,所有的按钮都弹出button4的话术,这是因为,当按钮被点击时,循环已经执行完毕,而循环中的变量i也已经变成了最终值4.
<!DOCTYPE html> <html lang="en"> <head> <title>Closures</title> <meta charset="UTF-8" /> <script> window.addEventListener("load", function() { for (var i = 1; i < 4; i++) { var button = document.getElementById("button" + i); button.addEventListener("click", function() { alert("Clicked button " + i); }); } }); </script> </head> <body> <input type="button" id="button1" value="One" /> <input type="button" id="button2" value="Two" /> <input type="button" id="button3" value="Three" /> </body> </html>
去解决这个问题,必须在循环中去掉闭包(译者:这里的闭包指的是click事件回调函数绑定了外层引用i),我们可以通过调用一个引用新环境的函数来解决。下面的代码中,循环中的变量传递给getHandler函数,getHandler返回一个闭包(译者:这个闭包指的是getHandler返回的内部方法绑定传入的i参数),独立于原来的for循环。
function getHandler(i) { return function handler() { alert("Clicked button " + i); }; } window.addEventListener("load", function() { for (var i = 1; i < 4; i++) { var button = document.getElementById("button" + i); button.addEventListener("click", getHandler(i)); } });
2.构造函数里的非必要使用
类的构造函数里,也是经常会产生闭包的错误使用。我们已经知道如何通过闭包设置类的私有属性,而如果当一个方法不需要调用私有属性,则造成的闭包是浪费的。下面的例子中,Person类增加了sayHello方法,但是它没有使用私有属性。
function Person(name) { var _name = name; this.getName = function() { return _name; }; this.sayHello = function() { alert("Hello!"); }; }
每当Person被实例化,创建sayHello都要消耗时间,想象一下有大量的Person被实例化。更好的实践是将sayHello放入Person的原型链里(prototype),原型链里的方法,会被所有的实例化对象共享,因此节省了为每个实例化对象去创建一个闭包(译者:指sayHello),所以我们有必要做如下修改:
function Person(name) { var _name = name; this.getName = function() { return _name; }; } Person.prototype.sayHello = function() { alert("Hello!"); };
需要记得一些事情
闭包包含了一个方法,以及创建它的代码环境引用
闭包会在外部函数包含内部函数的情况下形成
闭包可以轻松的帮助回调函数传入参数
类的私有属性可以通过闭包模拟
类的构造器中使用闭包不是一个好主意,将它们放到原型链中
以上がjs クロージャをさらに理解することができます (詳細)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

使用法: JavaScript では、insertBefore() メソッドを使用して、DOM ツリーに新しいノードを挿入します。このメソッドには、挿入される新しいノードと参照ノード (つまり、新しいノードが挿入されるノード) の 2 つのパラメータが必要です。

JavaScript は Web 開発で広く使用されているプログラミング言語であり、WebSocket はリアルタイム通信に使用されるネットワーク プロトコルです。 2 つの強力な機能を組み合わせることで、効率的なリアルタイム画像処理システムを構築できます。この記事では、JavaScript と WebSocket を使用してこのシステムを実装する方法と、具体的なコード例を紹介します。まず、リアルタイム画像処理システムの要件と目標を明確にする必要があります。リアルタイムの画像データを収集できるカメラ デバイスがあるとします。
