从ECMAScript规范深度分析JavaScript(六):function 函数

本文探讨了ECMAScript中的函数类型,包括函数声明、函数表达式和通过Function构造函数创建的函数。重点分析了函数声明对变量对象的影响、函数表达式的特性和命名函数表达式的内部机制。此外,还解答了函数声明与函数表达式在使用中的区别,以及立即执行函数表达式(IIFE)的括号包裹原因。
摘要由CSDN通过智能技术生成

本文译自Dmitry Soshnikov的《ECMA-262-3 in detail》系列教程。其中会加入一些个人见解以及配图举例等等,来帮助读者更好的理解JavaScript。

前言

在本文中,我们将讨论一个通用的ECMAScript对象——关于函数。特别是,我们将讨论各种类型的函数,定义每种类型如何影响上下文的变量对象,以及每个函数的作用域链中包含什么。我们会回答一些常见的问题,例如:以下所创建的函数之间有什么不同(如果有,是什么不同?)

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

通过习惯的方式定义的函数:

    function foo() {
   
      ...
    }

又或者说,为什么在下面这个调用,函数被括号包裹:

    (function () {
   
      ...
    })();

由于这些文章依赖于前面的章节,为了完全理解这部分内容,请先阅读前几篇文章(变量对象和作用域链),因为我们将使用这些文章中的术语

但是一口吃不成胖子,我们一个接一个来讲这些知识点,首先我们从函数类型开始

函数类型

在ECMAScript中,有三种函数类型并且他们都有自身的特性

1、函数声明

函数声明(Function Declaration, FD)是这样一个函数:

  1. 一个必须的名字;
  2. 它所处在源代码中的位置:要么在程序级别,要么在另一个函数体中(函数体FunctionBody);
  3. 在进入上下文时期被创建;
  4. 影响变量对象;
  5. 并且以如下方式声明:

示例:

  function exampleFunc() {
   
        ...
      }

这种类型的函数最主要的特性是只有他们影响变量对象(他们存储在上下文的变量对象中)。这个特性决定了第二个重要的点(变量对象性质的结果)——在代码执行阶段他们是可变的(因为函数声明FD在进入上下文阶段存储在变量对象VO中——在执行期开始之前)

例如(在函数声明源代码之前的位置调用函数):

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

同样重要的是函数在源代码中定义的位置(参见函数声明定义的第二项):

    // 函数声明
    // 1) 直接在全局上下文中
    function globalFD() {
   
      // 2) 或在其他函数体内
      function innerFD() {
   }
    }

代码中只有这两个地方可以声明函数(不可能在函数表达式中或者在一个代码块中)。

函数的声明(这里函数的声明(function declarations)不等同于函数声明(FD))还有一种方式叫做函数表达式,我们接下来会涉及这个。

2、函数表达式:

函数表达式(Function Expression,FE),是这样的函数:

  1. 在源代码中只能被定义在表达式的位置;
  2. 可以有一个可选的名字(可以有名字,也可以没有);
  3. 它的定义不影响变量对象;
  4. 在代码执行阶段被创建

这种类型函数的主要特性是在源代码中他们总是在表达式位置,这儿有个简单的例子,比如赋值表达式:

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

这个例子展示了匿名函数表达式(FE)赋值给变量foo,之后函数可以通过foo名称来获取到——foo()。

在定义阶段这种类型的函数可以拥有一个可选的名字:

    var foo = function _foo() {
   
      ...
    };

在这里一个需要提的重点是,从函数表达式(FE)的外部可以通过变量foo来获得到—foo(),当在函数内部中可以通过_foo名称来获取(比如,在回调中)。

当一个函数表达式赋给一个名称它很难被与函数声明(FD)区分开来,但是,如果你知道定义的话,很容易说出他们的区别:函数表达式总是在表达式的位置,在下面的例子中我们可以看到多种ECMASCript表达式,这种情况下的函数是函数表达式(FE):

    // 在括号中(这里应该叫做分组操作符)只能是一个表达式
    (function foo() {
   });
     
    // 数组初始化中只能是表达式
    [function bar() {
   }];
     
    // 逗号操作符情况也是表达式
    1, function baz() {
   }; 

这种定义方式还表明函数表达式FE在代码执行阶段被创建并且不存在变量对象中。让我们在一个例子中来看一看这种行为:

    // 函数表达式在定义之前不可获得,因为它在代码执行阶段被创建
      
    console.log(foo); // "foo" is not defined
      
    (function foo() {
   });
      
    // 之后也不存在,因为它不在变量对象中
      
    console.log(foo);  // "foo" is not defined

现在有个逻辑性的问题是我们为什么需要这种类型的函数呢?答案很明显—在表达式中使用它们并且不“污染”变量对象,可以通过将一个函数作为参数传递给另外一个函数来演示这个:

    function foo(callback) 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值