ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript 応用プログラミング(第 3 版)学習ノート 9 js 関数(その 2)_基礎知識

JavaScript 応用プログラミング(第 3 版)学習ノート 9 js 関数(その 2)_基礎知識

WBOY
WBOYオリジナル
2016-05-16 17:49:23935ブラウズ

次に、関数、つまり魔法のオブジェクトを見てみましょう。

9. 値としての関数

一般的なプログラミング言語では、関数を値として使用したい場合は、関数ポインターまたはプロキシを使用する必要があります。ただし、ECMAScript では、関数は独自のプロパティとメソッドを持つ関数に加えて、一般的なオブジェクトのすべての特性を備えたオブジェクトであり、実際、前の例では参照型の値としても使用できます。すでに関数をオブジェクト属性の値として使用しています。たとえば、非同期処理のコールバック関数は、関数を別の関数のパラメーターまたは戻り値として使用することもできます。

コードをコピーします コードは次のとおりです:

var name = 'linjisong' ;
var person = {name:'oulinhai'};
function getName(){
return this.name;
function sum(){
var total = 0 ,
l = argument.length;
for(; l; l--)
{
total = argument[l-1];
return total; >}

// 関数を仮パラメータとして使用して、関数を呼び出す関数を定義します。
function callFn(fn,arguments,scope){
arguments = argument || 🎜>scope =scope window;
return fn.apply(scope, argument);
}
// 関数を実際のパラメータとして使用して callFn を呼び出します
console.info(callFn() getName));//linjisong
console.info(callFn(getName,'',person));//oulinhai
console.info(callFn(sum,[1,2,3,4])) ;//10


関数を戻り値として使用する別の典型的な例を見てください。この例は、元の本の第 5 章からのものです:



コードをコピー
コードは次のとおりです: function createComparisonFunction(propertyName) { return function(object1, object2){
var値1 = オブジェクト1[プロパティ名];
var 値2 = オブジェクト2[プロパティ名];

if (値1 < 値2){
return -1 >} else if (値1 > 値2) ){
return 1;
} else {
return
}
}

var data = [{name: "ザカリー",年齢: 28}, {名前: "ニコラス", 年齢: 29 }];

data.sort(createComparisonFunction("name"));
console.info(data[0].name) ; //ニコラス

data.sort(createComparisonFunction ("age"))
console.info(data[0].name); //ザカリー




10. クロージャ (Closure)

クロージャは、別の関数のスコープ内の変数にアクセスできる関数です。オブジェクトは関数を伴うデータであり、クロージャはデータを伴う関数です。

まず、クロージャは関数であり、次にクロージャはデータを持つ関数です。それでは、どのようなデータを運ぶのでしょうか。戻り値として関数の例を見てみましょう。返されるのは匿名関数です。この無名関数が返されると、外側の関数 createComparisonFunction() のコードが実行されます。スタックからポップされて破棄されましたが、その後の並べ替えでは、createComparisonFunction() のスコープ内の propertyName は、返された匿名関数内で引き続きアクセスできることがわかります。これは、createComparisonFunction() に対応する実行環境が削除されていることを示しています。 destroy ですが、この実行環境に対応するアクティブ オブジェクトは破棄されていませんが、返された匿名関数のスコープ チェーン内のオブジェクトとして使用されます。つまり、返された匿名関数で構成されるクロージャに含まれるデータは次のとおりです。外部関数の対応するアクティビティ オブジェクト。アクティブなオブジェクトのプロパティ (つまり、外部関数で定義された変数、関数、および仮パラメータ) は、外部関数のコードが実行されるにつれて変化するため、最終的に作成される匿名関数で構成されるクロージャに含まれるデータは、返されるのは外側の層です。関数コードの実行が完了した後のアクティブなオブジェクト、つまり最終状態です。
上記の段落をよく理解して、何度も繰り返し理解していただければ幸いです。わかりやすくするために最善を尽くしましたが、クロージャの概念はまだ少し抽象的です。この例は、元の本の第 7 章からのものです。


コードをコピーします


コードは次のとおりです。return result;
}

var funcs = createFunctions();
for (var i=0,l=funcs.length; i
console.info(funcs[ i]());// 各関数は 10 を出力します
}


ここで、クロージャに含まれるデータはcreateFunctionsに対応するアクティブオブジェクトの最終状態であり、createFunctions()コード実行後はアクティブオブジェクトの属性iが10になっているので、以下のそれぞれ呼び出しはすべての関数の出力を返します 10. この問題に対処するには、匿名関数スコープを使用して状態を保存します:
コードをコピーコードは次のとおりです。

function createFunctions(){
var result = new Array();
for (var i=0; i result[ i] = (function(num){
return function(){
return num;
};
})(i);
return result
}

すぐに呼び出される匿名関数を使用して各状態を保存し (匿名関数に対応するアクティブ オブジェクトに保存されます)、最後に返された関数が呼び出されたときに、それを閉じることができます。パッケージに含まれるデータ(対応する無名関数アクティビティオブジェクト内のデータ)が正しくアクセスされ、出力結果は0、1、...9になります。もちろん、これを行うと 10 個のクロージャが作成され、パフォーマンスに大きな影響を与えるため、クロージャは他の実行環境のアクティブなオブジェクトを独自のオブジェクトの一部として保存するため、クロージャを乱用しないことをお勧めします。スコープ チェーン ループが発生し、メモリ リークが発生する可能性もあります。クロージャには効率性とメモリの面で危険性が潜んでいますが、クロージャの機能は強力すぎるので、クロージャの応用を見てみましょう。まず、昨日述べた関数バインディング メソッド binding() に戻りましょう。

(1) 関数のバインディングとカリー化

A. 最初に例を示します (元の本の第 22 章):

コードをコピーします コードは次のとおりです:
「Hello」ボタンをクリックすると、コンソールに何が表示されますか?その理由は、ボタンがクリックされると、ハンドラー関数の内部プロパティ this がボタン オブジェクトを指すためです。この問題を解決するには、クロージャを使用します。

コードをコピー コードは次のとおりです:
btn.onclick = function(event){
handler.handleClick(event);//クロージャを形成します。オブジェクト ハンドラーが関数を呼び出すもので、関数の内部属性 this はハンドラー オブジェクトを指します。イベントが出力されます}

B. 上記の解決策はエレガントではありません。ES5 には、このメソッドを使用して書き換えます。

コードをコピーします コードは次のとおりです。 if(!Function.prototype.bind){//bind は次のとおりです。 ES5 の新機能。正常な動作を確保するために、このメソッドをブラウザに追加する場合はサポートされません。
Function.prototype.bind = function(scope){
var that = this;// binding() メソッドを呼び出します
return function(){
that.apply(scope, argument);//apply メソッドを使用してその関数オブジェクトの内部プロパティを指定します this
}; >};
}
btn.onclick = handler.handleClick.bind( handler);//bind() メソッドを使用する場合、使用する必要があるステートメントは 1 つだけです


ここで追加されたbind()メソッドの主な技術は、実際に関数が呼び出されるときに、バインド中にパラメータを内部属性として保存するクロージャを作成することです。ブラウザ自体がbind()をサポートしているかどうか、またはここでbind()が機能するかどうかが不明な場合は、機能検出の条件判定を削除し、メソッド名を変更してみてください。
C. 上記の関数で binding() メソッドを使用する場合、bind() の呼び出し時に複数のパラメーターが渡され、実際に関数が呼び出されるときに 2 番目のパラメーターが使用されます。 , 次に、デフォルトのパラメーターを関数にバインドできます。



コードをコピー
コードは次のとおりです:

if(!Function.prototype.bind){
Function.prototype.bind = function(scope){
var that = this;//bind() メソッドを呼び出す関数オブジェクト
var args = Array.prototype.slice.call(arguments,1);//第 2 パラメータから始まるパラメータの配列
return function(){
var innerArgs = Array.prototype.slice.apply (arguments);
that.apply(scope, args.concat(innerArgs));//apply メソッドを使用し、その関数オブジェクトの内部プロパティ this を指定し、バインド時に渡されるパラメーターを入力します。 >};
};
}

D. カリー化: 上記のバインドの場合、すべてのパラメーターが呼び出されるときに、最初のパラメーターが使用されます。バインド中のすべては、関数カリー化と呼ばれる事前入力パラメーターとして使用されます。

コードをコピー コードは次のとおりです。
if(!Function.prototype.curry) {
Function.prototype.curry = function(){
var that = this;//curry() メソッドを呼び出す関数オブジェクト
var args = Array.prototype.slice.call(arguments) ;//事前に入力されたパラメータ array
return function(){
var innerArgs = Array.prototype.slice.apply(arguments);//実際に呼び出されたときのパラメータ配列
that.apply(this, args .concat(innerArgs)) ;//apply メソッドを使用し、事前に入力されたパラメータを追加します
}
}


(2) クロージャ キャッシュを使用します

また、フィボナッチ数列を実装するために再帰を使用した関数を覚えていますか?クロージャ キャッシュを使用して書き換えます:


コードをコピーします コードは次のとおりです:
var fibonacci = ( function(){//クロージャ キャッシュ、再帰を使用します。
var queue = [];
function f(n){
if(1 == n || 2 == n){
return 1;
}else{
cache[n] = キャッシュ[n] || (f(n-1) f(n-2));
}
}
return f;
})();

var f2 = function(n){// 直接再帰します
if (1 = = n || 2 == n){
return 1;
}else{
return f2(n-1) f2(n-2);
} ;


以下は、私のマシンでのテスト コードと実行結果です:



コードをコピーします
コードは次のとおりです。 var test = function(n){ var start = new Date().getTime();
console.info(fibonacci(n) );
console.info( new Date().getTime() - start);

start = new Date().getTime(); ;
console.info(new Date().getTime() - start);
test(10);//55,2,55,2
test(20); //6765,1,6765,7
test(30);//832040,2,832040,643


n の値が大きいほど、より明らかであることがわかります。キャッシュ計算を使用する利点。練習として、階乗を計算する関数を自分で変更してみることができます。

(3) ブロックレベルのスコープを模倣する

ECMAScript にはステートメントブロックはありますが、対応するブロックレベルのスコープはありませんが、クロージャを使用してブロックレベルのスコープを模倣することができます。一般的な形式は次のとおりです:




コードをコピー

上記のパターンは、特に次の理由により、すぐに呼び出される関数式とも呼ばれます。 jQuery へのソース コードは、この方法を使用して大規模に普及しました。
クロージャには、プライベート変数やプライベート関数、モジュール パターンの模倣など、多くの興味深い応用例もあります。ここではここでは説明しません。オブジェクトを深く理解した後で、これらの内容を見ていきます。

機能については、インターネット上に優れた記事がたくさんありますので、ご興味があればご自身で検索して読んでみてください。こちらは、「JavaScript Advanced Programming (3rd Edition)」の翻訳者によるお勧めの記事です:
名前付き関数式の探索
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。