javascript - js函数变量 - 解释function foo与var foo function用法的区别




var functionName=function(){} vs function functionName(){} (20)

new Function()可用于在函数字符串中传递函数的主体。因此,这可用于创建动态功能。在不执行脚本的情况下传递脚本。

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()

我最近开始维护其他人的JavaScript代码。 我正在修复错误,添加功能,还试图整理代码并使其更加一致。

以前的开发人员使用两种声明函数的方法,如果背后有原因,我就无法解决。

这两种方式是:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

使用这两种不同方法的原因是什么?每种方法的优缺点是什么? 有一种方法可以通过一种方法完成,而另一种方法无法做到吗?


Hoisting 是JavaScript解释器将所有变量和函数声明移动到当前范围顶部的操作。

但是,只有实际的声明才会被提升。 将任务留在原处。

  • 在页面内声明的变量/函数是全局的,可以访问该页面中的任何位置。
  • 函数内声明的变量/函数具有局部范围。 意味着它们在功能体(范围)内可用/访问,它们在功能体外部不可用。

Variable

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引入了箭头函数 :箭头函数表达式具有较短的语法,它们最适合非方法函数,并且它们不能用作构造函数。

ArrowFunction : ArrowParameters => ConciseBody

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd

一个重要原因是添加一个且只有一个变量作为命名空间的“根”...

var MyNamespace = {}
MyNamespace.foo= function() {

}

要么

var MyNamespace = {
  foo: function() {
  },
  ...
}

命名空间有很多技巧。 随着大量JavaScript模块的出现,它变得越来越重要。

另请参阅如何在JavaScript中声明命名空间?


一旦建立绑定,分配给变量的函数声明和函数表达式的行为相同。

然而,函数对象与其变量实际关联的方式时间存在差异。这种差异是由于JavaScript中的变量提升机制造成的。

基本上,所有函数声明和变量声明都被提升到声明发生的函数的顶部(这就是我们说JavaScript具有函数作用域的原因)。

  • 当函数声明被提升时,函数体“跟随”,因此当评估函数体时,变量将立即绑定到函数对象。

  • 当一个变量声明悬挂,初始化并没有跟随,而是“留下”。变量初始化为undefined函数体的开头,并在代码中的原始位置分配一个值。(实际上,它将在每个发生具有相同名称的变量声明的位置分配一个值。)

提升的顺序也很重要:函数声明优先于具有相同名称的变量声明,并且最后一个函数声明优先于具有相同名称的先前函数声明。

一些例子...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

变量foo被提升到的功能,初始化的顶部undefined,从而使!footrue,所以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中,使用通过计算第二个函数声明得到的函数对象进行初始化,然后进行分配4

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

这里首先提升函数声明,声明并初始化变量a。接下来,分配此变量10。换句话说:赋值不分配给外部变量a


不同之处在于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有效,在这种情况下它只会引发错误。


何时优先考虑第一种方法到第二种方法的说明是当您需要避免覆盖函数的先前定义时。

if (condition){
    function myfunction(){
        // Some code
    }
}

myfunction这个定义将覆盖任何先前的定义,因为它将在分析时完成。

if (condition){
    var myfunction = function (){
        // Some code
    }
}

只有在满足condition时才能正确定义myfunction


其他评论者已经涵盖了上述两种变体的语义差异。 我想要注意一个风格差异:只有“赋值”变体可以设置另一个对象的属性。

我经常用这样的模式构建JavaScript模块:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

使用此模式,您的公共函数将全部使用赋值,而您的私有函数使用声明。

(另请注意,赋值在语句后应该使用分号,而声明禁止它。)


在JavaScript中有两种创建函数的方法:

  1. 功能声明:

    function fn(){
      console.log("Hello");
    }
    fn();
    

    这是非常基本的,不言自明的,在C语言系列中以多种语言和标准使用。我们声明了一个函数定义它并通过调用它来执行它。

    您应该知道的是,函数实际上是JavaScript中的对象; 在内部,我们为上面的函数创建了一个对象,并给它一个名为fn的名称,或者对象的引用存储在fn中。函数是JavaScript中的对象; 函数实例实际上是一个对象实例。

  2. 功能表达:

    var fn=function(){
      console.log("Hello");
    }
    fn();
    

    JavaScript具有一流的功能,即创建一个函数并将其分配给变量,就像创建字符串或数字并将其分配给变量一样。这里,fn变量被赋值给一个函数。这个概念的原因是函数是JavaScript中的对象; fn指向上述函数的对象实例。我们初始化了一个函数并将其分配给变量。它没有执行该功能并分配结果。

参考:JavaScript函数声明语法:var fn = function(){} vs function fn(){}


如果您将使用这些函数来创建对象,您将获得:

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

它们非常相似,只有一些小差异,第一个是分配给匿名函数的变量(函数声明),第二个是在JavaScript中创建函数的常规方法(匿名函数声明),两者都有用法,缺点和优点:

1.功能表达

var functionOne = function() {
    // Some code
};

函数表达式将函数定义为更大表达式语法(通常是变量赋值)的一部分。通过函数表达式定义的函数可以命名或匿名。函数表达式不能以“function”开头(因此下面的自调用示例的括号)。

将一个变量分配给一个函数,意味着没有提升,因为我们知道JavaScript中的函数可以提升,意味着它们可以在声明之前被调用,而变量需要在访问它们之前声明,所以在这种情况下意味着我们不能在声明它之前访问函数,也可以是你编写函数的一种方式,对于返回另一个函数的函数,这种声明是有意义的,同样在ECMA6及以上你可以将它分配给一个箭头函数可以用来调用匿名函数,这种声明方式也是在JavaScript中创建构造函数的更好方法。

2.功能声明

function functionTwo() {
    // Some code
}

函数声明定义了一个命名函数变量,无需变量赋值。函数声明作为独立构造出现,不能嵌套在非函数块中。将它们视为变量声明的兄弟是有帮助的。正如变量声明必须以“var”开头一样,函数声明必须以“function”开头。

这是在JavaScript中调用函数的常规方法,这个函数可以在你将它声明之前调用,因为在JavaScript中所有函数都被提升,但是如果你有'use strict',这将不会像预期的那样提升,这是一个好方法调用行中不大的所有普通函数,也不是构造函数。

此外,如果您需要有关如何在JavaScript中提升的更多信息,请访问以下链接:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting


我列出了以下差异:

  1. 函数声明可以放在代码中的任何位置。即使在定义出现在代码之前调用它,它也会在函数声明被提交到内存或以某种方式被提升之前执行,之后页面中的任何其他代码开始执行。

    看看下面的功能:

    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
    

    函数表达式(如果在调用之前未定义)将导致错误。此外,这里函数定义本身不会像函数声明一样移动到顶部或提交到内存。但是我们分配函数的变量被提升并且未定义被分配给它。

    使用函数表达式的相同函数

    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
    
  2. 它是不是安全写在非功能块函数声明一样,如果因为他们将无法访问。

    if (test) {
        function x() { doSomething(); }
    }
    
  3. 如下所示的命名函数表达式可能无法在版本9之前的Internet Explorer浏览器中使用。

    var today = function today() {return new Date()}
    

我在代码中使用变量方法是出于一个非常具体的原因,其理论已经在上面以一种抽象的方式进行了介绍,但是一个例子可能会帮助像我这样的人,JavaScript专业知识有限。

我有代码,我需要运行160个独立设计的品牌。大多数代码都在共享文件中,但品牌特定的东西在一个单独的文件中,每个品牌一个。

有些品牌需要特定功能,有些则不需要。有时我必须添加新功能来执行新的品牌特定事物。我很乐意更改共享编码,但我不想更改所有160套品牌文件。

通过使用变量语法,我可以在共享代码中声明变量(本质上是一个函数指针),并分配一个普通的存根函数,或设置为null。

然后,需要特定功能实现的一个或两个品牌可以定义它们的函数版本,并在需要时将其分配给变量,其余的则不做任何事情。我可以在共享代码中执行之前测试null函数。

根据上面人们的评论,我认为也可以重新定义静态函数,但我认为变量解决方案很好而且清晰。


更好地解释格雷格的答案

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();
                            ---------------

格雷格的答案已经足够好了,但我仍然想补充一点,我刚才看到道格拉斯·克罗克福德的视频。

功能表达:

var foo = function foo() {};

功能说明:

function foo() {};

函数语句只是var带有function值的语句的简写。

所以

function foo() {};

扩展到

var foo = function foo() {};

其进一步扩展到:

var foo = undefined;
foo = function foo() {};

它们都被提升到代码的顶部。


第一个(函数doSomething(x))应该是对象表示法的一部分。

第二个(var doSomething = function(x){ alert(x);})只是创建一个匿名函数并将其赋值给变量doSomething。所以doSomething()会调用该函数。

您可能想知道函数声明函数表达式是什么。

函数声明定义了一个命名函数变量,而不需要变量赋值。函数声明作为独立构造出现,不能嵌套在非函数块中。

function foo() {
    return 3;
}

ECMA 5(13.0)将语法定义为
函数Identifier(FormalParameterList opt){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)将语法定义为
函数Identifier opt(FormalParameterList opt){FunctionBody}


给出了一个例子,他将一个指定的函数命名为能够shortcut()用作自身的内部引用。John Resig给出了另一个例子 - 在他的Learning Advanced Javascript教程中复制分配给另一个对象的递归函数。虽然向属性分配函数并不是严格意义上的问题,但我建议您主动尝试教程 - 通过单击右上角的按钮运行代码,然后双击代码进行编辑以符合您的喜好。

本教程中的示例:递归调用yell()

删除原始ninja对象时测试失败。(第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." );

这只是声明函数的两种可能方式,在第二种方式中,您可以在声明之前使用该函数。


这是创建函数的标准表单的纲要:( 最初是为另一个问题编写的,但在被移入规范问题后进行了调整。)

条款:

快速清单:

  • 功能声明

  • “匿名” function表达式(尽管有术语,有时会创建带有名称的函数)

  • 命名function表达式

  • 存取器功能初始化器(ES5 +)

  • 箭头函数表达式(ES2015 +) (与匿名函数表达式一样,不涉及显式名称,但可以创建带有名称的函数)

  • 对象初始化程序中的方法声明(ES2015 +)

  • 类中的构造函数和方法声明(ES2015 +)

功能声明

第一种形式是函数声明 ,如下所示:

function x() {
    console.log('x');
}

函数声明是一个声明 ; 这不是陈述或表达。 因此,你不遵循它; (虽然这样做是无害的)。

在执行任何逐步执行代码之前 ,执行进入其出现的上下文时,将处理函数声明。 它创建的函数有一个专有名称(上例中的x ),该名称放在声明出现的范围内。

因为它是在同一个上下文中的任何分步代码之前处理的,所以你可以这样做:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

直到ES2015,规范没有涵盖JavaScript引擎应该做的事情,如果你在一个控制结构中放置一个函数声明,如tryifswitchwhile等,如下所示:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

而且由于它们是逐步运行代码之前进行处理的,所以当它们处于控制结构中时,知道该怎么做是很棘手的。

尽管在ES2015之前没有指定这样做,但是它是一个允许的扩展来支持块中的函数声明。 不幸的是(并且不可避免地),不同的引擎做了不同的事情。

从ES2015开始,规范说明了该怎么做。 事实上,它提供了三个单独的事情:

  1. 如果松散模式不在 Web浏览器上,则JavaScript引擎应该做一件事
  2. 如果在Web浏览器上处于松散模式,则JavaScript引擎应该执行其他操作
  3. 如果在严格模式下(浏览器与否),JavaScript引擎应该做另一件事

松散模式的规则很棘手,但在严格模式下,块中的函数声明很容易:它们是块的本地(它们具有块范围 ,这在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表达

第二种常见形式称为匿名函数表达式

var y = function () {
    console.log('y');
};

与所有表达式一样,它是在逐步执行代码时达到的。

在ES5中,它创建的函数没有名称(它是匿名的)。 在ES2015中,如果可能,通过从上下文推断出该函数的名称。 在上面的示例中,名称将为y 。 当函数是属性初始值设定项的值时,会执行类似的操作。 (有关何时发生这种情况以及规则的详细信息,请在规范中搜索SetFunctionName - 它遍布整个地方。)

命名function表达式

第三种形式是命名函数表达式 (“NFE”):

var z = function w() {
    console.log('zw')
};

它创建的函数具有正确的名称(在本例中为w )。 与所有表达式一样,在逐步执行代码时会对其进行评估。 函数的名称添加到表达式出现的范围中; 名称在函数本身的范围内:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

请注意,NFE经常成为JavaScript实现的错误来源。 例如,IE8及更早版本完全错误地处理NFE,在两个不同的时间创建两个不同的函数。 早期版本的Safari也存在问题。 好消息是当前版本的浏览器(IE9及更高版本,当前的Safari)不再存在这些问题。 (但在撰写本文时,遗憾的是,IE8仍然广泛使用,因此使用NFE和Web代码一般仍然存在问题。)

存取器功能初始化器(ES5 +)

有时功能可以潜入大部分未被注意到; 这是访问者功能的情况。 这是一个例子:

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"

注意,当我使用该函数时,我没有使用() ! 那是因为它是一个属性的访问函数 。 我们以正常方式获取并设置属性,但在幕后,调用该函数。

您还可以使用Object.definePropertyObject.definePropertiesObject.create的鲜为人知的第二个参数创建访问器函数。

箭头功能表达(ES2015 +)

ES2015为我们带来了箭头功能 。 这是一个例子:

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

看到n => n * 2隐藏在map()调用中的东西? 这是一个功能。

关于箭头功能的一些事情:

  1. 他们没有自己的this 。 相反,他们关闭了定义它们的上下文。 (它们也关闭了arguments并且在相关的情况下, super 。)这意味着它们中的这个与创建它们的地方相同,并且不能更改。

  2. 正如您已经注意到的那样,您不使用关键字function ; 相反,你使用=>

上面的n => n * 2示例是它们的一种形式。 如果您有多个参数来传递函数,则使用parens:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

(请记住, Array#map将条目作为第一个参数传递,将索引作为第二个参数传递。)

在这两种情况下,函数的主体只是一个表达式; 函数的返回值将自动成为该表达式的结果(您不使用显式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));

没有{ ... }的版本称为带有表达体简洁体的箭头函数。 (另外:一个简洁的箭头函数。) { ... }定义主体的那个是带有函数体的箭头函数。 (另外:一个冗长的箭头功能。)

对象初始化程序中的方法声明(ES2015 +)

ES2015允许更短的形式声明引用函数的属性; 它看起来像这样:

var o = {
    foo() {
    }
};

ES5及更早版本中的等价物是:

var o = {
    foo: function foo() {
    }
};

类中的构造函数和方法声明(ES2015 +)

ES2015为我们带来了class语法,包括声明的构造函数和方法:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

上面有两个函数声明:一个用于构造函数,它获取名称Person ,另一个用于getFullName ,它是分配给Person.prototype的函数。


首先,我想纠正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;

在这种情况下, xyzabc都是同一对象的别名:

console.log(xyz === abc); // prints "true"

使用组合样式的一个令人信服的理由是函数对象的“名称”属性( Internet Explorer不支持 )。 基本上当你定义一个像这样的函数

function abc(){};
console.log(abc.name); // prints "abc"

其名称将自动分配。 但是当你定义它时

var abc = function(){};
console.log(abc.name); // prints ""

它的名称是空的 - 我们创建了一个匿名函数并将其分配给某个变量。

使用组合样式的另一个好理由是使用简短的内部名称来引用自身,同时为外部用户提供长的非冲突名称:

// 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);
  // ...
}

在上面的示例中,我们可以使用外部名称执行相同操作,但它会过于笨重(并且速度较慢)。

(另一种引用自身的方法是使用arguments.callee ,它仍然相对较长,在严格模式下不受支持。)

在内心深处,JavaScript以不同的方式处理两种语句。 这是一个函数声明:

function abc(){}

abc here在当前范围内的任何位置定义:

// 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()的情况下 - 答案是:它取决于浏览器。


两者都是定义函数的不同方式。不同之处在于浏览器如何解释并将它们加载到执行上下文中。

第一种情况是函数表达式,仅在解释器到达该行代码时才加载。因此,如果您执行以下操作,您将收到一个错误,即functionOne不是函数

functionOne();
var functionOne = function() {
    // Some code
};

原因是在第一行没有为functionOne赋值,因此它是未定义的。我们试图将其称为函数,因此我们遇到了错误。

在第二行,我们将一个匿名函数的引用分配给functionOne。

第二种情况是在执行任何代码之前加载的函数声明。因此,如果您喜欢以下内容,则在代码执行之前加载声明时不会出现任何错误。

functionOne();
function functionOne() {
   // Some code
}






idioms