JavaScript の関数は、Java のメソッドと同様の言語機能であるだけでなく、オブジェクトとしても存在できます。 この記事では、call、apply、bind の 3 つのプロトタイプ メソッドを含む、JavaScript での関数の特殊な使用法をいくつか説明します。
1. 関数の基本
JavaScript の関数は Java のメソッドに似た言語機能ですが、クラスとは独立して定義できます。
関数型プログラミング: JavaScript は匿名関数をサポートしているため、関数をオブジェクトとして使用できます。したがって、JavaScript は手続き型プログラミング (オブジェクト指向も手続き型プログラミングの一種です) だけでなく、関数型プログラミングもサポートしています。
コンテキスト
関数への各呼び出しには特別な値 (この呼び出しのコンテキスト) があり、これが this キーワードの値です。 関数がオブジェクトのプロパティとしてオブジェクトに実装されている場合、その関数はオブジェクトのメソッドと呼ばれます。このオブジェクトを通じて関数が呼び出される場合、オブジェクトは呼び出しのコンテキスト、つまり関数の this の値になります。
これはキーワードであり、変数や属性名ではないことに注意してください。 JavaScript 構文では、これに値を割り当てることはできません。
関数はオブジェクトの一種です
JavaScript の関数と Java のメソッドまたは C 言語の関数との最大の違いは、JavaScript の関数もオブジェクトであるということです。 しかし、これはすべてのオブジェクトが関数であるという意味ではありません。関数は、実行可能コードを含む特別なオブジェクトであり、他のコードから呼び出すことができます。
変数とは異なり、キーワード this にはスコープ制限がなく、入れ子関数はそれを呼び出す関数から this を継承しません。 - ネストされた関数がメソッドとして呼び出された場合、その this 値はそれを呼び出したオブジェクトを指します。 - ネストされた関数が関数として呼び出された場合、この値はグローバル オブジェクト (非厳密モードの場合) または未定義 (厳密モードの場合) のいずれかになります。
多くの人は、入れ子関数を呼び出すとき、これが外側の関数のコンテキストを指すと誤解しています。この外部関数の this 値にアクセスしたい場合は、この変数と内部関数の両方が同じスコープ内にある変数に this の値を保存する必要があります。例:
var o = { m: function() { var self = this; console.log(this==o); // true f(); function f() { console.log(this === o); // false,this的值是全局对象或undefined console.log(self === o); // true } } }
閉店
JavaScript 関数は、他の関数内にネストして定義できるため、定義されているスコープ内の任意の変数にアクセスできます。 これは、JavaScript 関数がクロージャを形成し、JavaScript に非常に強力なプログラミング機能をもたらすことを意味します。
値の関数として
JavaScript では、関数は構文であるだけでなく、値でもあります。つまり、関数は変数に割り当てられ、オブジェクトのプロパティまたは配列の要素に保存され、別の関数に渡されます。パラメータなど
バインド、呼び出し、適用
各関数には、オブジェクトへの参照であるプロトタイプ属性が含まれています。このオブジェクトは「プロトタイプ オブジェクト」と呼ばれます。 各関数には異なるプロトタイプ オブジェクトが含まれています。関数をコンストラクターとして使用する場合、新しく作成されたオブジェクトはプロトタイプ オブジェクトからプロパティを継承します。
Function.prototype.call() および Function.prototype.apply()
call() と apply() はオブジェクトのメソッドとみなすことができ、メソッドを呼び出すことで間接的に関数が呼び出されます。 最初のパラメーターは、呼び出される関数の親オブジェクト (呼び出しコンテキスト) であり、その参照は関数本体内のこれを通じて取得されます。 apply() メソッドは、関数の転送方法がすべて配列に配置されることを除いて、call() メソッドと同じ機能を持ちます。
たとえば、オブジェクト o のメソッドとして関数 f() を呼び出し、2 つのパラメーターを渡すには、次のようなコードを使用できます。
var o = {}; function f(a, b) { return a + b; } f.call(o, 1, 2); // 将函数f作为o的方法,实际上就是重新设置函数f的上下文 f.apply(o, [1, 2]);
別の例として、call メソッドを使用して匿名関数を呼び出します。
次の例の for ループ本体では、無名関数を作成し、各配列要素を指定された this 値として使用して、関数の call メソッドを呼び出して無名関数を実行します。 この匿名関数の主な目的は、各配列要素オブジェクトに print メソッドを追加して、配列内の各要素の正しいインデックス番号を出力することです。 もちろん、この値として配列要素を匿名関数に渡す必要はありません (通常のパラメーターで十分です)。呼び出しの使用法を示すことが目的です。
var animals = [ {species: 'Lion', name: 'King'}, {species: 'Whale', name: 'Fail'} ]; for (var i = 0; i < animals.length; i++) { (function (i) { this.print = function () { console.log('#' + i + ' ' + this.species + ': ' + this.name); } this.print(); }).call(animals[i], i); }
Function.prototype.bind()
bind() は ES5 の新しいメソッドです。名前からわかるように、このメソッドの主な機能は関数をオブジェクトにバインドすることです。 関数 f() で binding() メソッドを呼び出し、オブジェクト o をパラメータとして渡すと、このメソッドは新しい関数を返します。(関数呼び出しの形式で) 新しい関数を呼び出すと、元の関数 f が置き換えられます。 () o のメソッドとして呼び出されます。例:
function f(y) { return this.x + y; } var o = { x: 1 }; var g = f.bind(o); // 通过调用 g(x) 来调用 o.f(x) g(2); // 3
実際、bind() メソッドは簡単に実装できます。
// 返回一个函数,通过调用它来调用o中的方法f(),传递它所有的实参 function bind(f, o) { if (f.bind) return f.bind(o); // 如果bind()方法存在,使用bind()方法 else return function () { return f.apply(o, arguments); } }
二、函数式编程
JavaScript并非函数式编程语言,但在JavaScript中可以像操控对象一样操控函数,也就是说可以在JavaScript中应用函数式编程技术。
使用函数处理数组
假设有一个数组,数组元素都是数字,我们想要计算这些元素的平均值和标准差。
var data = [1, 1, 3, 5, 5]; var sum = function(x, y) { return x + y; }; var square = function(x) { return x * x; }; var mean = data.reduce(sum)/data.length; var deviations = data.map(x => x - mean);
var stddev = Math.sqrt(deviations.map(square).reduce(sum)/(data.length - 1));
高阶函数
高阶函数就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数。举个例子:
function not(f) { return function () { var result = f.apply(this, arguments); return !result; }; } // 判断x是否为偶数的函数 var even = function(x) { return x % 2 === 0; }; var odd = not(even); // 一个新函数,所做的事情和even()相反 [1, 1, 3, 5, 5].every(odd); // true,每个函数都是奇数
函数not()是个高阶函数,因为它返回一个新的函数,这个新函数将它的实参传入f(),并返回f的返回值的逻辑非。
以上就是关于javascript的call()、apply()、bind()的用法,希望对大家的学习有所帮助。