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

xia*_*406 46 javascript ecmascript-6 reactjs eslint airbnb-js-styleguide

// 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>;
}
Run Code Online (Sandbox Code Playgroud)

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

Bra*_*rst 27

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

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

(x) => x+2;
Run Code Online (Sandbox Code Playgroud)

要使常规函数等效:

function(x) {
  return x+2;
}
Run Code Online (Sandbox Code Playgroud)

期望这段代码很容易:

let foo = (x) => x+2;
Run Code Online (Sandbox Code Playgroud)

那么相当于:

let foo = function(x) {
  return x+2;
}
Run Code Online (Sandbox Code Playgroud)

函数仍然是匿名的,并且无法引用自身来执行递归等操作.

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

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

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

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

在其他情况下,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
Run Code Online (Sandbox Code Playgroud)

您可以在这个快速演示中查看"查看已编译" 以查看Babel实际上是如何进行转换以维护匿名函数的行为.


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

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


Ash*_*man 23

编辑#2:在他们的Javascript风格指南中找到了AirBnbs的原因

不要忘记命名表达式 - 匿名函数可以使错误调用堆栈中的问题更难找到(讨论)

原答案如下

MDN对函数名称推断的工作方式有很好的规定,包括两个警告:

意见

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

  1. 使用脚本解释器时

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

  1. 使用js工具时

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

....

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

什么是函数名称推断?

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

function doSomething() {}

console.log(doSomething.name); // logs "doSomething"
Run Code Online (Sandbox Code Playgroud)

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

var f = function() {};
var object = {
  someMethod: function() {}
};

console.log(f.name == ''); // true
console.log(object.someMethod.name == ''); // also true
Run Code Online (Sandbox Code Playgroud)

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

var f = function() {};
console.log(f.name); // "f"
Run Code Online (Sandbox Code Playgroud)

意见

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

首先,我从来没有使用过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
}
Run Code Online (Sandbox Code Playgroud)

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

  • 向读者传达没有this,没有arguments等等
  • 看起来更好(imho)
  • 性能(我上次看,箭头功能略快)

编辑:内存快照

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

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

另外,我只使用过一次内存快照,因此我觉得在默认情况下(主观)清晰度可以放弃一些"指示".


Est*_*ask 6

与任何其他风格指南一样,Airbnb 的风格指南固执己见,而且并不总是有充分理由。

除了在客户端应用程序中进行调试之外,函数name属性不应用于任何其他用途,因为函数原始名称在缩小过程中会丢失。至于调试,如果函数在调用堆栈中没有有意义的名称,那么效率就会降低,因此在某些情况下保留它是有益的。

函数name同时具有函数定义(如 )function Foo = () => {}和函数命名表达式(如 中的箭头)const Foo = () => {}。这会导致Foo函数具有给定名称Foo.name === 'Foo'

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

var Foo = function Foo() {};
Run Code Online (Sandbox Code Playgroud)

TypeScript 违反了规范:

var Foo = function () {};
Run Code Online (Sandbox Code Playgroud)

这并不意味着命名函数表达式不好并且应该被阻止。只要转译器符合规范或者函数名称并不重要,就可以忽略这个问题。

该问题适用于转译的应用程序。这取决于使用的转译器以及保留函数name属性的必要性。原生 ES6 中不存在这个问题。