[英]Why does the Airbnb style guide say that relying on function name inference is discouraged?
// 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
您可以在這個快速演示中查看“查看已編譯”以查看Babel實際上是如何進行轉換以維護匿名函數的行為。
簡而言之,明確你正在做的事情通常是一個好主意,因為你確切地知道你的代碼會有什么期望。 不鼓勵使用隱式函數命名可能是支持這一點的風格選擇,同時也保持簡潔明了。
可能還在吊裝。 但是,嘿,有趣的一趟。
不要忘記命名表達式 - 匿名函數可以使錯誤調用堆棧中的問題更難找到( 討論 )
MDN對函數名稱推斷的工作方式有很好的規定,包括兩個警告:
在以下兩種情況中存在非標准的<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
等 我正在收聽一個播客 ,客人告訴他一個情況是他必須處理使用箭頭功能和內存分析的限制,我之前一直處於完全相同的情況。
目前,內存快照不包含變量名稱 - 因此您可能會發現自己將箭頭函數轉換為命名函數只是為了連接內存分析器。 我的經歷非常簡單,我對箭頭功能感到滿意。
另外,我只使用了一次內存快照,因此我覺得在默認情況下(主觀)清晰度可以放棄一些“指示”。
這是因為:
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是自以為是,並不總是很有道理。
函數name
屬性不應該用於客戶端應用程序中的任何調試,因為函數原始名稱在縮小期間會丟失。 至於調試,如果函數在調用堆棧中沒有有意義的名稱,則效率會降低,因此在某些情況下保留它是有益的。
函數獲取name
,其函數定義類似function Foo = () => {}
,函數名稱表達式類似於const Foo = () => {}
的箭頭。 這導致Foo
函數具有給定名稱, Foo.name === 'Foo'
。
一些轉發器遵循規范。 Babel將此代碼轉換為ES5:
var Foo = function Foo() {};
TypeScript打破了規范:
var Foo = function () {};
這並不意味着命名函數表達式是壞的,應該不鼓勵。 只要轉換器符合規范或功能名稱無關緊要,就可以放棄這種擔憂。
該問題適用於轉換后的應用程序。 它取決於使用中的轉換器以及保持函數name
屬性的必要性。 本機ES6中不存在此問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.