サーチ…
前書き
JavaScriptの関数は、一連のアクションを実行するための整理された再利用可能なコードを提供します。関数はコーディングプロセスを簡素化し、冗長ロジックを防止し、コードを簡単にフォローします。このトピックでは、JavaScriptの関数、引数、パラメータ、return文およびスコープの宣言と使用について説明します。
構文
関数の例(x){return x}
var example = function(x){return x}
(関数() { ... })(); //直ちに呼び出される関数式(IIFE)
var instance = new例(x);
メソッド
fn.apply(valueForThis [、arrayOfArgs])
fn.bind(valueForThis [、arg1 [、arg2、...]])
fn.call(valueForThis [、arg1 [、arg2、...]])
ES2015 +(ES6 +):
const example = x => {return x}; //矢印関数explicit return
const example = x => x; //矢印関数は暗黙の復帰
const example =(x、y、z)=> {...} // Arrowは複数の引数を扱います
(()=> {...})(); //矢印関数を使用したIIFE
備考
矢印機能の詳細については、 矢印機能のドキュメントを参照してください。
変数としての機能
通常の関数宣言は次のようになります。
function foo(){
}
このように定義された関数は、コンテキスト内のどこからでもその名前でアクセスできます。しかし、オブジェクト参照のような関数参照を扱うと便利なことがあります。たとえば、いくつかの条件セットに基づいて変数にオブジェクトを割り当て、後で一方または他方のオブジェクトからプロパティを取得することができます。
var name = 'Cameron';
var spouse;
if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };
var spouseName = spouse.name;
JavaScriptでは、関数でも同じことができます:
// Example 1
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };
hash('Fred');
上記の例では、 hash
は通常の変数です。関数への参照が割り当てられ、その後、関数が参照する関数は、通常の関数宣言と同様にカッコを使用して呼び出すことができます。
上記の例は、独自の名前を持たない無名関数...関数を参照しています。変数を使用して名前付き関数を参照することもできます。上記の例は次のように書き直すことができます:
// Example 2
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = sha1Hash;
else if ( hashAlgorithm === 'md5' ) hash = md5Hash;
hash('Fred');
function md5Hash(value){
// ...
}
function sha1Hash(value){
// ...
}
または、オブジェクトプロパティから関数参照を割り当てることができます。
// Example 3
var hashAlgorithms = {
sha1: function(value) { /**/ },
md5: function(value) { /**/ }
};
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = hashAlgorithms.sha1;
else if ( hashAlgorithm === 'md5' ) hash = hashAlgorithms.md5;
hash('Fred');
括弧を省略することで、ある変数が保持する関数への参照を別の変数に代入することができます。これは、関数の戻り値を別の変数に代入しようとしたが誤ってその関数への参照を割り当てようとすると、簡単に間違いが起きる可能性があります。
// Example 4
var a = getValue;
var b = a; // b is now a reference to getValue.
var c = b(); // b is invoked, so c now holds the value returned by getValue (41)
function getValue(){
return 41;
}
関数への参照は他の値と同じです。ご覧のとおり、参照を変数に割り当てることができ、その変数の参照値は後で他の変数に割り当てることができます。関数への参照を別の関数の戻り値として渡すなど、他の値と同様に関数への参照を渡すことができます。例えば:
// Example 5
// getHashingFunction returns a function, which is assigned
// to hash for later use:
var hash = getHashingFunction( 'sha1' );
// ...
hash('Fred');
// return the function corresponding to the given algorithmName
function getHashingFunction( algorithmName ){
// return a reference to an anonymous function
if (algorithmName === 'sha1') return function(value){ /**/ };
// return a reference to a declared function
else if (algorithmName === 'md5') return md5;
}
function md5Hash(value){
// ...
}
変数を呼び出すには、変数への関数参照を割り当てる必要はありません。この例題は、例5に基づいていますが、getHashingFunctionを呼び出して、すぐに返された関数を呼び出し、その戻り値をhashedValueに渡します。
// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );
ホイストに関する注意
通常の関数宣言とは異なり、関数を参照する変数は "ホイスト"されません。例2では、 md5Hash
関数とsha1Hash
関数はスクリプトの最後に定義されていますが、すぐにどこでも使用できます。関数をどこで定義しても、インタプリタはその関数をスコープの先頭に「持ち上げ」、すぐに利用できるようにします。これは変数定義では当てはまりませんので、次のようなコードは中断します:
var functionVariable;
hoistedFunction(); // works, because the function is "hoisted" to the top of its scope
functionVariable(); // error: undefined is not a function.
function hoistedFunction(){}
functionVariable = function(){};
匿名機能
匿名関数の定義
関数が定義されているときは、その関数に名前をつけ、その名前を使って関数を呼び出すことができます。
foo();
function foo(){
// ...
}
このようにして関数を定義すると、Javascriptランタイムは関数をメモリに保存し、割り当てられた名前を使用してその関数への参照を作成します。その名前は現在のスコープ内でアクセス可能です。これは関数を作成するのに非常に便利な方法ですが、Javascriptでは関数に名前を割り当てる必要はありません。以下は完全に合法です。
function() {
// ...
}
関数が名前なしで定義されるとき、それは無名関数として知られています。この関数はメモリに保存されていますが、実行時に自動的にリファレンスが作成されるわけではありません。一見すると、あたかもそのようなものが役に立たないように見えるかもしれませんが、無名関数が非常に便利ないくつかのシナリオがあります。
変数への匿名関数の割り当て
無名関数の非常に一般的な使い方は、それらを変数に代入することです:
var foo = function(){ /*...*/ };
foo();
このような匿名関数の使用については、「 関数としての関数」で詳しく説明しています。
パラメータとしての匿名関数の別の関数への供給
一部の関数は、関数への参照をパラメータとして受け入れることができます。これらは、「依存関係注入」または「コールバック」と呼ばれることもあります。これは、呼び出し元の関数がコードに「コールバック」できるようにして、呼び出された関数の振る舞いを変更する機会を与えるためです。たとえば、Arrayオブジェクトのmap関数を使用すると、配列の各要素を反復処理し、各要素に変換関数を適用して新しい配列を作成できます。
var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]
この1つの場所でのみ必要な関数でスコープを乱雑にし、自然な流れとコードの読み込みを破る名前付き関数を作成するのは、面倒で冗長で不要です(同僚はこのコードを残して、何が起こっているのかを理解する機能)。
別の関数からの匿名関数の返却
関数を別の関数の結果として返すと便利なことがあります。例えば:
var hash = getHashFunction( 'sha1' );
var hashValue = hash( 'Secret Value' );
function getHashFunction( algorithm ){
if ( algorithm === 'sha1' ) return function( value ){ /*...*/ };
else if ( algorithm === 'md5' ) return function( value ){ /*...*/ };
}
即座に匿名関数を呼び出す
他の多くの言語とは異なり、Javascriptのスコープはブロックレベルではなく、ファンクションレベルです。 ( 関数スコープを参照)。ただし、場合によっては新しいスコープを作成する必要があります。たとえば、変数名をグローバルスコープで定義するのではなく、 <script>
タグでコードを追加するときに新しいスコープを作成するのが一般的です(他のスクリプトが変数名と衝突するリスクがあります)。このような状況を処理する一般的な方法は、新しい匿名関数を定義してすぐに呼び出すことです。匿名関数の範囲内で変数を安全に隠し、漏れ関数名を使用してコードを第三者にアクセスさせることはありません。例えば:
<!-- My Script -->
<script>
function initialize(){
// foo is safely hidden within initialize, but...
var foo = '';
}
// ...my initialize function is now accessible from global scope.
// There's a risk someone could call it again, probably by accident.
initialize();
</script>
<script>
// Using an anonymous function, and then immediately
// invoking it, hides my foo variable and guarantees
// no one else can call it a second time.
(function(){
var foo = '';
}()) // <--- the parentheses invokes the function immediately
</script>
自己参照無名関数
無名関数がそれ自身を参照できるようにするのが便利なことがあります。たとえば、関数は再帰的に自身を呼び出したり、自身にプロパティを追加したりする必要があります。しかし、関数が匿名である場合、関数が割り当てられている変数の知識を必要とするため、これは非常に困難です。これは理想的ではないソリューションです。
var foo = function(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) foo(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Bad.
ここでの意図は、無名関数が再帰的に自分自身を呼び出すためですが、fooの値が変わると、バグを追跡するのが難しくなります。
そうではなく、私たちは無名関数に自分自身への参照を与えることができます。
var foo = function myself(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) myself(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Whassup?
関数名はそれ自身のスコープであることに注意してください。名前は外側の範囲に漏れていません:
myself(false); // ReferenceError: myself is not defined
このテクニックは、再帰的な無名関数をコールバックパラメータとして扱うときに特に便利です。
// Calculate the fibonacci value for each number in an array:
var fib = false,
result = [1,2,3,4,5,6,7,8].map(
function fib(n){
return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 );
});
// result = [1, 1, 2, 3, 5, 8, 13, 21]
// fib = false (the anonymous function name did not overwrite our fib variable)
直ちに呼び出される関数式
場合によっては、あなたの関数を変数としてアクセス可能/格納することを望まないことがあります。すぐ呼び出される関数式(IIFE)を作成することができます。これらは基本的に自己実行型の匿名関数です。彼らは周囲のスコープにアクセスできますが、関数自体と内部変数は外部からはアクセスできません。 IIFEについての重要なことは、関数の名前を指定しても、IIFEは標準関数のように吊り上げられておらず、宣言されている関数名で呼び出すことができないということです。
(function() {
alert("I've run - but can't be run again because I'm immediately invoked at runtime,
leaving behind only the result I generate");
}());
これはIIFEを書く別の方法です。セミコロンの前の閉じ括弧は、閉じた中括弧の直後に移動して配置されています。
(function() {
alert("This is IIFE too.");
})();
パラメータをIIFEに簡単に渡すことができます:
(function(message) {
alert(message);
}("Hello World!"));
さらに、値を周囲のスコープに返すこともできます。
var example = (function() {
return 42;
}());
console.log(example); // => 42
必要に応じて、IIFEの名前を付けることができます。あまり頻繁には見られませんが、このパターンには、再帰に使用できるリファレンスを提供するなど、いくつかの利点があります。また、名前がコールスタックに含まれるため、デバッグが簡単になります。
(function namedIIFE() {
throw error; // We can now see the error thrown in 'namedIIFE()'
}());
関数が括弧で囲まれているのは、式が期待される場所でJavascriptパーサに表現する最も一般的な方法ですが、式が既に予想されている場所では、表記を簡潔にすることができます。
var a = function() { return 42 }();
console.log(a) // => 42
直ちに呼び出される関数の矢印バージョン:
(() => console.log("Hello!"))(); // => Hello!
関数スコープ
関数を定義すると、 スコープが作成されます 。
関数内で定義されたすべての関数は、関数外のコードからアクセスできません。このスコープ内のコードだけがスコープ内で定義されたエンティティを見ることができます。
function foo() {
var a = 'hello';
console.log(a); // => 'hello'
}
console.log(a); // reference error
JavaScriptでは入れ子関数が使用でき、同じ規則が適用されます。
function foo() {
var a = 'hello';
function bar() {
var b = 'world';
console.log(a); // => 'hello'
console.log(b); // => 'world'
}
console.log(a); // => 'hello'
console.log(b); // reference error
}
console.log(a); // reference error
console.log(b); // reference error
JavaScriptが参照または変数を解決しようとすると、現在のスコープ内で参照または変数を探し始めます。現在のスコープでその宣言を見つけることができない場合は、それを探すために1つのスコープを登ります。このプロセスは、宣言が見つかるまで繰り返されます。 JavaScriptパーサがグローバルスコープに達しても参照を見つけることができない場合は、参照エラーがスローされます。
var a = 'hello';
function foo() {
var b = 'world';
function bar() {
var c = '!!';
console.log(a); // => 'hello'
console.log(b); // => 'world'
console.log(c); // => '!!'
console.log(d); // reference error
}
}
このクライミングの振る舞いは、外側のスコープ内の類似した名前の参照が、最初に参照されているため、ある参照が「陰になる」ことを意味します。
var a = 'hello';
function foo() {
var a = 'world';
function bar() {
console.log(a); // => 'world'
}
}
JavaScriptがスコープを解決する方法は、 const
キーワードにも当てはまります。 const
キーワードで変数を宣言すると、値を再割り当てすることはできませんが、関数内で宣言すると新しいスコープが作成され、新しい変数が作成されます。
function foo() {
const a = true;
function bar() {
const a = false; // different variable
console.log(a); // false
}
const a = false; // SyntaxError
a = false; // TypeError
console.log(a); // true
}
しかし、関数は、 let
またはconst
を使用している場合は、スコープを作成する唯一のブロックではありません。 let
宣言とconst
宣言には最も近いブロック文のスコープがあります。より詳細な説明については、 ここを参照してください。
`this`と引数をバインドする
JavaScriptのメソッド(関数であるプロパティ)への参照を取得するとき、通常、最初にアタッチされたオブジェクトは覚えていません。方法として、そのオブジェクトを参照する必要がある場合this
ことはできなくなりますし、それを呼び出すと、おそらくクラッシュの原因となります。
関数で.bind()
メソッドを使用すると、 this
と任意の数の先行引数の値を含むラッパーを作成できます。
var monitor = {
threshold: 5,
check: function(value) {
if (value > this.threshold) {
this.display("Value is too high!");
}
},
display(message) {
alert(message);
}
};
monitor.check(7); // The value of `this` is implied by the method call syntax.
var badCheck = monitor.check;
badCheck(15); // The value of `this` is window object and this.threshold is undefined, so value > this.threshold is false
var check = monitor.check.bind(monitor);
check(15); // This value of `this` was explicitly bound, the function works.
var check8 = monitor.check.bind(monitor, 8);
check8(); // We also bound the argument to `8` here. It can't be re-specified.
厳密なモードでない場合、関数がメソッド、バインド、またはメソッド.call
構文で呼び出されない限り、関数はグローバルオブジェクト(ブラウザのwindow
)をthis
に.call
ます。
window.x = 12;
function example() {
return this.x;
}
console.log(example()); // 12
strictモードでは、 this
はデフォルトではundefined
です
window.x = 12;
function example() {
"use strict";
return this.x;
}
console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
バインド演算子
二重コロンのbind演算子は、上で説明した概念の短縮構文として使用できます。
var log = console.log.bind(console); // long version
const log = ::console.log; // short version
foo.bar.call(foo); // long version
foo::bar(); // short version
foo.bar.call(foo, arg1, arg2, arg3); // long version
foo::bar(arg1, arg2, arg3); // short version
foo.bar.apply(foo, args); // long version
foo::bar(...args); // short version
この構文は、結合を心配することなく、あなたが正常に書き込むことができthis
どこでも。
コンソール関数を変数にバインドする
var log = console.log.bind(console);
使用法:
log('one', '2', 3, [4], {5: 5});
出力:
one 2 3 [4] Object {5: 5}
どうしてそうするか?
1つのユースケースは、カスタムロガーがあり、実行時にどちらを使用するかを決定したい場合に使用できます。
var logger = require('appLogger');
var log = logToServer ? logger.log : console.log.bind(console);
関数引数、 "arguments"オブジェクト、restおよびspreadパラメータ
関数は、独自のスコープ内で使用および割り当てできる変数の形式で入力を受け取ることができます。次の関数は2つの数値をとり、その合計を返します。
function addition (argument1, argument2){
return argument1 + argument2;
}
console.log(addition(2, 3)); // -> 5
arguments
オブジェクト
arguments
オブジェクトには、 デフォルト以外の値を含むすべての関数のパラメータが含まれます 。パラメータが明示的に宣言されていない場合でも使用できます。
(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]
arguments
出力するとき、出力は配列に似ていますが、実際にはオブジェクトです。
(function() { console.log(typeof arguments) })(); // -> object
残りのパラメータ: function (...parm) {}
ES6では、 ...
構文を関数のパラメータの宣言で使用すると、変数を右に変換して、宣言されたものの後に提供される残りのすべてのパラメータを含む単一のオブジェクトに変換します。これにより、この変数の一部となる無制限の引数で関数を呼び出すことができます。
(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123
スプレッドパラメータ: function_name(...varb);
ES6では、 ...
構文は、オブジェクト/変数を右に置いて関数を呼び出すときにも使用できます。これにより、そのオブジェクトの要素をその関数に単一のオブジェクトとして渡すことができます。
let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42
名前付き関数
関数は名前を付けることも名前を付けないこともできます( 匿名関数 )。
var namedSum = function sum (a, b) { // named
return a + b;
}
var anonSum = function (a, b) { // anonymous
return a + b;
}
namedSum(1, 3);
anonSum(1, 3);
4
4
しかし、彼らの名前は独自の範囲に限定されています。
var sumTwoNumbers = function sum (a, b) {
return a + b;
}
sum(1, 3);
Uncaught ReferenceError:合計が定義されていません
名前付き関数は、複数のシナリオでの匿名関数と異なります。
- デバッグ中は、関数の名前がエラー/スタックトレースに表示されます
- 名前の付いた関数は持ち上げられ 、匿名の関数は呼び出されません。
- 名前付き関数と匿名関数は、再帰を処理するときの動作が異なります
- ECMAScriptのバージョンによっては、名前付きおよび匿名関数は、関数
name
プロパティを異なる方法で扱うことがあります
指定された関数が呼び出されます。
無名関数を使用する場合、関数は宣言の行の後にのみ呼び出すことができますが、名前付き関数は宣言の前に呼び出すことができます。検討する
foo();
var foo = function () { // using an anonymous function
console.log('bar');
}
捕捉されないTypeError:fooは関数ではありません
foo();
function foo () { // using a named function
console.log('bar');
}
バー
再帰的シナリオでの名前付き関数
再帰関数は次のように定義できます。
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
//you could call 'say' directly,
//but this way just illustrates the example
var sayHelloTimes = say;
sayHelloTimes(2);
こんにちは!
こんにちは!
あなたのコードのどこかに元の関数バインディングが再定義されたらどうでしょうか?
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
こんにちは!
捕捉されないTypeError:sayは関数ではありません
これは、名前付き関数を使用して解決できます
// The outer variable can even have the same name as the function
// as they are contained in different scopes
var say = function say (times) {
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it uses the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
こんにちは!
こんにちは!
また、ボーナスとして、指定された関数を内部からでもundefined
に設定することはできません:
var say = function say (times) {
// this does nothing
say = undefined;
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it's using the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
こんにちは!
こんにちは!
関数のname
プロパティ
ES6以前では、名前付き関数のname
プロパティは関数名に設定され、匿名関数のname
プロパティは空文字列に設定されていました。
var foo = function () {}
console.log(foo.name); // outputs ''
function foo () {}
console.log(foo.name); // outputs 'foo'
ES6以降の名前付き関数と名前なし関数は、どちらもname
プロパティを設定します。
var foo = function () {}
console.log(foo.name); // outputs 'foo'
function foo () {}
console.log(foo.name); // outputs 'foo'
var foo = function bar () {}
console.log(foo.name); // outputs 'bar'
再帰関数
再帰関数は単なる関数であり、それ自体を呼び出すものです。
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
上記の関数は、階乗を返す再帰関数を実行する基本的な例を示しています。
もう1つの例は、配列内の偶数の合計を取得することです。
function countEvenNumbers (arr) {
// Sentinel value. Recursion stops on empty array.
if (arr.length < 1) {
return 0;
}
// The shift() method removes the first element from an array
// and returns that element. This method changes the length of the array.
var value = arr.shift();
// `value % 2 === 0` tests if the number is even or odd
// If it's even we add one to the result of counting the remainder of
// the array. If it's odd, we add zero to it.
return ((value % 2 === 0) ? 1 : 0) + countEvens(arr);
}
このような関数が、無限ループを避けるために何らかのセンチネル値チェックを行うことが重要です。上記の最初の例では、 n
が1以下の場合、再帰が停止し、各呼び出しの結果が呼び出しスタックに返されます。
カッシング
カリングとは、 n
アーリー・ファンクションまたはn
アーギュメントの関数を、ただ1つのアーギュメントを取るn
ファンクションのシーケンスに変換することです。
ユースケース:いくつかの引数の値が他のものよりも前に利用可能な場合、カリングを使用して関数を一連の関数に分解し、各値が到着するごとに段階的に作業を完了できます。これは便利です:
- 引数の値がほとんど変わらない場合(たとえば変換係数)、値をハードコーディングするのではなく、その値を設定する柔軟性を維持する必要がある場合。
- 他のカルト関数が実行される前に、カルト関数の結果が有用な場合。
- 特定の順序で関数の到着を検証する。
例えば、直角プリズムの体積は、長さ( l
)、幅( w
)、および高さ( h
)の3つの因子の関数によって説明することができる。
var prism = function(l, w, h) {
return l * w * h;
}
この関数のカーリーバージョンは次のようになります:
function prism(l) {
return function(w) {
return function(h) {
return l * w * h;
}
}
}
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;
これらの関数のシーケンスは、 prism(2)(3)(5)
で呼び出すことができます。これは30と評価されます。
余分な機械(ライブラリなど)がなければ、プレースホルダの値が不足しているためJavaScript(ES 5/6)では構文上の柔軟性が制限されています。したがって、 var a = prism(2)(3)
を使用して部分的に適用された関数を作成することはできますが、 prism()(3)(5)
は使用できません。
return文の使用
return文は、関数の出力を作成するのに便利な方法です。 return文は、関数がまだ使用されるコンテキストがわからない場合に特に便利です。
//An example function that will take a string as input and return
//the first character of the string.
function firstChar (stringIn){
return stringIn.charAt(0);
}
この関数を使用するには、コード内の別の場所に変数を置く必要があります。
他の関数の引数として関数の結果を使用する:
console.log(firstChar("Hello world"));
コンソール出力は次のようになります。
> H
return文は、関数を終了します。
最初に関数を変更すると、returnステートメントが関数を終了することが示されます。
function firstChar (stringIn){
console.log("The first action of the first char function");
return stringIn.charAt(0);
console.log("The last action of the first char function");
}
このようにこの関数を実行すると、次のようになります。
console.log(firstChar("JS"));
コンソール出力:
> The first action of the first char function
> J
関数が終了したので、return文の後にメッセージは表示されません。
複数の行にまたがる文を返す:
JavaScriptでは、通常、1行のコードを複数の行に分けて読みやすくするか、または組織化することができます。これは有効なJavaScriptです:
var
name = "bob",
age = 18;
JavaScriptがvar
ような不完全なステートメントを見ると、次の行を見て自分自身を完成させます。しかし、 return
文で同じミスをした場合、期待した結果が得られません。
return
"Hi, my name is "+ name + ". " +
"I'm "+ age + " years old.";
このコードは戻りますundefined
ためreturn
、それ自体はJavascriptで完全なステートメントであるので、それは自分自身を完了するために、次の行には見えません。 return
文を複数の行に分割する必要がある場合は、分割する前に戻り値の隣に値を設定します。
return "Hi, my name is " + name + ". " +
"I'm " + age + " years old.";
参照または値による引数の受け渡し
JavaScriptでは、すべての引数が値渡しされます。関数が引数変数に新しい値を割り当てると、その変更は呼び出し元には表示されません。
var obj = {a: 2};
function myfunc(arg){
arg = {a: 5}; // Note the assignment is to the parameter variable itself
}
myfunc(obj);
console.log(obj.a); // 2
しかし、そのような引数の(ネストされた)プロパティに対する変更は、呼び出し元に表示されます。
var obj = {a: 2};
function myfunc(arg){
arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5
これは、 参照することにより、コールとして見ることができます:関数はそれに新しい値を割り当てることによって、呼び出し元のオブジェクトを変更することはできませんが、それは、呼び出し元のオブジェクトを変異させることができます。
数字や文字列のようなプリミティブな値の引数は不変であるため、関数がそれらを変更する方法はありません:
var s = 'say';
function myfunc(arg){
arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'
関数が引数として渡されたオブジェクトを変更したいが、実際に呼び出し元のオブジェクトを変更したくない場合、引数の変数を再割り当てする必要があります。
var obj = {a: 2, b: 3};
function myfunc(arg){
arg = Object.assign({}, arg); // assignment to argument variable, shallow copy
arg.a = 5;
}
myfunc(obj);
console.log(obj.a); // 2
引数のインプレース変異の代替として、関数は引数に基づいて新しい値を作成し、それを返すことができます。呼び出し側はそれを引数として渡された元の変数にも割り当てることができます。
var a = 2;
function myfunc(arg){
arg++;
return arg;
}
a = myfunc(a);
console.log(obj.a); // 3
電話と申し込み
関数は、プログラマが引数と供給できるようにする2つの組み込み方法を持っているthis
:異なった変数をcall
とapply
。
あるオブジェクト(そのプロパティであるオブジェクト)で動作する関数は、別の互換性のあるオブジェクトで動作するように再利用できるため、これは便利です。さらに、引数は、ES6のspread( ...
)演算子と同様に、配列としてワンショットで与えることができます。
let obj = {
a: 1,
b: 2,
set: function (a, b) {
this.a = a;
this.b = b;
}
};
obj.set(3, 7); // normal syntax
obj.set.call(obj, 3, 7); // equivalent to the above
obj.set.apply(obj, [3, 7]); // equivalent to the above; note that an array is used
console.log(obj); // prints { a: 3, b: 5 }
let myObj = {};
myObj.set(5, 4); // fails; myObj has no `set` property
obj.set.call(myObj, 5, 4); // success; `this` in set() is re-routed to myObj instead of obj
obj.set.apply(myObj, [5, 4]); // same as above; note the array
console.log(myObj); // prints { a: 3, b: 5 }
ECMAScript 5では、 call()
およびapply()
ほかにbind()
という別のメソッドが導入され、 this
関数の値を特定のオブジェクトに明示的に設定しthis
ます。
それは他の2つとは全く違った動作をします。 bind()
の最初の引数は、 this
新しい関数の値です。他のすべての引数は、新しい関数に永続的に設定する必要のある名前付きパラメータを表します。
function showName(label) {
console.log(label + ":" + this.name);
}
var student1 = {
name: "Ravi"
};
var student2 = {
name: "Vinod"
};
// create a function just for student1
var showNameStudent1 = showName.bind(student1);
showNameStudent1("student1"); // outputs "student1:Ravi"
// create a function just for student2
var showNameStudent2 = showName.bind(student2, "student2");
showNameStudent2(); // outputs "student2:Vinod"
// attaching a method to an object doesn't change `this` value of that method.
student2.sayName = showNameStudent1;
student2.sayName("student2"); // outputs "student2:Ravi"
デフォルトパラメータ
ECMAScript 2015(ES6)の前に、パラメータのデフォルト値を次のように割り当てることができました:
function printMsg(msg) {
msg = typeof msg !== 'undefined' ? // if a value was provided
msg : // then, use that value in the reassignemnt
'Default value for msg.'; // else, assign a default value
console.log(msg);
}
ES6では、上記の条件と再割り当てが不要になった新しい構文が提供されました。
function printMsg(msg='Default value for msg.') {
console.log(msg);
}
printMsg(); // -> "Default value for msg."
printMsg(undefined); // -> "Default value for msg."
printMsg('Now my msg in different!'); // -> "Now my msg in different!"
また、関数が呼び出されたときにパラメーターが見つからなかった場合、その値はundefined
として保持されます( 矢印関数を使用して明示的に指定することで確認できます)。
let param_check = (p = 'str') => console.log(p + ' is of type: ' + typeof p);
param_check(); // -> "str is of type: string"
param_check(undefined); // -> "str is of type: string"
param_check(1); // -> "1 is of type: number"
param_check(this); // -> "[object Window] is of type: object"
デフォルト値としての関数/変数およびパラメータの再利用
デフォルトのパラメータの値は、数値、文字列、または単純なオブジェクトに限定されません。関数をデフォルト値として設定することもできますcallback = function(){}
:
function foo(callback = function(){ console.log('default'); }) {
callback();
}
foo(function (){
console.log('custom');
});
// custom
foo();
//default
デフォルト値を使用して実行できる操作の特定の特性があります。
- 以前に宣言されたパラメータは、今後のパラメータの値のデフォルト値として再利用できます。
- インライン操作は、デフォルト値をパラメータに割り当てるときに許可されます。
- 宣言されている関数の同じスコープに存在する変数は、デフォルト値で使用できます。
- 戻り値をデフォルト値に提供するために、関数を呼び出すことができます。
let zero = 0;
function multiply(x) { return x * 2;}
function add(a = 1 + zero, b = a, c = b + a, d = multiply(c)) {
console.log((a + b + c), d);
}
add(1); // 4, 4
add(3); // 12, 12
add(2, 7); // 18, 18
add(1, 2, 5); // 8, 10
add(1, 2, 5, 10); // 8, 20
関数の戻り値を新しい呼び出しのデフォルト値に再利用する:
let array = [1]; // meaningless: this will be overshadowed in the function's scope
function add(value, array = []) {
array.push(value);
return array;
}
add(5); // [5]
add(6); // [6], not [5, 6]
add(6, add(5)); // [5, 6]
arguments
値と長さ
arguments
配列オブジェクトは、デフォルト値ではないパラメータ、つまり関数が呼び出されたときに明示的に提供されるパラメータのみを保持します。
function foo(a = 1, b = a + 1) {
console.info(arguments.length, arguments);
console.log(a,b);
}
foo(); // info: 0 >> [] | log: 1, 2
foo(4); // info: 1 >> [4] | log: 4, 5
foo(5, 6); // info: 2 >> [5, 6] | log: 5, 6
引数が不明な関数(可変関数)
未定数の引数を受け入れる関数を作成するには、環境に応じて2つの方法があります。
関数が呼び出されるたびに、関数に渡されたすべての引数を含むArrayのような引数オブジェクトがスコープ内にあります。これにインデックスを付けたり、これを繰り返したりすることで、引数へのアクセスが可能になります。
function logSomeThings() {
for (var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
}
}
logSomeThings('hello', 'world');
// logs "hello"
// logs "world"
必要に応じて、 arguments
を実際の配列に変換することができます。参照: 配列に似たオブジェクトを配列に変換する
ES6から、 残りの演算子 ( ...
)を使用して関数を最後のパラメータで宣言できます。これは、その点以降の引数を保持する配列を作成します
function personLogsSomeThings(person, ...msg) {
msg.forEach(arg => {
console.log(person, 'says', arg);
});
}
personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"
関数も同様の方法で呼び出すことができます。
const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]
logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]
この構文は、任意の数の引数を任意の位置に挿入するために使用でき、任意の繰り返し可能(iterable)で使用できます( apply
は配列のようなオブジェクトのみを受け入れます)。
const logArguments = (...args) => console.log(args)
function* generateNumbers() {
yield 6
yield 5
yield 4
}
logArguments('a', ...generateNumbers(), ...'pqr', 'b')
// output: Array [ "a", 6, 5, 4, "p", "q", "r", "b" ]
関数オブジェクトの名前を取得する
ES5 :
関数への参照がある場合は、次の操作を実行できます。
function functionName( func )
{
// Match:
// - ^ the beginning of the string
// - function the word 'function'
// - \s+ at least some white space
// - ([\w\$]+) capture one or more valid JavaScript identifier characters
// - \( followed by an opening brace
//
var result = /^function\s+([\w\$]+)\(/.exec( func.toString() )
return result ? result[1] : ''
}
部分的なアプリケーション
curryingと同様に、関数に渡される引数の数を減らすために、部分的なアプリケーションが使用されます。カレーリングとは異なり、数字は1つ下がる必要はありません。
例:
この機能...
function multiplyThenAdd(a, b, c) {
return a * b + c;
}
...は、常に2を掛けて、渡された値に10を加算する別の関数を作成するために使用できます。
function reversedMultiplyThenAdd(c, b, a) {
return a * b + c;
}
function factory(b, c) {
return reversedMultiplyThenAdd.bind(null, c, b);
}
var multiplyTwoThenAddTen = factory(2, 10);
multiplyTwoThenAddTen(10); // 30
部分適用の「適用」部分は、単に機能のパラメータを固定することを意味する。
機能構成
複数の関数を1つにまとめることは、関数型プログラミングの一般的なプラクティスです。
構成は、私たちのデータが通過するパイプラインを作って、単に機能コンポジション(トラックの断片を一緒にスナップするようなもの)で作業するだけです。
あなたはいくつかの責任関数から始めます:
const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
const sign = x => x + ',\nmade with love';
変換トラックを簡単に作成できます。
const formatText = compose(capitalize, sign);
formatText('this is an example')
//This is an example,
//made with love
NB Compositionは、この例のように通常はcompose
と呼ばれるユーティリティ関数によって実現されます。
compose
実装は、多くのJavaScriptユーティリティーライブラリ( lodash 、 rambdaなど)にありますが、次のような簡単な実装から始めることもできます。
const compose = (...funs) =>
x =>
funs.reduce((ac, f) => f(ac), x);