为什么Airbnb风格指南表示不鼓励依赖功能名称推断?

时间:2016-05-18 01:56:05

标签: javascript reactjs ecmascript-6 eslint airbnb

// bad
class Listing extends React.Component {
  render() {
    return <div>{this.props.hello}</div>;
  }
}

// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
  <div>{hello}</div>
);

// good
function Listing({ hello }) {
  return <div>{hello}</div>;
}

这取自Airbnb反应风格指南。有人可以解释为什么“不鼓励依赖功能名称推断”?这只是一种风格问题吗?

4 个答案:

答案 0 :(得分:26)

我认为这也可能与您可能遇到的意外行为有关,这些行为可能会隐含地将词汇名称赋予您可能期望的匿名函数。

例如,有人理解箭头功能:

(x) => x+2;

使常规函数等效:

function(x) {
  return x+2;
}

期待这段代码非常容易:

let foo = (x) => x+2;

然后相当于:

let foo = function(x) {
  return x+2;
}

函数保持匿名状态,并且无法引用自身来执行递归等操作。

如果那时候,在我们幸福的无知中,我们发生了类似的事情:

let foo = (x) => (x<2) ? foo(2) : "foo(1)? I should be a reference error";
console.log(foo(1));

它会成功运行,因为该功能显然不是匿名的:

let foo = function foo(x) {
  return (x<2) ? foo(2) : "foo(1)? I should be a reference error";
}  

在Babel隐式地为匿名函数添加名称的其他情况下,这可能会加剧这种情况(我认为这实际上是支持隐式函数名称的副作用,尽管我可能是错误的),他们正确处理任何边缘情况并抛出你期望的参考错误。

例如:

let foo = {
  bar: function() {}
} 

// Will surprisingly transpile to..

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


// But doing something like:

var foo = {
  bar: function(x) {
    return (x<2) ? bar(2) : 'Whats happening!?';
  }
}

console.log(foo.bar(1));

// Will correctly cause a ReferenceError: bar is not defined

您可以查看&#39;查看已编译的&#39;在这个快速DEMO上看看Babel实际上是如何实现这一点来维护匿名函数的行为。

简而言之,明确了解您正在做的事情通常是一个好主意,因为您确切地知道您的代码会发生什么。不鼓励使用隐式函数命名可能是支持这一点的风格选择,同时也保持简洁明了。

可能还在吊装。但是,嘿,有趣的一趟。

答案 1 :(得分:21)

编辑#2:在Javascript style guide

中找到了AirBnbs的原因
  

不要忘记命名表达式 - 匿名函数可以使错误的调用堆栈中找到问题变得更加困难(Discussion

以下原始答案

MDN对 function name inference 的工作方式有很好的规定,包括两个警告:

观察

以下两种情况中存在非标准<function>.name推理行为:

  1. 使用脚本解释器时
  2.   

    只有当函数没有名为name的自己的属性时,脚本解释器才会设置函数的name属性。

    1. 使用js工具时
    2.   

      使用Function.name和源代码转换时要小心,例如JavaScript压缩器(缩小器)或混淆器执行的转换

           

      ...

           

      在未压缩版本中,程序运行到truthy-branch,而日志'foo'是'Foo'的实例,而在压缩版本中,它的行为不同并且运行到else-branch。因此,如果您依赖于上面示例中的Function.name,请确保您的构建管道不会更改函数名称或不假定函数具有特定名称。

      什么是函数名称推断?

        

      name属性返回函数的名称,或者(在ES6实现之前)返回匿名函数的空字符串

      function doSomething() {}
      
      console.log(doSomething.name); // logs "doSomething"
      
        

      使用语法new Function(...)或只是Function(...)创建的函数将其name属性设置为空字符串。在以下示例中,将创建匿名函数,因此name返回空字符串

      var f = function() {};
      var object = {
        someMethod: function() {}
      };
      
      console.log(f.name == ''); // true
      console.log(object.someMethod.name == ''); // also true
      

      实现ES6功能的浏览器可以从语法位置推断出匿名函数的名称。例如:

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

      意见

      我个人更喜欢分配给变量的(箭头)函数,原因有三个:

      首先,我永远不会使用function.name

      其次,将命名函数的词法范围与赋值混合感觉有点松散:

      // This...
      function Blah() {
         //...
      }
      Blah.propTypes = {
       thing: PropTypes.string
      }
      // ...is the same as...
      Blah.propTypes = {
       thing: PropTypes.string
      }
      function Blah() {
         //...
      }
      
      // ALTERNATIVELY, here lexical-order is enforced
      const Blah = () => {
         //...
      }
      Blah.propTypes = {
          thing: PropTypes.string
      }
      

      第三,在所有条件相同的情况下,我更喜欢箭头功能:

      • 告知读者没有this,没有arguments
      • 看起来更好(imho)
      • 表现(我上次看,箭头功能略快)

      编辑:内存快照

      我正在听一个Podcast并且客人告诉他一个情况是他必须处理使用箭头功能和内存分析的限制,我一直在完全相同的情况之前。

      目前,内存快照不包含变量名 - 因此您可能会发现自己将箭头函数转换为命名函数只是为了连接内存分析器。我的经历很简单,我对箭头功能感到满意。

      另外,我只使用过一次内存快照,所以我觉得在默认情况下(主观)清晰度可以放弃一些“指导”。

答案 2 :(得分:4)

这是因为:

const Listing = ({ hello }) => (
  <div>{hello}</div>
);

有一个列表的推断名称,虽然看起来你正在命名它,但你实际上并不是:

Example

// we know the first three ways already...

let func1 = function () {};
console.log(func1.name); // func1

const func2 = function () {};
console.log(func2.name); // func2

var func3 = function () {};
console.log(func3.name); // func3

这是怎么回事?

const bar = function baz() {
    console.log(bar.name); // baz
    console.log(baz.name); // baz
};

function qux() {
  console.log(qux.name); // qux
}

答案 3 :(得分:1)

与其他任何风格指南一样,Airbnb的观点是自以为是的,并非总是合理的。

Function name property不能用于客户端应用程序中的调试,因为在缩小过程中会丢失函数的原始名称。至于调试,如果函数在调用堆栈中没有有意义的名称,效率会降低,因此在某些情况下保留它很有好处。

一个函数同时具有namefunction Foo = () => {}中的箭头之类的函数定义,而获得const Foo = () => {}。这样会导致Foo函数具有给定名称Foo.name === 'Foo'

某些编译器遵循规范。 Babel将此代码转换为ES5:

var Foo = function Foo() {};

TypeScript违反了规范:

var Foo = function () {};

这并不意味着命名函数表达式不好,因此不建议使用。只要编译器符合规范或功能名称无关紧要,就可以忽略此问题。

该问题适用于已编译的应用程序。它取决于使用的编译器以及保持功能name属性的必要性。该问题在本机ES6中不存在。