私は最近他の誰かのJavaScriptコードを保守し始めました。私はバグを修正し、機能を追加し、さらにコードを整理して一貫性を高めようとしています。
前の開発者は2つの方法で関数を宣言していますが、その理由があるかどうかわからないと解決できません。
2つの方法があります:
var functionOne = function() {
// Some code
};
function functionTwo() {
// Some code
}
これら2つの異なる方法を使用する理由は何ですか、またそれぞれの長所と短所は何ですか?ある方法ではできない、他の方法ではできないことがありますか。
違いは、functionOne
は関数式であり、その行に到達したときにのみ定義されるのに対して、functionTwo
は関数宣言であり、周囲の関数またはスクリプトが実行されるとすぐに定義されることです( hoisting のため)。
たとえば、関数式
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
そして、関数宣言:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
これはまた、関数宣言を使って関数を条件付きで定義できないことを意味します。
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
上記は実際にはfunctionThree
の値に関係なくtest
を定義します - use strict
が有効でない限り、それは単にエラーを発生させます。
最初にGregを修正します。function abc(){}
もスコープ指定されています - abc
という名前は、この定義があるスコープ内で定義されています。例:
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
次に、両方のスタイルを組み合わせることが可能です。
var xyz = function abc(){};
xyz
は通常通りに定義され、abc
はすべてのブラウザで未定義ですが、Internet Explorer - 定義されていることに頼らないでください。しかし、それはその本体の中で定義されます。
var xyz = function abc(){
// xyz is visible here
// abc is visible here
}
// xyz is visible here
// abc is undefined here
すべてのブラウザで関数のエイリアスを作りたい場合は、次のような宣言を使用してください。
function abc(){};
var xyz = abc;
この場合、xyz
とabc
は同じオブジェクトのエイリアスです。
console.log(xyz === abc); // prints "true"
複合スタイルを使用する説得力のある理由の1つは、機能オブジェクトの "name"属性です( Internet Explorerではサポートされていません )。基本的にあなたがのような関数を定義するとき
function abc(){};
console.log(abc.name); // prints "abc"
その名前は自動的に割り当てられます。しかし、あなたがそれを次のように定義すると
var abc = function(){};
console.log(abc.name); // prints ""
その名前は空です - 私たちは無名関数を作成し、それを何らかの変数に割り当てました。
複合スタイルを使用するもう1つの良い理由は、短い内部名を使用して自分自身を参照する一方で、外部ユーザーには競合しない長い名前を提供することです。
// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
// Let it call itself recursively:
shortcut(n - 1);
// ...
// Let it pass itself as a callback:
someFunction(shortcut);
// ...
}
上記の例では、外部の名前でも同じことができますが、扱いにくくなり過ぎます(そして遅くなります)。
(それ自体を参照するもう1つの方法はarguments.callee
を使用することです。これはまだ比較的長く、strictモードではサポートされていません。)
深く、JavaScriptは両方のステートメントを異なる方法で扱います。これは関数宣言です。
function abc(){}
ここでのabc
は、現在のスコープ内のあらゆる場所で定義されています。
// We can call it here
abc(); // Works
// Yet, it is defined down there.
function abc(){}
// We can call it again
abc(); // Works
また、それはreturn
ステートメントを通して掲げられました:
// We can call it here
abc(); // Works
return;
function abc(){}
これは関数式です。
var xyz = function(){};
ここでのxyz
は代入の点から定義されます。
// We can't call it here
xyz(); // UNDEFINED!!!
// Now it is defined
xyz = function(){}
// We can call it here
xyz(); // works
関数宣言対関数式が、Gregによって実証された違いがある本当の理由です。
楽しい事実:
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
個人的には、「関数式」宣言を好むのは、この方法で可視性を制御できるからです。関数を次のように定義すると
var abc = function(){};
関数をローカルに定義したことを知っています。関数を次のように定義すると
abc = function(){};
スコープチェーンのどこにもabc
を定義しないという条件で、私はそれをグローバルに定義したことを知っています。この定義スタイルはeval()
の中で使われても弾力的です。定義しながら
function abc(){};
特にeval()
の場合、文脈に依存し、それが実際に定義されている場所を推測したままにするかもしれません - 答えはブラウザに依存します。
関数を作成する標準フォームの概要は次のとおりです。(元は別の質問用に書かれていますが、正規の質問に移動した後に適合します。)
条項:
クイックリスト:
関数宣言
「匿名」function
式(この用語にもかかわらず、名前付きの関数を作成する場合があります)
名前付きfunction
式
アクセサー関数初期化子(ES5 +)
矢印関数式(ES2015 +)(匿名関数式と同様に、明示的な名前を含まず、名前を持つ関数を作成できます)
オブジェクト初期化子のメソッド宣言(ES2015 +)
class
のコンストラクターとメソッドの宣言(ES2015 +)
最初の形式は関数宣言で、次のようになります。
function x() {
console.log('x');
}
関数宣言はdeclaration;です。ステートメントや式ではありません。そのため、;
を付けないでください(そうすることは無害です)。
関数宣言は、実行が出現するコンテキストに入ると処理されます。beforeステップバイステップコードが実行されます。作成する関数には適切な名前(上記の例ではx
)が与えられ、その名前は宣言が現れるスコープに入れられます。
同じコンテキストでステップバイステップのコードの前に処理されるため、次のようなことができます。
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
ES2015まで、仕様では、try
、if
、switch
、while
などのような制御構造内に関数宣言を配置した場合、JavaScriptエンジンが何を行うべきかについては説明していませんでした。
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
そして、それらは処理されるためbeforeステップバイステップのコードが実行されるため、制御構造内にいるときに何をすべきかを知ることはトリッキーです。
これを行うことは指定 ES2015までではありませんでしたが、ブロックでの関数宣言をサポートするための許可される拡張でした。残念ながら(そして必然的に)、異なるエンジンは異なることをしました。
ES2015の時点で、仕様は何をすべきかを示しています。実際、次の3つのことを行う必要があります。
ルーズモードのルールは扱いにくいですが、strictモードでは、ブロック内の関数宣言は簡単です。ブロックに対してローカルです(ブロックスコープがあります。 ES2015の新機能)、ブロックの最上部に引き上げられます。そう:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
function
式2番目の一般的な形式は、匿名関数式と呼ばれます。
var y = function () {
console.log('y');
};
すべての式と同様に、コードの段階的な実行で到達すると評価されます。
ES5では、これが作成する関数には名前がありません(匿名です)。 ES2015では、コンテキストから推測することにより、可能であれば関数に名前が割り当てられます。上記の例では、名前はy
になります。関数がプロパティ初期化子の値である場合、同様のことが行われます。 (これがいつ起こるかとルールの詳細については、 仕様 —でSetFunctionName
を検索してください-all over the place。)
function
式3番目の形式は名前付き関数式( "NFE")です:
var z = function w() {
console.log('zw')
};
これが作成する関数には適切な名前(この場合はw
)があります。すべての式と同様に、これはコードの段階的な実行で到達すると評価されます。関数の名前はnotであり、式が表示されるスコープに追加されます。関数自体のスコープ内の名前is:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
NFEはJavaScript実装のバグの原因であることが多いことに注意してください。たとえば、IE8以前では、NFEを処理します 完全に間違っています 、2つの異なる時間に2つの異なる関数を作成します。 Safariの初期バージョンにも問題がありました。幸いなことに、現在のバージョンのブラウザ(IE9以降、現在のSafari)にはこれらの問題はありません。 (しかし、この記事の執筆時点では、残念ながらIE8が広く使用されているため、一般にWebのコードでNFEを使用することには依然として問題があります。)
機能がほとんど気付かれずに潜入することがあります。 accessor functionsの場合です。以下に例を示します。
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
関数を使用したとき、()
を使用しなかったことに注意してください。これは、プロパティのaccessor functionであるためです。通常の方法でプロパティを取得および設定しますが、背後で関数が呼び出されます。
Object.defineProperty
、Object.defineProperties
、およびObject.create
へのあまり知られていない2番目の引数を使用して、アクセサー関数を作成することもできます。
ES2015は矢印関数をもたらします。次に例を示します。
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
n => n * 2
がmap()
呼び出しに隠れていることを確認しますか?それは機能です。
矢印関数に関するいくつかのこと:
独自のthis
はありません。代わりに、close over定義されているコンテキストのthis
。 (arguments
、および関連する場合はsuper
も閉じます。)これは、その中のthis
が作成されたthis
と同じであり、変更できないことを意味します。
上記でお気づきのとおり、キーワードfunction
は使用しません。代わりに、=>
を使用します。
上記のn => n * 2
の例は、それらの1つの形式です。関数を渡す複数の引数がある場合、括弧を使用します。
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Array#map
は、エントリを最初の引数として渡し、インデックスを2番目の引数として渡すことに注意してください。)
どちらの場合も、関数の本体は単なる式です。関数の戻り値は自動的にその式の結果になります(明示的なreturn
は使用しません)。
単一の式以上のことをしている場合は、通常どおり、{}
と明示的なreturn
(値を返す必要がある場合)を使用します。
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
{ ... }
のないバージョンは、expression bodyまたはconcise bodyの矢印関数と呼ばれます。 (また:A 簡潔矢印関数。){ ... }
が本体を定義するものは、関数本体を持つ矢印関数です。 (また:A verbose arrow関数。)
ES2015では、method definition;と呼ばれる関数を参照するプロパティを宣言する短い形式が許可されています。次のようになります。
var o = {
foo() {
}
};
eS5以前でほぼ同等のものは次のようになります。
var o = {
foo: function foo() {
}
};
違い(冗長性以外)は、メソッドはsuper
を使用できますが、関数は使用できないことです。したがって、たとえば、メソッド構文を使用して(たとえば)valueOf
を定義したオブジェクトがある場合、Object.prototype.valueOf
が返す値を取得するためにsuper.valueOf()
を使用できます(おそらく他の何かを行う前に) ES5バージョンでは、代わりにObject.prototype.valueOf.call(this)
を実行する必要があります。
また、メソッドには定義されたオブジェクトへの参照があるため、そのオブジェクトが一時的な場合(たとえば、ソースオブジェクトの1つとしてObject.assign
に渡す場合)、メソッド構文- couldは、オブジェクトがガベージコレクションされる可能性がある場合にメモリに保持されることを意味します(JavaScriptエンジンがその状況を検出せず、メソッドがsuper
を使用しない場合は処理します)。
class
のコンストラクターとメソッドの宣言(ES2015 +)ES2015は、宣言されたコンストラクターとメソッドを含むclass
構文を提供します。
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
上記の2つの関数宣言があります。1つは名前Person
を取得するコンストラクター用、もう1つはPerson.prototype
に割り当てられた関数であるgetFullName
用です。
グローバルコンテキストについて言えば、最後のvar
ステートメントとFunctionDeclaration
は両方ともグローバルオブジェクトに削除不可プロパティを作成しますが、両方の値は{上書き可能} _です。
2つの方法の微妙な違いは、 Variable Instantiation プロセスが実行されると(実際のコード実行の前に)var
で宣言されたすべての識別子はundefined
で初期化され、FunctionDeclaration
で使用される識別子はその時点から使用できる、 例えば:
alert(typeof foo); // 'function', it's already available
alert(typeof bar); // 'undefined'
function foo () {}
var bar = function () {};
alert(typeof bar); // 'function'
bar
FunctionExpression
の割り当ては実行時まで行われます。
FunctionDeclaration
によって作成されたグローバルプロパティは、変数値のように問題なく上書きできます。
function test () {}
test = null;
2つの例のもう1つの明らかな違いは、最初の関数には名前がありませんが、2番目の関数には名前があることです。これはデバッグ時(つまり呼び出しスタックの検査時)に非常に便利です。
最初の編集例(foo = function() { alert('hello!'); };
)については、宣言されていない割り当てです。常にvar
キーワードを使用することを強くお勧めします。
代入では、var
ステートメントを使用せずに、参照された識別子がスコープチェーンに見つからない場合、それはグローバルオブジェクトの削除可能プロパティになります。
また、宣言されていない代入はECMAScript 5の Strict Mode の下でReferenceError
をスローします。
必読:
注 :この答えは 他の質問 からマージされています。この質問では、OPからの大きな疑問と誤解はFunctionDeclaration
で宣言された識別子を上書きできないということでした。
ここに投稿した2つのコードスニペットは、ほぼすべての目的で同じように動作します。
ただし、動作の違いは、最初のバリアント(var functionOne = function() {}
)では、その関数はコード内のその時点以降にしか呼び出せないことです。
2番目のバリエーション(function functionTwo()
)では、関数は、宣言された場所より上で実行されるコードで使用できます。
これは、最初のバリアントでは、実行時に関数が変数foo
に割り当てられるためです。 2番目の関数では、解析時に関数がその識別子foo
に割り当てられます。
もっと技術的な情報
JavaScriptには関数を定義する3つの方法があります。
eval()
と同じように機能するので、これを使用することはお勧めできません。functionTwo();
function functionTwo() {
}
なぜエラーがないのか式は上から下に実行されることを常に教えられてきました(??)
関数宣言と変数宣言は、JavaScriptインタプリタによって常に見えないようにそれらの包含スコープの先頭に移動(
hoisted
)します。関数パラメータと言語定義の名前は、明らかにすでに存在しています。 ベンチェリー
これは次のようなコードを意味します。
functionOne(); --------------- var functionOne;
| is actually | functionOne();
var functionOne = function(){ | interpreted |-->
}; | like | functionOne = function(){
--------------- };
宣言の代入部分が引き上げられていないことに注意してください。名前だけが掲げられています。
ただし、関数宣言の場合は、関数本体全体も同様に引き上げられます:
functionTwo(); --------------- function functionTwo() {
| is actually | };
function functionTwo() { | interpreted |-->
} | like | functionTwo();
---------------
他のコメンターはすでに上記の2つの異形の意味の違いをカバーしています。私はスタイルの違いに注目したいと思いました: "割り当て"バリエーションだけが他のオブジェクトのプロパティを設定することができます。
私はよく次のようなパターンでJavaScriptモジュールを構築します。
(function(){
var exports = {};
function privateUtil() {
...
}
exports.publicUtil = function() {
...
};
return exports;
})();
このパターンでは、パブリック関数はすべて代入を使用し、プライベート関数は宣言を使用します。
(また、代入では文の後にセミコロンが必要ですが、宣言では禁止されています)。
最初の方法を2番目の方法よりも優先する場合の例は、関数の以前の定義をオーバーライドしないようにする必要がある場合です。
あり
if (condition){
function myfunction(){
// Some code
}
}
myfunction
のこの定義は、解析時に行われるため、以前の定義をオーバーライドします。
しながら
if (condition){
var myfunction = function (){
// Some code
}
}
myfunction
が定義されている場合にのみcondition
を定義する正しい仕事をします。
重要な理由は、あなたの名前空間の "Root"としてただ一つの変数を追加することです...
var MyNamespace = {}
MyNamespace.foo= function() {
}
または
var MyNamespace = {
foo: function() {
},
...
}
名前空間の付け方はたくさんあります。たくさんのJavaScriptモジュールが利用可能になったことで、より重要になります。
Hoistingは、すべての変数と関数の宣言を現在のスコープの先頭に移動するというJavaScriptインタプリタの動作です。
ただし、実際の宣言のみが掲げられています。割り当てをそのままにします。
Javascriptは緩やかに型付けされた言語と呼ばれています。つまり、JavaScript変数は任意の Data-Typeの値を保持できます。 Javascriptは実行時に提供された値/リテラルに基づいて変数タイプを自動的に変更します。
global_Page = 10; var global_Page; « undefined
« Integer literal, Number Type. ------------------- global_Page = 10; « Number
global_Page = 'Yash'; | Interpreted | global_Page = 'Yash'; « String
« String literal, String Type. « AS « global_Page = true; « Boolean
var global_Page = true; | | global_Page = function (){ « function
« Boolean Type ------------------- var local_functionblock; « undefined
global_Page = function (){ local_functionblock = 777;« Number
var local_functionblock = 777; };
// Assigning function as a data.
};
関数
function Identifier_opt ( FormalParameterList_opt ) {
FunctionBody | sequence of statements
« return; Default undefined
« return 'some data';
}
関数のデフォルトの戻り値は ' undefined '、 Variable 宣言のデフォルト値も 'undefined'です。
Scope with respect to function-block global.
Scope with respect to page undefined | not available.
関数宣言
function globalAccess() { function globalAccess() {
} ------------------- }
globalAccess(); | | function globalAccess() { « Re-Defined / overridden.
localAccess(); « Hoisted As « function localAccess() {
function globalAccess() { | | }
localAccess(); ------------------- localAccess(); « function accessed with in globalAccess() only.
function localAccess() { }
} globalAccess();
} localAccess(); « ReferenceError as the function is not defined
関数式
10; « literal
(10); « Expression (10).toString() -> '10'
var a;
a = 10; « Expression var a.toString() -> '10'
(function invoke() { « Expression Function
console.log('Self Invoking'); (function () {
}); }) () -> 'Self Invoking'
var f;
f = function (){ « Expression var Function
console.log('var Function'); f () -> 'var Function'
};
変数に割り当てられた関数例:
(function selfExecuting(){
console.log('IIFE - Immediately-Invoked Function Expression');
}());
var anonymous = function (){
console.log('anonymous function Expression');
};
var namedExpression = function for_InternalUSE(fact){
if(fact === 1){
return 1;
}
var localExpression = function(){
console.log('Local to the parent Function Scope');
};
globalExpression = function(){
console.log('creates a new global variable, then assigned this function.');
};
//return; //undefined.
return fact * for_InternalUSE( fact - 1);
};
namedExpression();
globalExpression();
javaScriptは次のように解釈されます
var anonymous;
var namedExpression;
var globalExpression;
anonymous = function (){
console.log('anonymous function Expression');
};
namedExpression = function for_InternalUSE(fact){
var localExpression;
if(fact === 1){
return 1;
}
localExpression = function(){
console.log('Local to the parent Function Scope');
};
globalExpression = function(){
console.log('creates a new global variable, then assigned this function.');
};
return fact * for_InternalUSE( fact - 1); // DEFAULT UNDEFINED.
};
namedExpression(10);
globalExpression();
あなたは jsperf Test Runner
を使って異なるブラウザの上で関数宣言、式テストをチェックすることができます
ES5コンストラクタ関数クラス :Function.prototype.bindで作成した関数オブジェクト
JavaScriptは関数をファーストクラスのオブジェクトとして扱うので、オブジェクトであるため、関数にプロパティを割り当てることができます。
function Shape(id) { // Function Declaration
this.id = id;
};
// Adding a prototyped method to a function.
Shape.prototype.getID = function () {
return this.id;
};
Shape.prototype.setID = function ( id ) {
this.id = id;
};
var expFn = Shape; // Function Expression
var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10
ES6が導入されました Arrow function :arrow function式は、より短い構文を持ち、メソッド以外の関数に最も適しており、コンストラクタとしては使用できません。
ArrowFunction : ArrowParameters => ConciseBody
。const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; }; console.log( fn(2) ); // Even console.log( fn(3) ); // Odd
他のみんなが巻き上げ部分を徹底的にカバーしたからといって、私は私自身の答えを加えています。
私は今のところどの方法がずっと良いのか疑問に思いました、そしておかげで http://jsperf.com 今私は知っています:)
関数宣言 は速いです、そしてそれはWeb開発者にとって本当に重要なことなのですか? ;)
バインディングが確立されると、関数宣言と変数に割り当てられた関数式は同じように動作します。
ただし、howとwhenには違いがあります。関数オブジェクトは実際にはその変数に関連付けられています。この違いは、JavaScriptのvariable hoistingというメカニズムによるものです。
基本的に、すべての関数宣言と変数宣言は、宣言が行われるfunctionの先頭に配置されています(これが、JavaScriptがfunction scopeを持つ理由です)。
関数宣言が持ち上げられると、関数本体はに「従います」ので、関数本体が評価されると、変数はすぐに関数オブジェクトにバインドされます。
変数宣言が持ち上げられると、初期化はnot に従いますが、「取り残された」ままです。変数は、関数本体の先頭で undefined
に初期化され、assigned のコード内の元の位置の値になります。 (実際には、同じ名前の変数の宣言が行われるeveryの位置に値が割り当てられます。)
巻き上げの順序も重要です。関数宣言は同じ名前の変数宣言よりも優先され、最後の関数宣言は同じ名前の前の関数宣言よりも優先されます。
いくつかの例...
var foo = 1;
function bar() {
if (!foo) {
var foo = 10 }
return foo; }
bar() // 10
変数foo
は関数の先頭に持ち上げられ、undefined
に初期化されるので、!foo
はtrue
になり、foo
には10
が割り当てられます。 foo
のスコープ外のbar
は何の役割も果たさずそのままです。
function f() {
return a;
function a() {return 1};
var a = 4;
function a() {return 2}}
f()() // 2
function f() {
return a;
var a = 4;
function a() {return 1};
function a() {return 2}}
f()() // 2
関数宣言は変数宣言よりも優先され、最後の関数宣言は「固執」します。
function f() {
var a = 4;
function a() {return 1};
function a() {return 2};
return a; }
f() // 4
この例では、a
は2番目の関数宣言を評価した結果の関数オブジェクトで初期化され、その後4
が割り当てられています。
var a = 1;
function b() {
a = 10;
return;
function a() {}}
b();
a // 1
ここでは関数宣言が最初に持ち上げられ、変数a
を宣言して初期化します。次に、この変数に10
を割り当てます。言い換えると、代入は外部変数a
に代入しません。
最初の例は関数宣言です。
function abc(){}
2番目の例は関数式です。
var abc = function() {};
主な違いは、それらがどのように吊り上げられるか(持ち上げられ宣言される)です。最初の例では、関数宣言全体がまとめられています。 2番目の例では、var 'abc'だけが持ち上げられ、その値(関数)は未定義になり、関数自体は宣言された位置に残ります。
簡単に言えば:
//this will work
abc(param);
function abc(){}
//this would fail
abc(param);
var abc = function() {}
このトピックについてもっと学ぶために、私はあなたにこれを強く勧めます link
コード保守コストの観点からは、名前付き関数がより好ましいです。
名前付き関数のためのもっと多くのPROSが続くのではないかと思います。そして、名前付き関数の利点として挙げられていることは、無名関数にとっては不利です。
歴史的に、無名関数は、名前付き関数を持つメンバーをリストするための言語としてのJavaScriptの無力から現れました。
{
member:function() { /* How do I make "this.member" a named function? */
}
}
私は自分のコードで可変的なアプローチを非常に具体的な理由で使用していますが、その理論は上で抽象的な方法でカバーされていますが、JavaScriptの専門知識が限られています。
私は160個の独立したデザインのブランディングで実行する必要があるコードを持っています。コードの大部分は共有ファイルにありますが、ブランド固有のものは、ブランドごとに1つの別々のファイルにあります。
特定の機能を必要とするブランドもあれば、そうでないブランドもあります。新しいブランド特有のことをするために新しい機能を追加しなければならないことがあります。共有コードを変更できてうれしいですが、160セットのブランドファイルすべてを変更する必要はありません。
変数構文を使用することで、共有コード内で変数(本質的には関数ポインタ)を宣言し、簡単なスタブ関数を割り当てるか、またはnullに設定することができます。
関数の特定の実装を必要とする1つまたは2つのブランドは、その関数のバージョンを定義し、必要に応じてこれを変数に割り当てることができ、残りは何もしません。共有コードで実行する前に、null関数をテストできます。
上記の人々のコメントから、私は静的関数も再定義することが可能かもしれないと私は集めるが、私は変数の解決策が素晴らしく明確であると思う。
コンピュータサイエンスの用語では、無名関数と名前付き関数について説明します。最も重要な違いは、無名関数は名前にバインドされていないということです。つまり、無名関数という名前です。 JavaScriptでは、実行時に動的に宣言されるファーストクラスオブジェクトです。
匿名関数とラムダ計算の詳細については、Wikipediaをお勧めします( http://en.wikipedia.org/wiki/Anonymous_function )。
Greg's Answer は十分ですが、 Douglas Crockfordの を見ていたので、まだ追加したいと思います。
関数式:
var foo = function foo() {};
機能ステートメント:
function foo() {};
Functionステートメントは、var
値を持つfunction
ステートメントの単なる略記です。
そう
function foo() {};
に展開
var foo = function foo() {};
これはさらに拡張されます。
var foo = undefined;
foo = function foo() {};
そして、どちらもコードの先頭に掲げられています。
@EugeneLazutkin は自分自身への内部参照として shortcut()
を使うことができるように割り当てられた関数を指定します。 John Resig は別の例を挙げます - 彼の Learning Advanced Javascript チュートリアルで他のオブジェクトに割り当てられた再帰関数をコピーする。ここでは関数にプロパティを割り当てることは厳密には問題ではありませんが、私は積極的にチュートリアルを試してみることをお勧めします - 右上隅のボタンをクリックしてコードを実行し、好みに合わせてコードをダブルクリックします。
チュートリアルの例:yell()
内の再帰呼び出し:
元の忍者オブジェクトが削除されるとテストは失敗します。 (13ページ)
var ninja = {
yell: function(n){
return n > 0 ? ninja.yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." );
var samurai = { yell: ninja.yell };
var ninja = null;
try {
samurai.yell(4);
} catch(e){
assert( false, "Uh, this isn't good! Where'd ninja.yell go?" );
}
再帰的に呼び出される関数に名前を付けると、テストに合格します。 (14ページ)
var ninja = {
yell: function yell(n){
return n > 0 ? yell(n-1) + "a" : "hiy";
}
};
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" );
var samurai = { yell: ninja.yell };
var ninja = {};
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
他の答えで言及されていないもう一つの違いはあなたが無名関数を使用する場合です。
var functionOne = function() {
// Some code
};
そしてそれをコンストラクタとして使用する
var one = new functionOne();
one.constructor.name
は定義されません。 Function.name
は非標準ですが、Firefox、Chrome、他のWebkit由来のブラウザ、およびIE 9+でサポートされています。
あり
function functionTwo() {
// Some code
}
two = new functionTwo();
two.constructor.name
を使用してコンストラクタの名前を文字列として取得することは可能です。
最初のもの(関数doSomething(x))はオブジェクト表記法の一部であるべきです。
2番目のもの(var doSomething = function(x){ alert(x);}
)は、単純に無名関数を作成してそれを変数doSomething
に割り当てることです。そのため、doSomething()は関数を呼び出します。
関数宣言 および 関数式 が何であるかを知りたいと思うかもしれません。
関数宣言は、変数の割り当てを必要とせずに名前付き関数変数を定義します。関数宣言は独立した構成要素として行われ、非関数ブロック内にネストすることはできません。
function foo() {
return 3;
}
ECMA 5(13.0)では、構文は次のように定義されています。
関数識別子(FormalParameterListopt ){FunctionBody}
上記の条件では、関数名はその有効範囲とその親の有効範囲内に表示されます(そうでなければ到達できません)。
そして関数式の中で
関数式は、より大きな式の構文の一部として関数を定義します(通常は変数代入)。関数式によって定義された関数は、名前付きまたは匿名にすることができます。関数式は「function」で始まってはいけません。
// Anonymous function expression
var a = function() {
return 3;
}
// Named function expression
var a = function foo() {
return 3;
}
// Self-invoking function expression
(function foo() {
alert("hello!");
})();
ECMA 5(13.0)では、構文は次のように定義されています。
機能識別子opt (FormalParameterListopt ){FunctionBody}
これらの関数を使ってオブジェクトを作成すると、次のようになります。
var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function
var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
私は以下の違いを挙げています:
関数宣言はコード内の任意の場所に配置できます。定義がコードに表示される前に呼び出された場合でも、ページ内の他のコードが実行を開始する前に、関数宣言がメモリにコミットされるとき、または起動されるときに実行されます。
以下の機能を見てください。
function outerFunction() {
function foo() {
return 1;
}
return foo();
function foo() {
return 2;
}
}
alert(outerFunction()); // Displays 2
これは、実行中、次のように見えるためです。 -
function foo() { // The first function declaration is moved to top
return 1;
}
function foo() { // The second function declaration is moved to top
return 2;
}
function outerFunction() {
return foo();
}
alert(outerFunction()); //So executing from top to bottom,
//the last foo() returns 2 which gets displayed
関数式を呼び出す前に定義されていないと、エラーになります。また、ここでは関数定義自体が先頭に移動されたり、関数宣言のようにメモリにコミットされたりすることはありません。しかし、関数に割り当てた変数は起動され、 undefined に割り当てられます。
関数式を使った同じ関数
function outerFunction() {
var foo = function() {
return 1;
}
return foo();
var foo = function() {
return 2;
}
}
alert(outerFunction()); // Displays 1
これは、実行中は次のようになるためです。
function outerFunction() {
var foo = undefined;
var foo = undefined;
foo = function() {
return 1;
};
return foo ();
foo = function() { // This function expression is not reachable
return 2;
};
}
alert(outerFunction()); // Displays 1
アクセスできないため、 if のように非ファンクションブロックにファンクション宣言を記述するのは安全ではありません。
if (test) {
function x() { doSomething(); }
}
以下のような名前付き関数式は、バージョン9より前のInternet Explorerブラウザでは機能しない可能性があります。
var today = function today() {return new Date()}
「名前付き関数はスタックトレースに現れる」という議論に照らして、現代のJavaScriptエンジンは実際には無名関数を表現する能力がかなりあります。
これを書いている時点では、V8、SpiderMonkey、Chakra、Nitroは常に名前付き関数をそれらの名前で参照しています。ほとんどの場合、無名関数を持っていれば、その名前で無名関数を参照します。
SpiderMonkeyは他の関数から返された無名関数の名前を知ることができます。残りはできません。
あなたが本当に、本当にあなたのイテレータと成功のコールバックがトレースに現れることを望んでいたならば、あなたもそれらに名前を付けることができます...
[].forEach(function iterator() {});
しかし、ほとんどの場合、それは強調する価値がありません。
'use strict';
var a = function () {
throw new Error();
},
b = function b() {
throw new Error();
},
c = function d() {
throw new Error();
},
e = {
f: a,
g: b,
h: c,
i: function () {
throw new Error();
},
j: function j() {
throw new Error();
},
k: function l() {
throw new Error();
}
},
m = (function () {
return function () {
throw new Error();
};
}()),
n = (function () {
return function n() {
throw new Error();
};
}()),
o = (function () {
return function p() {
throw new Error();
};
}());
console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {
try {
func();
} catch (error) {
return logs.concat('func.name: ' + func.name + '\n' +
'Trace:\n' +
error.stack);
// Need to manually log the error object in Nitro.
}
}, []).join('\n\n'));
func.name:
Trace:
Error
at a (http://localhost:8000/test.js:4:11)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: b
Trace:
Error
at b (http://localhost:8000/test.js:7:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: d
Trace:
Error
at d (http://localhost:8000/test.js:10:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name:
Trace:
Error
at a (http://localhost:8000/test.js:4:11)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: b
Trace:
Error
at b (http://localhost:8000/test.js:7:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: d
Trace:
Error
at d (http://localhost:8000/test.js:10:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name:
Trace:
Error
at e.i (http://localhost:8000/test.js:17:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: j
Trace:
Error
at j (http://localhost:8000/test.js:20:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: l
Trace:
Error
at l (http://localhost:8000/test.js:23:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name:
Trace:
Error
at http://localhost:8000/test.js:28:19
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: n
Trace:
Error
at n (http://localhost:8000/test.js:33:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: p
Trace:
Error
at p (http://localhost:8000/test.js:38:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27 test.js:42
func.name:
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name:
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name:
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name:
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: undefined
Trace:
Error
at a (http://localhost:8000/test.js:4:5)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at b (http://localhost:8000/test.js:7:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at d (http://localhost:8000/test.js:10:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at a (http://localhost:8000/test.js:4:5)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at b (http://localhost:8000/test.js:7:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at d (http://localhost:8000/test.js:10:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at e.i (http://localhost:8000/test.js:17:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at j (http://localhost:8000/test.js:20:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at l (http://localhost:8000/test.js:23:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at Anonymous function (http://localhost:8000/test.js:28:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at n (http://localhost:8000/test.js:33:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at p (http://localhost:8000/test.js:38:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name:
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name:
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name:
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name:
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
以下にリストされているように、2つの異なる関数宣言の間には4つの注目すべき比較があります。
function add()
は最も近いブロックにスコープされているので、次のように動作します。
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
function add(a, b){
return a + b;
}
以下は機能しません(var add=
がfunction add()
を置き換えたため)。
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
var add=function add(a, b){
return a + b;
}
add
は使用後に宣言されるため、以下は機能しません。
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
var add=function(a, b){
return a + b;
}
このように宣言された場合、関数の名前function thefuncname(){}
は thefuncname です。
function foobar(a, b){}
console.log(foobar.name);
var a = function foobar(){};
console.log(a.name);
そうではなく、関数がfunction(){}
として宣言されている場合、 function 。nameが関数を格納するために使用される最初の変数です。
var a = function(){};
var b = (function(){ return function(){} });
console.log(a.name);
console.log(b.name);
関数に変数が設定されていない場合、関数名は空の文字列(""
)です。
console.log((function(){}).name === "");
最後に、関数が割り当てられた変数が最初に名前を設定しますが、関数に設定された連続した変数は名前を変更しません。
var a = function(){};
var b = a;
var c = b;
console.log(a.name);
console.log(b.name);
console.log(c.name);
GoogleのV8とFirefoxのSpidermonkeyでは、数マイクロ秒のJISTコンパイルの違いがあるかもしれませんが、最終的には結果はまったく同じです。これを証明するために、2つの空白コードスニペットの速度を比較することによって、マイクロベンチマークでのJSPerfの効率を調べてみましょう。 JSPerfテストはこちら にあります。そして、 jsben.chテストがここにあります 。ご覧のとおり、存在しないはずの場合は顕著な違いがあります。もしあなたが私のように本当にパフォーマンスが悪いのであれば、スコープ内の変数や関数の数を減らし、特に多相(2つの異なる型を格納するために同じ変数を使うなど)を排除しようとするとより価値があります。
変数を宣言するためにvar
キーワードを使用すると、そのように変数に別の値を再割り当てすることができます。
(function(){
"use strict";
var foobar = function(){}; // initial value
try {
foobar = "Hello World!"; // new value
console.log("[no error]");
} catch(error) {
console.log("ERROR: " + error.message);
}
console.log(foobar, window.foobar);
})();
しかし、constステートメントを使用すると、変数参照は不変になります。つまり、変数に新しい値を代入することはできません。ただし、これによって変数の内容が不変になるわけではありません。const arr = []
を実行しても、arr[10] = "example"
を実行できます。 arr = "new value"
やarr = []
のようなことをするだけで、以下のようにエラーが発生します。
(function(){
"use strict";
const foobar = function(){}; // initial value
try {
foobar = "Hello World!"; // new value
console.log("[no error]");
} catch(error) {
console.log("ERROR: " + error.message);
}
console.log(foobar, window.foobar);
})();
興味深いことに、変数をfunction funcName(){}
として宣言した場合、変数の不変性はvar
で宣言した場合と同じになります。
(function(){
"use strict";
function foobar(){}; // initial value
try {
foobar = "Hello World!"; // new value
console.log("[no error]");
} catch(error) {
console.log("ERROR: " + error.message);
}
console.log(foobar, window.foobar);
})();
「最近接ブロック」は、最も近い「関数」です(非同期関数、ジェネレータ関数、および非同期ジェネレータ関数を含みます)。しかし、興味深いことに、function functionName() {}
は、クロージャの外側の項目に対する非クロージャブロックの中では、var functionName = function() {}
のように振る舞います。観察する。
var add=function(){}
try {
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined") {
add(1, 1); // just to prove it
console.log("Not a block");
}else if(add===undefined){ // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b){return a+b}');
}
} catch(e) {
console.log("Is a block");
}
var add=function(a, b){return a + b}
function add(){}
try {
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined") {
add(1, 1); // just to prove it
console.log("Not a block");
}else if(add===undefined){ // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b){return a+b}')
}
} catch(e) {
console.log("Is a block");
}
function add(a, b){
return a + b;
}
try {
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined") {
add(1, 1); // just to prove it
console.log("Not a block");
}else if(add===undefined){ // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b){return a+b}')
}
} catch(e) {
console.log("Is a block");
}
(function () {
function add(a, b){
return a + b;
}
})();
if
、else
、for
、while
、try
/catch
、finally
、switch
/do
、while
、with
try {
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined") {
add(1, 1); // just to prove it
console.log("Not a block");
}else if(add===undefined){ // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b){return a+b}')
}
} catch(e) {
console.log("Is a block");
}
{
function add(a, b){
return a + b;
}
}
var add=function()
の付いた矢印関数try {
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined") {
add(1, 1); // just to prove it
console.log("Not a block");
}else if(add===undefined){ // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b){return a+b}')
}
} catch(e) {
console.log("Is a block");
}
(() => {
var add=function(a, b){
return a + b;
}
})();
function add()
の付いた矢印関数try {
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined") {
add(1, 1); // just to prove it
console.log("Not a block");
}else if(add===undefined){ // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b){return a+b}')
}
} catch(e) {
console.log("Is a block");
}
(() => {
function add(a, b){
return a + b;
}
})();
JavaScriptでは、関数を作成する方法は2つあります。
関数宣言:
function fn(){
console.log("Hello");
}
fn();
これは非常に基本的でわかりやすいもので、多くの言語で使用されており、C言語ファミリでは標準的なものです。関数を定義し、それを呼び出すことによって実行しました。
知っておくべきことは、関数は実際にはJavaScriptのオブジェクトであるということです。内部的には上記の関数のオブジェクトを作成し、それにfnという名前を付けるか、オブジェクトへの参照をfnに格納します。関数はJavaScriptのオブジェクトです。関数のインスタンスは実際にはオブジェクトインスタンスです。
関数式:
var fn=function(){
console.log("Hello");
}
fn();
JavaScriptにはファーストクラスの関数があります。つまり、文字列または数値を作成してそれを変数に割り当てるのと同じように、関数を作成してそれを変数に割り当てます。ここで、fn変数は関数に割り当てられています。この概念の理由は、関数はJavaScriptのオブジェクトだからです。 fnは上記の関数のオブジェクトインスタンスを指しています。関数を初期化して変数に代入しました。関数を実行して結果を代入するのではありません。
参照:JavaScript関数宣言の構文:var fn = function(){} vs function fn(){}
どちらも関数の定義方法が異なります。違いは、ブラウザがそれらを解釈して実行コンテキストに読み込む方法です。
最初のケースは、インタプリタがそのコード行に到達したときにのみロードされる関数式です。したがって、次のようにすると、 functionOneは関数 ではないというエラーが発生します。
functionOne();
var functionOne = function() {
// Some code
};
その理由は、最初の行でfunctionOneに値が割り当てられていないため、未定義のためです。これを関数として呼び出そうとしているため、エラーが発生しています。
2行目では、無名関数の参照をfunctionOneに代入しています。
2番目のケースは、コードが実行される前にロードされる関数宣言です。したがって、次のようにしても、コードの実行前に宣言が読み込まれるため、エラーにはなりません。
functionOne();
function functionOne() {
// Some code
}
パフォーマンスについて
新しいバージョンのV8
では、いくつかの内部最適化が導入され、SpiderMonkey
も導入されました。
式と宣言の間にはほとんど違いはありません。
関数式 の方が速いようです now。
Anonymous
関数式 はNamed
関数式に対してより良いパフォーマンス を持つようです.
最初のものは無名関数に割り当てられた変数(関数宣言)で、2番目はJavaScriptで関数を作成する通常の方法(無名関数宣言)です。 :
1.関数式
var functionOne = function() {
// Some code
};
関数式は、より大きな式の構文の一部として関数を定義します(通常は変数代入)。関数式で定義された関数は、名前付きまたは匿名のどちらでもかまいません。関数式は、 "function"で始めることはできません(したがって、以下の自己呼び出しの例を囲む括弧)。
JavaScriptの関数はホイストすることができますが、変数は関数にアクセスする前に宣言する必要がありますが、変数にアクセスする前に呼び出すことができます。宣言されている場所の前でその関数にアクセスする、それはあなたがあなたの関数を書く方法であるかもしれません。他の関数を返す関数に対しては、この種の宣言は意味をなさないでしょう。無名関数を呼び出すために使用することもできます。この宣言方法は、JavaScriptでコンストラクタ関数を作成するためのより良い方法です。
2.関数宣言
function functionTwo() {
// Some code
}
関数宣言は、変数割り当てを必要とせずに名前付き関数変数を定義します。関数宣言はスタンドアロン構造として発生し、非関数ブロック内にネストすることはできません。それらを変数宣言の兄弟として考えることは有用です。 “ var”、Function 宣言は“ function”で始めなければなりません。
これはJavaScriptで関数を呼び出す通常の方法です。JavaScriptのように宣言する前にこの関数を呼び出すこともできますが、すべての関数がHoistedになる場合は、期待どおりにHoistが使用されません。行が大きくなく、コンストラクタ関数でもない通常の関数をすべて呼び出すため。
また、JavaScriptでのホイストのしくみについての詳細情報が必要な場合は、以下のリンクにアクセスしてください。
これは、関数を宣言する2つの方法にすぎません。2番目の方法では、宣言の前に関数を使用できます。
new Function()
は、関数の本体を文字列で渡すために使用できます。したがって、これを使って動的関数を作成することができます。スクリプトを実行せずにスクリプトを渡します。
var func = new Function("x", "y", "return x*y;");
function secondFunction(){
var result;
result = func(10,20);
console.log ( result );
}
secondFunction()
これは関数式と呼ばれます。
var getRectArea = function(width, height) {
return width * height;
};
console.log("Area of Rectangle: " + getRectArea(3,4));
// This should return the following result in the console:
// Area of Rectangle: 12
これは関数宣言と呼ばれます。
var w = 5;
var h = 6;
function RectArea(width, height) { //declaring the function
return area = width * height;
} //note you do not need ; after }
RectArea(w,h); //calling or executing the function
console.log("Area of Rectangle: " + area);
// This should return the following result in the console:
// Area of Rectangle: 30
これが、関数式と関数宣言の違い、およびそれらの使用方法の説明に役立つことを願っています。ありがとう。
Javascriptにはファーストクラスの機能があります。これは、他の変数と同様に扱うことができることを意味します。関数は、関数の引数として渡すことができ、関数から返されます変数に格納できます。
ただし、関数を変数に保存する(関数式)だけが関数を作成する方法ではありません。これは関数宣言を使用して行うこともできます。主な違いは次のとおりです。
undefined
の値を持つように持ち上げられます。以下に例を示します。
try {
functionOne();
} catch (e) {
console.log('i cant run because im not hoisted');
}
functionTwo();
// function expression, does not get hoisted
let functionOne = function randomName() {
// Some code
};
// function declaration, gets hoisted
function functionTwo() {
console.log('I get hoisted');
}
try {
randomName(); // this isn't the proper name, it is functionOne
} catch (e) {
console.log('You cant call me with randomName my name is function one');
}
:
JSでの表現 :値を返すもの
例:クロムコンソールで以下を試してください。
a = 10
output : 10
(1 + 3)
output = 4
宣言/文 :値を返さないもの
例:
if (1 > 2) {
// do something.
}
ここで(1> 2)は式ですが、 'if'文はそうではありません。それは何も返さない。
同様に、関数宣言/ステートメント対関数式があります。
例を見てみましょう:
// test.js
var a = 10;
// function expression
var fun_expression = function() {
console.log("Running function Expression");
}
// funciton expression
function fun_declaration() {
console.log("Running function Statement");
}
重要: JavaScriptエンジンが上記のjsファイルを実行するとどうなりますか。
このjsが実行されると、次のことが起こります。
では、jsをに更新したとします。
// test.js
console.log(a) //output: udefined (No error)
console.log(fun_expression) // output: undefined (No error)
console.log(fun_expression()) // output: Error. As we trying to invoke undefined.
console.log(fun_declaration()) // output: running function statement (As fun_declaration is already hoisted in the memory).
var a = 10;
// function expression
var fun_expression = function() {
console.log('Running function expression')
}
// function declaration
function fun_declaration() {
console.log('running function declaration')
}
console.log(a) // output: 10
console.log(fun_expression()) //output: Running function expression
console.log(fun_declaration()) //output: running function declaration
上記のコメントでの出力は、関数式と関数文/宣言の違いを理解するのに役立ちます。
注意すべき重要な点は以下のとおりです。 -
二つの機能があるとしましょう: -
sum(1,2);
const sum = function(first, second) {
return first + second;
}
上記の場合、sumが定義されていないというエラーが出ますが、
sum(1,2);
function sum(first, second) {
return first + second;
}
この場合function hoisting
が行われるので、この関数はエラーにはなりません。
名前付き関数と無名関数
無名関数式は素早く簡単に入力でき、多くのライブラリやツールはこの慣用的な形式のコードを推奨する傾向があります。ただし、考慮すべきいくつかの 欠点 があります。
命名関数式
関数式に名前を付けることは、これらすべての欠点に非常に効果的に対処し、明らかな欠点はありません。ベストプラクティスは、常に関数式に名前を付けることです。
setTimeout(function timeHandler() { // <-- named function here!
console.log('I've waited 1 second');
}, 1000);
即時呼び出し関数式(IIFE)の命名
var a = 42;
(function IIFE(global) { // <-- named IIFEs!
console.log(global.a); // 42
})(window);
注:この場合、関数に名前を付ける、変数に割り当てられた関数の はあまり一般的ではなく、混乱を引き起こす可能性があります。この場合は、arrow関数を使用することをお勧めします。
関数を変数として定義するのが好きです。
let first = function(x){
return x[0];
}
の代わりに:
function first(){
....
}
関数を定義するとき、私は式とデコレータを使うことができるからです。例えば:
let safe = function(f){
try {f()...}
}
let last = safe(function(x){return x[0]}).
ES6では、はるかに短くなりました。
let last = x => x[0]
...........
function last(x){
return x[0];
}
......
let last = safe(x => x[0]);
両方の関数のもう1つの違いは、functionOneは複数の関数をその中に保持できる変数として使用でき、functionTwoは呼び出されたときにすべて実行されるコードブロックを保持できることです。以下を確認してください。
var functionOne = (function() {
return {
sayHello: function(){
console.log('say hello')
},
redirectPage:function(_url){
window.location.href = _url;
}
}
})();
どの機能を呼び出すかを選択できます。例:functionOne.sayHelloまたはfunctionOne。 redirectPage。また、functionTwoを呼び出すと、コード全体が実行されます。