Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/409.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript var functionName=function(){}vs function functionName(){}_Javascript_Function_Syntax_Idioms - Fatal编程技术网

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

Javascript var functionName=function(){}vs function functionName(){},javascript,function,syntax,idioms,Javascript,Function,Syntax,Idioms,我最近开始维护其他人的JavaScript代码。我正在修复bug,添加特性,并试图整理代码,使其更加一致 以前的开发人员使用了两种声明函数的方法,我无法确定这背后是否有原因 这两种方式是: var functionOne = function() { // Some code }; 使用这两种不同方法的原因是什么?每种方法的优缺点是什么?有什么可以用一个方法完成而不能用另一个方法完成的吗?不同之处在于,functionOne是一个函数表达式,因此只有在到达该行时才定义,而functio

我最近开始维护其他人的JavaScript代码。我正在修复bug,添加特性,并试图整理代码,使其更加一致

以前的开发人员使用了两种声明函数的方法,我无法确定这背后是否有原因

这两种方式是:

var functionOne = function() {
    // Some code
};
使用这两种不同方法的原因是什么?每种方法的优缺点是什么?有什么可以用一个方法完成而不能用另一个方法完成的吗?

不同之处在于,functionOne是一个函数表达式,因此只有在到达该行时才定义,而functionTwo是一个函数声明,并且在其周围的函数或脚本由于错误而执行时才定义

例如,函数表达式:

var xyz = function(){};
var abc = function() {};
var fn=function(){
  console.log("Hello");
}
fn();
//TypeError:functionOne不是函数 功能一; var functionOne=函数{ 控制台,你好!;
}; 首先我想更正Greg:函数abc{}也是作用域-名称abc是在遇到此定义的作用域中定义的。例如:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here
其次,可以将两种风格结合起来:

var xyz = function abc(){};
xyz将像往常一样定义,abc在除InternetExplorer之外的所有浏览器中都未定义-不要依赖于它的定义。但它将在其体内定义:

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;
function abc(){}
function abc(){}
function fn(){
  console.log("Hello");
}
fn();
在这种情况下,xyz和abc都是同一对象的别名:

console.log(xyz === abc); // prints "true"
使用组合样式的一个引人注目的原因是InternetExplorer不支持的函数对象的name属性。基本上当你定义一个函数,比如

function abc(){};
console.log(abc.name); // prints "abc"
var abc = function(){};
abc = function(){};
它的名称是自动指定的。但当你把它定义为

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,它仍然相对较长,并且在strict模式下不受支持

实际上,JavaScript对这两条语句的处理方式不同。这是一个函数声明:

function abc(){};
var xyz = abc;
function abc(){}
function abc(){}
function fn(){
  console.log("Hello");
}
fn();
abc在当前范围内的任何地方都有定义:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works
此外,它还通过一个返回声明:

// We can call it here
abc(); // Works
return;
function abc(){}
这是一个函数表达式:

var xyz = function(){};
var abc = function() {};
var fn=function(){
  console.log("Hello");
}
fn();
这里的xyz是从分配点定义的:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works
函数声明与函数表达式是格雷格所证明的差异的真正原因

有趣的事实:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
就个人而言,我更喜欢函数表达式声明,因为这样可以控制可见性。当我定义函数时,比如

function abc(){};
console.log(abc.name); // prints "abc"
var abc = function(){};
abc = function(){};
我知道我在本地定义了函数。当我定义函数时,比如

function abc(){};
console.log(abc.name); // prints "abc"
var abc = function(){};
abc = function(){};
我知道我在全球范围内定义了它,但前提是我没有在范围链的任何地方定义abc。即使在eval中使用,这种定义风格也是有弹性的。而定义

function abc(){};

取决于上下文,可能会让您猜测它的实际定义,尤其是在eval的情况下-答案是:它取决于浏览器。

在计算机科学术语中,我们谈论匿名函数和命名函数。我认为最重要的区别在于匿名函数不绑定到名称,因此命名为匿名函数。在JavaScript中,它是在运行时动态声明的第一类对象


有关匿名函数和lambda演算的更多信息,Wikipedia是一个良好的开端:。

就代码维护成本而言,命名函数更可取:

独立于声明地点,但仍受范围限制。 更能抵抗条件初始化之类的错误,如果需要,您仍然可以重写。 通过将本地函数与作用域功能分开分配,代码变得更可读。通常在作用域中,先是功能,然后是本地函数的声明。 在调试器中,您将清楚地看到调用堆栈上的函数名,而不是匿名/求值函数。 我怀疑命名函数还有更多的优点。命名函数的优点是匿名函数的缺点

历史上,匿名函数出现的原因是JavaScript无法作为一种语言列出具有命名函数的成员:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}

您在那里发布的两个代码片段在几乎所有情况下的行为都是相同的

然而,行为上的区别在于,对于第一个变量var functionOne=function{},只能在代码中该点之后调用该函数

对于第二个变量函数functionTwo,该函数可用于在声明该函数的位置上运行的代码

这是因为对于第一个变量,functio n在运行时分配给变量foo。在第二种情况下,在解析时将函数分配给该标识符foo

更多技术信息

JavaScript有三种定义函数的方法

您的第一个代码段显示了一个函数表达式。这涉及到使用函数操作符创建函数-该操作符的结果可以存储在任何变量或对象属性中。通过这种方式,函数表达式非常强大。函数表达式通常称为匿名函数,因为它不必有名称, 第二个示例是函数声明。这将使用function语句创建函数。该函数在解析时可用,可以在该范围内的任何位置调用。以后仍可以将其存储在变量或对象属性中。 定义函数的第三种方法是函数构造函数,这在您的原始帖子中没有显示。不建议使用它,因为它的工作方式与eval相同,eval也有它的问题。
说到全局上下文,最后的var语句和FunctionDeclaration都会在全局对象上创建一个不可删除的属性,但是两者的值都可以被覆盖

这两种方法之间的细微差别在于,当流程在实际代码执行之前运行时,所有用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;
两个示例之间的另一个明显区别是,第一个函数没有名称,但第二个函数有名称,这在调试(即检查调用堆栈)时非常有用

关于您编辑的第一个示例foo=函数{alert'hello!';};,这是一个未声明的任务,我强烈建议您始终使用var关键字

使用赋值时,如果在范围链中找不到引用的标识符,则不使用var语句,它将成为全局对象的可删除属性

另外,未声明的赋值会在ECMAScript 5上抛出一个ReferenceError

A必须阅读:


注意:此答案已从中合并,其中OP的主要疑问和误解是无法覆盖使用FunctionDeclaration声明的标识符,但事实并非如此。

一个重要原因是添加一个且仅一个变量作为名称空间的根

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

}

有许多用于名称空间的技术。随着大量可用的JavaScript模块的出现,这一点变得越来越重要


另请参见

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

我经常使用如下模式构建JavaScript模块:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();
function x() {
    console.log('x');
}
x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}
if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}
var o = {
    foo() {
    }
};
functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };
使用此模式,公共函数将全部使用赋值,而私有函数使用声明

还要注意的是,赋值应该在语句后加上分号,而声明禁止使用分号。

给出了一个例子,其中他作为对自身的内部引用。给出了另一个例子-在他的教程中复制分配给另一个对象的递归函数。虽然将函数指定给属性并不是严格意义上的问题,但我建议您积极尝试本教程-通过单击右上角的按钮运行代码,然后双击代码进行编辑

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

第13页

第14页


其他答案中没有提到的另一个区别是,如果您使用匿名函数

var functionOne = function() {
    // Some code
};
并将其用作构造函数,如中所示

var one = new functionOne();
然后将不定义one.constructor.name。Function.name是非标准的,但受Firefox、Chrome、其他Webkit衍生浏览器和IE 9+的支持


可以用两个.constructor.name作为字符串检索构造函数的名称。

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

我有代码,我需要运行160个独立设计的品牌。大部分代码都在共享文件中,但品牌特定的内容在单独的文件中,每个品牌对应一个文件

有些品牌需要特定的功能,有些则不需要。有时我不得不添加新的功能来做新的品牌特定的事情。我很乐意更改共享代码,但我不想更改所有160组品牌文件

通过使用变量语法,我可以将变量声明为sh中的函数指针 ared代码,或者分配一个平凡的存根函数,或者设置为null

需要特定函数实现的一两个品牌可以定义其函数版本,并根据需要将其分配给变量,其余品牌则不做任何操作。我可以在共享代码中执行空函数之前测试它


从上面人们的评论中,我认为重新定义一个静态函数也是可能的,但我认为变量的解决方案是好的和清晰的。

第一个函数doSomethingx应该是对象符号的一部分

第二个变量var doSomething=functionx{alertx;}只是创建一个匿名函数并将其分配给变量doSomething。因此doSomething将调用该函数

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
您可能想知道什么是函数声明和函数表达式

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

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}


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

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

基本上,所有函数声明和变量声明都被提升到声明所在函数的顶部,这就是为什么我们说JavaScript具有函数作用域

悬挂功能声明时,功能体如下 因此,当对函数体求值时,变量将立即 必须绑定到函数对象

当挂起变量声明时,初始化不起作用 跟随,但被落在后面。变量初始化为 在函数体的开头未定义,并且将被分配 位于代码中原始位置的值。实际上,在声明同名变量的每个位置,都会给它赋值

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

一些例子

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10
变量foo被提升到函数的顶部,初始化为未定义,以便!foo为true,因此foo被赋值为10。酒吧范围外的foo不扮演任何角色,也未被触及

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


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

当您需要避免覆盖函数以前的定义时,说明何时首选第一种方法而不是第二种方法

,myfunction的此定义将覆盖以前的任何定义,因为它将在解析时完成


仅当满足条件时,才正确定义myfunction。

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

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

下面是创建函数的标准表单的概述:最初是为另一个问题编写的,但在转移到规范问题之后进行了修改

条款:

ES5:,2009 ES2015:也称为ES6 快速列表:

函数声明

匿名函数表达式,尽管有术语,但有时创建具有名称的函数

命名函数表达式

访问器函数初始值设定项ES5+

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

对象初始值设定项ES2015中的方法声明+

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

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

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();
function x() {
    console.log('x');
}
x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}
if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}
var o = {
    foo() {
    }
};
functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };
函数声明是一种声明;这不是一种陈述或表达 N因此,你不必带着怀疑去跟随它;尽管这样做是无害的

在执行任何分步代码之前,当执行进入其出现的上下文时,将处理函数声明。在上面的示例中,它创建的函数被赋予了一个正确的名称x,该名称被放在声明出现的范围内

因为它是在同一上下文中的任何分步代码之前处理的,所以您可以执行以下操作:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();
function x() {
    console.log('x');
}
x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}
if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}
var o = {
    foo() {
    }
};
functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };
在ES2015之前,规范没有涵盖JavaScript引擎应该做什么,如果您将函数声明放在控件结构(如try、if、switch、while等)中,比如:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();
function x() {
    console.log('x');
}
x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}
if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}
var o = {
    foo() {
    }
};
functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };
匿名函数表达式 第二种常见形式称为匿名函数表达式:

var xyz = function(){};
var abc = function() {};
var fn=function(){
  console.log("Hello");
}
fn();
与所有表达式一样,它在代码的逐步执行过程中到达时进行计算

在ES5中,它创建的函数没有名字,它是匿名的。在ES2015中,如果可能的话,通过从上下文推断函数来为其指定名称。在上面的例子中,名称应该是y。当函数是属性初始值设定项的值时,也会执行类似的操作。有关何时发生这种情况以及规则的详细信息,请在-中搜索SetFunctionName,它会到处出现

命名函数表达式 第三种形式是命名函数表达式NFE:

在本例中,该函数创建的函数有一个专有名称w。与所有表达式一样,当在代码的逐步执行中达到该值时,将对其进行计算。函数名不会添加到表达式出现的范围中;名称在函数本身的范围内:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"
请注意,NFE经常是JavaScript实现的bug源。例如,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.defineProperty、Object.defineProperties和Object.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了吗?这是一个函数

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
关于arrow函数,有两点:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
他们没有自己的这个。相反,它们结束了定义它们的上下文的这一部分。他们也会结束争论,如果相关的话,还会结束讨论。这意味着它们内部的This与创建它们的This相同,不能更改

正如您在上面所注意到的,您没有使用关键字函数;而是使用=>

上面的n=>n*2示例就是其中的一种形式。如果有多个参数要传递函数,请使用参数:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
请记住,Arraymap将条目作为第一个参数传递,索引作为第二个参数传递

在这两种情况下,函数体只是一个表达式;如果不使用显式返回,函数的返回值将自动成为该表达式的结果

如果您所做的不仅仅是一个表达式,如果需要返回值,请使用{}和显式返回,这与正常情况相同:

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));
没有{…}的版本称为带有表达式体或简明体的箭头函数。另外:一个简洁的箭头函数。带有{…}定义主体的函数是带有函数主体的箭头函数。另外:一个详细的箭头函数

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
对象初始值设定项ES2015中的方法声明+ ES2015允许以较短的形式声明引用称为方法定义的函数的属性;看起来是这样的:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();
function x() {
    console.log('x');
}
x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}
if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}
var o = {
    foo() {
    }
};
functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };
在ES5和更早版本中,几乎相当于:

var o = {
    foo: function foo() {
    }
};
与冗长不同的是,方法可以使用super,但函数不能。因此,例如,如果您有一个使用方法语法定义了say valueOf的对象,它可以使用super.valueOf来获取值object.prototype.valueOf将在使用它执行其他操作之前返回,而ES5版本则必须执行object.prototype.valueOf.callthis

这也意味着该方法有一个对其上定义的对象的引用,因此,如果该对象是临时对象,例如,您将其传递到object.assign作为源对象之一,则方法语法可能意味着该对象保留在内存中,否则,如果 如果没有一个方法使用super,JavaScript引擎不会检测到这种情况并进行处理

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

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

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

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

第一个示例是函数声明:

function abc(){};
var xyz = abc;
function abc(){}
function abc(){}
function fn(){
  console.log("Hello");
}
fn();
第二个示例是函数表达式:

var xyz = function(){};
var abc = function() {};
var fn=function(){
  console.log("Hello");
}
fn();
主要区别在于它们的吊装和申报方式。在第一个示例中,整个函数声明被挂起。在第二个示例中,只有var‘abc’被提升,其函数值将未定义,函数本身保持在声明的位置

简言之:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}
为了更深入地研究这个话题,我强烈建议你 更好的解释

为什么没有错误?我们一直被教导,表达式是从上到下执行的

因为: 函数声明和变量声明总是被JavaScript解释器以不可见的方式移动到其包含范围的顶部。显然,函数参数和语言定义的名称已经存在

这意味着像这样的代码:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

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

    return exports;
})();
function x() {
    console.log('x');
}
x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}
if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}
var o = {
    foo() {
    }
};
functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };
请注意,声明的转让部分未被悬挂。只有名字被悬挂

但如果有功能声明,整个功能体也将被提升:


根据stack traces参数中显示的命名函数,现代JavaScript引擎实际上非常能够表示匿名函数

在撰写本文时,V8、SpiderMonkey、Chakra和Nitro总是按名称引用命名函数。如果匿名函数有标识符,它们几乎总是通过其标识符引用匿名函数

SpiderMonkey可以找出从另一个函数返回的匿名函数的名称。其他人不行

如果您真的非常希望迭代器和成功回调显示在跟踪中,您也可以将它们命名为

[].forEach(function iterator() {});
但在大多数情况下,这不值得过分强调

马具 V8 蜘蛛门钥匙 硝基
我添加我自己的答案,只是因为其他人已经完全涵盖了提升部分

很长一段时间以来,我一直想知道哪种方式更好,多亏了现在我知道了:


函数声明更快,这才是web开发中真正重要的,对吧

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

已经足够好了,但是我仍然想添加一些我刚才在观看视频时学到的东西

函数表达式:

var xyz = function(){};
var abc = function() {};
var fn=function(){
  console.log("Hello");
}
fn();
函数语句:

// We can call it here
abc(); // Works
return;
function abc(){}
function语句只是带有函数值的var语句的简写

所以

扩展到

var foo = function foo() {};
进一步扩展到:

var foo = undefined;
foo = function foo() {};
它们都被吊到了代码的顶端


我在下面列出了不同之处:

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

functionOne();
function functionOne() {
   // Some code
}
请看下面的函数:

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
在非函数块(如if)中编写函数声明是不安全的,因为它们不可访问

if (test) {
    function x() { doSomething(); }
}
命名函数表达式(如下所示)在InternetExplorer 9之前的浏览器中可能不起作用

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

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

第一种情况是函数表达式,它仅在解释器到达该代码行时加载。因此,如果您像下面这样做,您将得到一个错误,functionOne不是一个函数

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
原因是在第一行没有为functionOne赋值,因此它是未定义的。我们试图将其作为函数调用,因此我们得到了一个错误

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

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

functionOne();
function functionOne() {
   // Some code
}
是JavaScript解释器移动所有变量和函数decl的动作 到当前范围的顶部

然而,只有实际的申报才被悬挂。把作业留在原地

页面中声明的变量/函数是全局变量,可以访问该页面中的任何位置。 函数中声明的变量/函数具有局部作用域。表示它们在功能体范围内可用/访问,在功能体范围外不可用。 Javascript被称为松散类型语言。这意味着Javascript变量可以保存任何值。Javascript会根据运行时提供的值/文字自动更改变量类型

作用

页面内声明的函数被提升到具有全局访问权限的页面顶部。 功能块内声明的功能被提升到功能块顶部。 函数的默认返回值为,声明默认值也为“未定义”

Scope with respect to function-block global. 
Scope with respect to page undefined | not available.
函数声明

函数表达式

分配给变量的函数示例:

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();
您可以通过不同浏览器的使用检查函数声明、表达式测试

:使用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函数:Arrow函数表达式的语法较短,最适合非方法函数,并且不能用作构造函数

新函数可用于以字符串形式传递函数体。因此,这可以用来创建动态函数。也可以在不执行脚本的情况下传递脚本

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

secondFunction()

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

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}
功能声明:

function abc(){};
var xyz = abc;
function abc(){}
function abc(){}
function fn(){
  console.log("Hello");
}
fn();
这是非常基本的、自解释的,在许多语言中使用,并且是跨C语言家族的标准。我们声明了一个定义它的函数,并通过调用它来执行它

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

函数表达式:

var xyz = function(){};
var abc = function() {};
var fn=function(){
  console.log("Hello");
}
fn();
JavaScript具有一流的函数,即创建函数并将其分配给变量,就像创建字符串或数字并将其分配给变量一样。这里,fn变量被分配给一个函数。这个概念的原因是函数是JavaScript中的对象;fn指向上述函数的对象实例。我们已经初始化了一个函数并将其分配给一个变量。它不是执行函数并分配结果


参考:

它们非常相似,但有一些小的区别,第一个是分配给匿名函数声明的变量,第二个是在JavaScriptAnonymous函数声明中创建函数的常规方法,两者都有用法,优缺点:

一,。函数表达式

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

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

二,。函数声明

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

// 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!");
})();
这是在JavaScript中调用函数的正常方式,在您将其声明为JavaScript中的所有函数都被提升之前,就可以调用该函数,但是如果您使用了“use strict”,这将不会像预期的那样提升,这是调用所有行数不大且都不是构造函数的正常函数的好方法

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
此外,如果您需要有关JavaScript中提升工作原理的更多信息,请访问以下链接:

关于性能:

新版本的V8引入了se veral在引擎盖下进行了优化,SpiderMonkey也进行了优化

现在表达式和声明之间几乎没有区别。函数表达式

铬62.0.3202

火狐55

铬金丝雀63.0.3225

匿名函数表达式 针对命名函数表达式

火狐 铬金丝雀 铬

虽然这是正确的,但这一点与所问的问题有什么关系?结果差异太小,不能视为差异。如果您将测试运行100次,您将得到100个结果。@RonnySherer,您熟悉jsperf吗?测试是在运行超过1000万次之后进行的!每次测量都有干扰。计算机可能会发现它不处于相同的状态,并且这不是计算机上运行的唯一进程。当差异如此之小时,就意味着你不能依赖它,它实际上是一样的。试着一个接一个地运行sane测试10次,你会发现数字是不同的。非常接近,但不尽相同。@RonnySherer js perf创建了一个虚拟环境,特别是用来解释具有这些微小差异的进程。它没有在我的计算机上运行。它只运行那个。当事情这么小的时候,也许有人不该管它。但无论如何,我都会正确计算并报告。如果有人想在一个循环中使用它,进行数十亿次迭代,那么他应该选择性能最好的函数。虚拟环境位于一个服务器上,它可能会做其他事情。我做了一些测试。结果永远不会完全相同。函数定义在代码进入周围块时执行,而不是在代码进入封闭函数时执行。我不知道事情是否总是这样,但如果一个块使用let或const来定义一个被其中的函数关闭的变量,这是不可避免的,一致地应用该规则可能比仅在不可避免的情况下应用该规则要好。由于提升而产生的句子可能会给人一种错误的印象,即只有命名的函数被提升。事实上,var functionOne和functionTwo都在某种程度上被提升——只是functionOne被设置为undefined,你可以称之为半提升,变量总是被提升到那个程度,而functionTwo是完全提升的,因为它是定义和声明的。调用未定义的内容当然会抛出一个类型错误。感谢Greg和@Ben_Aston解释了这一区别。但是,你们中的一位是否也可以按照用户的要求给出一些“优点”和“缺点”的说明呢?请看,不同的resultsvar abc=function{};console.logabc.name;//abc//从2021年开始,JS运行时明显变得更加智能。然而,总结一下:var abc==>function{};console.logabc.name;//没有什么