ES6中的函数声明顺序是否重要?

时间:2017-11-20 21:29:50

标签: javascript ecmascript-6 redux react-redux

我正在使用Dan Abramov's Redux course videos on http://egghead.io学习Redux。

我有这个代码,工作正常:

export const todoApp = (state = {}, action) => {
    return {
        todos: todos(state.todos, action),
        visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    }
}

export const todos = (state = [], action) => {
    // ...implementation
};

const visibilityFilter = (state = "SHOW_ALL", action) => {
    // ...implementation
}

然后我尝试将todoApp reducer函数替换为使用combineReducers辅助函数生成的函数:

import { combineReducers } from 'redux'

export const todoApp = combineReducers({
    todos: todos,
    visibilityFilter: visibilityFilter,
});

export const todos = (state = [], action) => {
    // ...implementation
};

const visibilityFilter = (state = "SHOW_ALL", action) => {
    // ...implementation
}

但是当我尝试运行单元测试(工作正常)时,我收到了这个错误:

ReferenceError: todos is not defined

但是,如果我将todoApp函数定义剪切并复制到文件的底部(如下面的代码中所示),那么一切都可以正常工作了!

import { combineReducers } from 'redux'

export const todos = (state = [], action) => {
    // ...implementation
};

const visibilityFilter = (state = "SHOW_ALL", action) => {
    // ...implementation
}

export const todoApp = combineReducers({
    todos: todos,
    visibilityFilter: visibilityFilter,
});

我还尝试在todoApptodos函数之间放置visibilityFilter定义。在那种情况下,我收到错误

ReferenceError: visibilityFilter is not defined

因此,如果我在文件顶部自己创建todoApp reducer函数,无论函数声明顺序如何,一切正常,但如果我使用combineReducers辅助函数,我需要做它在所有其他声明之下。

有人可以解释一下为什么会发生这种情况以及有关申报令的良好做法是什么?

2 个答案:

答案 0 :(得分:1)

这与ECMAScript的评估和初始化方式有关。在您的第一个代码段中,todoApp显示在其他功能之前:

export const todoApp = (state = {}, action) => {
  return {
    todos: todos(state.todos, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
  }
}

这不会引发ReferenceError的原因是因为在 todosvisibilityFilter定义之后评估了返回值。这在ECMAScript 2018 Specification

中有所描述
  

9.2.12 FunctionDeclarationInstantiation( func argumentsList

     
    

注1 [...]在评估函数体时初始化所有其他绑定。

  

这里,规范指出当调用FunctionDeclarationInstantiation时(在创建todoApp之类的新函数时调用它,期间评估绑定(如变量或返回值) >函数体的评估。这意味着在创建函数时函数体评估,因此不知道返回值对象包含对todos或{{的引用1}}现在还不存在。Object Initializers

也加强了这一点
  

12.2.6对象初始化程序

     
    

注1 对象初始值设定项是一个描述Object初始化的表达式,以类似于文字的形式编写。它是零个或多个属性键和关联值对的列表,用大括号括起来。价值观不一定是文字;每次评估对象初始值设定项时都会对它们进行评估。

  

最后一行提到在评估对象本身之前不会评估对象的值。

因此,由于在实际调用visibilityFilter之前不评估todoApp的返回值,并且在评估返回值之前不评估对象值,因此不会报告ReferenceError,因为todoApptodos引用未经过评估

相反,在定义visibilityFiltertodos之前评估您的第二个示例:

visibilityFilter

这里,export const todoApp = combineReducers({ todos: todos, visibilityFilter: visibilityFilter, }); 是函数调用。由于combineReducerstodos是函数表达式,因此它们不会被挂起,因此visibilityFilter调用会在它们存在之前进行评估。对象初始值设定项进行求值,因为它是combineReducers的参数,并且一旦初始化程序被计算,就会对值进行求值。对combineReducerstodos的引用会产生ReferenceError,因为它们尚不存在。

这也解释了第三个示例,因为它是有效的,因为在{/ em> visibilityFiltercombineReducers之后todos被称为。您将visibilityFilter置于todosApptodos之间的最后尝试会引发另一个ReferenceError,因为visibilityFilter将被评估并存在,但todos将不会因为它未被提升而存在。

答案 1 :(得分:-2)

是。如果您使用function关键字,则声明会“悬挂”到范围顶部,与var关键字的工作方式相同。

如果您使用constlet,那么变量实际上并不“存在”,直到它声明的行,并且高于undefined。 (技术术语是“时间死区”。)