// 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反应风格指南。有人可以解释为什么“不鼓励依赖功能名称推断”?这只是一种风格问题吗?
我认为这也可能与您可能遇到的意外行为有关,这些行为可能会隐含地将词汇名称赋予您可能期望的匿名函数。
例如,有人理解箭头功能:
(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
你可以在这个快速的DEMO上查看'查看已编译'以查看Babel实际上是如何进行转换来维护匿名函数的行为。
简而言之,明确你正在做的事情通常是一个好主意,因为你确切地知道你的代码会有什么期望。不鼓励使用隐式函数命名可能是支持这一点的风格选择,同时也保持简洁明了。
可能还在吊装。但是,嘿,有趣的一趟。
不要忘记命名表达式 - 匿名函数可以使错误的调用堆栈中更难找到问题(Discussion)
MDN对function name inference如何运作有很好的破坏,包括两个警告:
在以下两种情况下存在非标准的<function>.name
推理行为:
仅当函数没有名为name的自己的属性时,脚本解释器才会设置函数的name属性。
使用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
等我正在听Podcast和客人讲述他必须处理使用箭头功能和内存分析的局限性,我之前一直处于完全相同的情况。
目前,内存快照不包含变量名称 - 因此您可能会发现自己将箭头函数转换为命名函数只是为了连接内存分析器。我的经历非常简单,我对箭头功能感到满意。
另外,我只使用过一次内存快照,因此我觉得在默认情况下(主观)清晰度可以放弃一些“仪器”。
这是因为:
const Listing = ({ hello }) => (
<div>{hello}</div>
);
有一个推断的列表名称,虽然它看起来像你命名它,你实际上不是:
// 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
}
与任何其他风格指南一样,Airbnb是自以为是,并不总是很有道理。
Function name
property不应该用于客户端应用程序中的任何调试,因为函数原始名称在缩小时会丢失。至于调试,如果函数在调用堆栈中没有有意义的名称,则效率会降低,因此在某些情况下保留它是有益的。
函数获取name
,其功能定义如function Foo = () => {}
和函数名称表达式,如const Foo = () => {}
中的箭头。这导致Foo
函数具有给定名称Foo.name === 'Foo'
。
一些转发器遵循规范。 Babel将此代码转换为ES5:
var Foo = function Foo() {};
TypeScript打破了规范:
var Foo = function () {};
这并不意味着命名函数表达式是坏的,应该不鼓励。只要转换器符合规范或功能名称无关紧要,就可以放弃这种担忧。
该问题适用于转换后的应用程序。它取决于使用的转换器和保持功能name
属性的必要性。本机ES6中不存在此问题。