lys*_*ing 379 javascript lambda ecmascript-harmony ecmascript-6 arrow-functions
这个问题针对的是那些在即将到来的ECMAScript 6(Harmony)背景下已经考虑过代码风格并且已经使用过该语言的人.
有了() => {}和function () {}我们有两种非常相似的方法来编写ES6中的函数.在其他语言中,lambda函数通常通过匿名来区分自己,但在ECMAScript中,任何函数都可以是匿名的.这两种类型中的每一种都具有唯一的使用域(即,当this需要明确地绑定或明确地不绑定时).在这些域之间存在大量的情况,其中任何一种符号都可以.
ES6中的箭头功能至少有两个限制:
newthis在初始化时限制范围抛开这两个限制,箭头函数理论上几乎可以在任何地方替换常规函数.在实践中使用它们的正确方法是什么?是否应使用箭头功能,例如:
this变量不可知,我们不创建对象.我正在寻找的是在ECMAScript的未来版本中选择适当的函数符号的指南.该指南需要明确,以便可以向团队中的开发人员讲授,并保持一致,这样就不需要从一个函数符号到另一个函数符号进行不断的重构.
lys*_*ing 304
不久前,我们的团队将其所有代码(一个中型AngularJS应用程序)迁移到使用Traceur Babel编译的JavaScript .我现在使用以下经验法则来处理ES6及更高版本中的函数:
function全局范围和Object.prototype属性.class的对象构造.=>在其他地方使用.为什么几乎到处使用箭头功能?
thisObject与根一样使用.如果将一个标准函数回调与一堆箭头函数混合在一起,则范围可能会变得混乱.function立即伸出来定义范围.开发人员总是可以查找下一个更高的function语句来查看它thisObject是什么.为什么总是在全局范围或模块范围上使用常规函数?
thisObject.window对象(全局范围)最好是明确解决.Object.prototype定义都存在于全球范围内(想想String.prototype.truncate等等),并且通常必须是类型function的.始终function在全局范围内使用有助于避免错误.function foo(){}比const foo = () => {}-特别是外其他函数调用.(2)函数名称显示在堆栈跟踪中.虽然命名每个内部回调会很繁琐,但命名所有公共函数可能是一个好主意.
对象构造函数
尝试实例化箭头函数会引发异常:
var x = () => {};
new x(); // TypeError: x is not a constructor
Run Code Online (Sandbox Code Playgroud)
因此,函数优于箭头函数的一个关键优势是函数兼作对象构造函数:
function Person(name) {
this.name = name;
}
Run Code Online (Sandbox Code Playgroud)
但是,功能相同的2 ES Harmony 草案类定义几乎同样紧凑:
class Person {
constructor(name) {
this.name = name;
}
}
Run Code Online (Sandbox Code Playgroud)
我希望最终不鼓励使用前一种表示法.某些对象构造函数表示法仍然可以用于简单的匿名对象工厂,其中对象是以编程方式生成的,但其他情况并非如此.
如果需要对象构造函数,则应考虑将函数转换为class如上所示的函数.语法也适用于匿名函数/类.
箭头功能的可读性
坚持常规功能的可能最好的理由 - 范围安全被诅咒 - 箭头功能的可读性低于常规功能.如果您的代码首先不起作用,那么箭头函数似乎不是必需的,并且当箭头函数不一致使用时,它们看起来很难看.
ECMAScript已经发生了很大变化,因为ECMAScript 5.1为我们提供了功能Array.forEach,Array.map所有这些功能编程功能都让我们使用了以前使用for循环的函数.异步JavaScript已经取得了相当大的进展.ES6还将发布一个Promise对象,这意味着更多的匿名函数.功能编程没有回头路.在功能JavaScript中,箭头函数优于常规函数.
以这个(特别令人困惑的)代码3为例:
function CommentController(articles) {
this.comments = [];
articles.getList()
.then(articles => Promise.all(articles.map(article => article.comments.getList())))
.then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
.then(comments => {
this.comments = comments;
})
}
Run Code Online (Sandbox Code Playgroud)
具有常规功能的同一段代码:
function CommentController(articles) {
this.comments = [];
articles.getList()
.then(function (articles) {
return Promise.all(articles.map(function (article) {
return article.comments.getList();
}));
})
.then(function (commentLists) {
return commentLists.reduce(function (a, b) {
return a.concat(b);
});
})
.then(function (comments) {
this.comments = comments;
}.bind(this));
}
Run Code Online (Sandbox Code Playgroud)
虽然任何一个箭头功能都可以用标准功能代替,但这样做几乎没有什么好处.哪个版本更具可读性?我会说第一个.
我认为,随着时间的推移,使用箭头功能或常规功能的问题将变得不那么重要.大多数函数要么成为类方法,要么取消function关键字,否则它们将成为类.函数将继续用于修补类Object.prototype.与此同时,我建议function为任何真正属于类方法或类的东西保留关键字.
笔记
extend关键字即可.一个小的区别是类声明是常量,而函数声明不是.Jac*_*son 79
根据该提案,箭头旨在"解决和解决传统的几个常见痛点" Function Expression.他们打算通过this词汇绑定和提供简洁的语法来改善问题.
然而,
this词汇绑定因此,箭头函数会产生混淆和错误的机会,并且应该从JavaScript程序员的词汇表中排除,仅替换为function.
关于词汇 this
this 有问题:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Run Code Online (Sandbox Code Playgroud)
箭头函数旨在解决我们需要访问this回调内部属性的问题.有几种方法可以做到这一点:可以分配this给变量,使用bind或使用Array聚合方法上可用的第3个参数.箭头似乎是最简单的解决方法,因此该方法可以像这样重构:
this.pages.forEach(page => page.draw(this.settings));
Run Code Online (Sandbox Code Playgroud)
但是,请考虑代码是否使用了像jQuery这样的库,其方法this特别绑定.现在,有两个this值要处理:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Run Code Online (Sandbox Code Playgroud)
我们必须使用function以便动态each绑定this.我们不能在这里使用箭头功能.
处理多个this值也可能令人困惑,因为很难知道this作者在谈论哪个:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
Run Code Online (Sandbox Code Playgroud)
作者真的打算打电话Book.prototype.reformat吗?或者他忘了绑定this,打算打电话Reader.prototype.reformat?如果我们将处理程序更改为箭头函数,我们同样会想知道作者是否需要动态this,但是选择了箭头,因为它适合于一行:
function Reader() {
this.book.on('change', () => this.reformat());
}
Run Code Online (Sandbox Code Playgroud)
人们可能会提出这样的说法:"箭头有时可能是错误的使用功能吗?也许如果我们很少需要动态this值,那么大多数时候使用箭头仍然可以."
但是问问自己:"调试代码并发现错误的结果是由'边缘案例引起的'是否值得'?'"我宁愿避免麻烦,不仅在大多数时候,而且100%的时间.
有一种更好的方法:始终使用function(因此this总是可以动态绑定),并始终this通过变量引用.变量是词法并且有许多名称.分配this给变量将使您的意图明确:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Run Code Online (Sandbox Code Playgroud)
此外,始终分配this给变量(即使存在单个this或没有其他功能)也可确保即使在代码更改后,一个人的意图仍然清晰.
而且,动态this也不例外.jQuery用于超过5000万个网站(截至2016年2月撰写).以下是this动态绑定的其他API :
this.this.this.EventTarget与this.this.(统计数据来自http://trends.builtwith.com/javascript/jQuery和https://www.npmjs.com.)
您可能this已经需要动态绑定.
this有时期望词汇,但有时不会; 正如this有时候会有动力,但有时却没有.值得庆幸的是,有一种更好的方法,它始终生成并传达预期的绑定.
关于简洁的语法
Arrow函数成功地为函数提供了"更短的语法形式".但这些较短的功能会让你更成功吗?
是x => x * x"容易阅读"比function (x) { return x * x; }?也许是这样,因为它更有可能产生一个短的代码行.根据戴森的阅读速度和线长对屏幕阅读效果的影响,
中线长度(每行55个字符)似乎支持正常和快速的有效读数.这产生了最高水平的理解...
类似的理由是针对条件(三元)运算符和单行if语句.
但是,您是否真的在编写提案中公布的简单数学函数?我的域名不是数学的,所以我的子程序很少如此优雅.相反,我经常看到箭头函数打破了列限制,并由于编辑器或样式指南而换行到另一行,这使Dyson定义的"可读性"无效.
有人可能会说,"如果可能,只使用短版本的短版本怎么样?" 但是现在风格规则与语言约束相矛盾:"尽量使用最短的函数符号,记住有时只有最长的符号会this按预期绑定." 这种混淆使得箭头特别容易被误用.
箭头函数语法有很多问题:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Run Code Online (Sandbox Code Playgroud)
这两个函数在语法上都是有效的.但doSomethingElse(x);它不是在身体中b,它只是一个很难缩进的顶级声明.
当扩展到块形式时,不再是隐式的return,可能会忘记恢复.但这个表达可能只是为了产生副作用,所以谁知道return未来是否需要明确的?
const create = () => User.create();
const create = () => {
let user;
User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
const create = () => {
let user;
return User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
Run Code Online (Sandbox Code Playgroud)
可以将作为rest参数的内容解析为扩展运算符:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
Run Code Online (Sandbox Code Playgroud)
分配可能与默认参数混淆:
const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens
Run Code Online (Sandbox Code Playgroud)
块看起来像对象:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
Run Code Online (Sandbox Code Playgroud)
这是什么意思?
() => {}
Run Code Online (Sandbox Code Playgroud)
作者是否打算创建一个无操作或返回空对象的函数?(考虑到这一点,我们应该放在{后面=>吗?我们是否应该仅限于表达式语法?这会进一步降低箭头的频率.)
=>看起来像<=和>=:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Run Code Online (Sandbox Code Playgroud)
要立即调用箭头函数表达式,必须放在()外面,但放在()里面是有效的,可能是故意的.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Run Code Online (Sandbox Code Playgroud)
虽然,如果一个人写(() => doSomething()());的目的是编写一个立即调用的函数表达式,那么根本不会发生任何事情.
考虑到上述所有情况,很难说箭头功能"更容易理解".人们可以学习使用这种语法所需的所有特殊规则.是不是真的值得吗?
语法function是无异常概括的.function单独使用意味着语言本身可以防止编写令人困惑的代码.为了编写在所有情况下应该在语法上理解的过程,我选择function.
关于指南
您需要一份需要"明确"和"一致"的指南.使用箭头函数最终将导致语法有效,逻辑无效的代码,两种函数形式交织在一起,有意义且任意.因此,我提供以下内容:
function.创建程序.this给变量.不要用() => {}.Tha*_*var 36
创建箭头函数是为了简化功能scope并this通过使关键字变得更简单来解决关键字.它们使用的=>语法看起来像箭头.
注意:它不会替换现有功能.如果用箭头函数替换每个函数语法,它在所有情况下都不起作用.
让我们看看现有的ES5语法,如果this关键字位于对象的方法(属于对象的函数)中,它会引用什么?
var Actor = {
name: 'RajiniKanth',
getName: function() {
console.log(this.name);
}
};
Actor.getName();
Run Code Online (Sandbox Code Playgroud)
上面的代码片段将引用object并打印出名称"RajiniKanth".让我们探讨下面的片段,看看这里会指出什么.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
Run Code Online (Sandbox Code Playgroud)
那么如果this关键字在里面method’s function呢?
在这里,这将指window object的inner function是它的堕落scope.因为this,对于这种情况,总是引用它所在函数的所有者 - 因为它现在超出了范围 - 窗口/全局对象.
当它在一个object方法的内部时- 它function的拥有者就是对象.因此this关键字绑定到对象.然而,当它在一个函数内部时,无论是独立还是在另一个方法中,它总是会引用该window/global对象.
var fn = function(){
alert(this);
}
fn(); // [object Window]
Run Code Online (Sandbox Code Playgroud)
有很多方法可以解决我们ES5自己的这个问题,让我们在深入了解ES6箭头函数之前先了解一下如何解决它.
通常你会在方法的内部函数之外创建一个变量.现在的‘forEach’方法获得访问this,因此object’s属性和它们的值.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
var _this = this;
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
Run Code Online (Sandbox Code Playgroud)
使用bind附加的this引用该方法的关键字method’s inner function.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
}).bind(this);
}
};
Actor.showMovies();
Run Code Online (Sandbox Code Playgroud)
现在有了ES6箭头功能,我们可以用lexical scoping更简单的方式处理问题.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
showMovies: function() {
this.movies.forEach((movie) => {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
Run Code Online (Sandbox Code Playgroud)
Arrow functions更像函数语句,只是他们bind的这parent scope.如果arrow function is in top scope,this参数将引用window/global scope,而常规函数内的箭头函数将使其this参数与其外部函数相同.
随着arrow功能this势必封闭scope在创建时,不能更改.新的运算符,绑定,调用和应用对此没有影响.
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`
asyncFunction(o, function (param) {
// We made a mistake of thinking `this` is
// the instance of `o`.
console.log('param === this?', param === this);
});
}
};
o.doSomething(); // param === this? false
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我们失去了对此的控制.我们可以通过使用this或使用变量引用来解决上面的例子bind.使用ES6,管理this它的界限变得更加容易lexical scoping.
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`.
//
// Because this arrow function is created within
// the scope of `doSomething` it is bound to this
// lexical scope.
asyncFunction(o, (param) => {
console.log('param === this?', param === this);
});
}
};
o.doSomething(); // param === this? true
Run Code Online (Sandbox Code Playgroud)
在对象文字内.
var Actor = {
name: 'RajiniKanth',
movies: ['Kabali', 'Sivaji', 'Baba'],
getName: () => {
alert(this.name);
}
};
Actor.getName();
Run Code Online (Sandbox Code Playgroud)
Actor.getName与箭头函数来定义,但在调用它,因为警报未定义this.name是undefined作为上下文仍然window.
之所以会发生这种情况,是因为箭头函数用词汇方式将上下文与window object...即外部范围绑定在一起.执行this.name等同于window.name,未定义.
对象原型
在a上定义方法时适用相同的规则prototype object.而不是使用箭头函数来定义sayCatName方法,这会带来不正确的context window:
function Actor(name) {
this.name = name;
}
Actor.prototype.getName = () => {
console.log(this === window); // => true
return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined
Run Code Online (Sandbox Code Playgroud)
调用构造函数
this在构造调用中是新创建的对象.当执行新的Fn()时,它的上下文constructor Fn是一个新对象:this instanceof Fn === true.
this 是从封闭上下文设置的,即外部作用域,它不会将其分配给新创建的对象.
var Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');
Run Code Online (Sandbox Code Playgroud)
使用动态上下文进行回调
箭头函数context在声明上静态绑定,不可能使其动态化.将事件侦听器附加到DOM元素是客户端编程中的常见任务.事件触发处理程序函数,并将此作为目标元素.
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
Run Code Online (Sandbox Code Playgroud)
this是在全局上下文中定义的箭头函数中的窗口.当发生单击事件时,浏览器尝试使用按钮上下文调用处理函数,但箭头函数不会更改其预定义的上下文.this.innerHTML等同于window.innerHTML并且毫无意义.
您必须应用函数表达式,该表达式允许根据目标元素更改它:
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
Run Code Online (Sandbox Code Playgroud)
当用户单击按钮时,处理程序功能中的按钮就是按钮.因此,this.innerHTML = 'Clicked button'正确修改按钮文本以反映单击状态.
参考文献:https: //rainsoft.io/when-not-to-use-arrow-functions-in-javascript/
Man*_*z90 14
箭头功能 - 迄今为止最广泛使用的ES6功能......
用法:除以下情况外,所有ES5功能都应替换为ES6箭头功能:
不应使用箭头函数:
this/ arguments时
this/ arguments他们自己的,他们依靠自己的外部环境.constructor
this.this(应该是对象本身).让我们了解箭头函数的一些变体以便更好地理解:
变体1:当我们想要将多个参数传递给函数并从中返回一些值时.
ES5版本:
var multiply = function (a,b) {
return a*b;
};
console.log(multiply(5,6)); //30
Run Code Online (Sandbox Code Playgroud)
ES6版本:
var multiplyArrow = (a,b) => a*b;
console.log(multiplyArrow(5,6)); //30
Run Code Online (Sandbox Code Playgroud)
注意:
function不需要关键字.
=>是必须的.
{}是可选的,当我们不提供{} return时,由JavaScript隐式添加,当我们提供时,{}我们需要添加,return如果我们需要它.
变体2:当我们想要将一个参数传递给一个函数并从中返回一些值时.
ES5版本:
var double = function(a) {
return a*2;
};
console.log(double(2)); //4
Run Code Online (Sandbox Code Playgroud)
ES6版本:
var doubleArrow = a => a*2;
console.log(doubleArrow(2)); //4
Run Code Online (Sandbox Code Playgroud)
注意:当只传递一个参数时,我们可以省略括号().
变体3:当我们不想将任何参数传递给函数并且不想返回任何值时.
ES5版本:
var sayHello = function() {
console.log("Hello");
};
sayHello(); //Hello
Run Code Online (Sandbox Code Playgroud)
ES6版本:
var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); //sayHelloArrow
Run Code Online (Sandbox Code Playgroud)
变体4:当我们想要从箭头函数显式返回时.
ES6版本:
var increment = x => {
return x + 1;
};
console.log(increment(1)); //2
Run Code Online (Sandbox Code Playgroud)
变体5:当我们想要从箭头函数返回一个对象时.
ES6版本:
var returnObject = () => ({a:5});
console.log(returnObject());
Run Code Online (Sandbox Code Playgroud)
注意:我们需要将对象包装在括号中,()否则JavaScript无法区分块和对象.
变体6:箭头函数没有自己的arguments(类似对象的数组)它们依赖于外部上下文arguments.
ES6版本:
function foo() {
var abc = i => arguments[0];
console.log(abc(1));
};
foo(2); // 2
Run Code Online (Sandbox Code Playgroud)
注意:
foo是ES5功能,与arguments像对象阵列和传递参数给它是2这样arguments[0]对foo为2.
abc是一个ES6箭头功能,因为它不具有它自己的arguments,因此它输出arguments[0]的foo是外部语境来代替.
变体7:箭头函数没有this它们自己的依赖于外部上下文this
ES5版本:
var obj5 = {
greet: "Hi, Welcome ",
greetUser : function(user) {
setTimeout(function(){
console.log(this.greet + ": " + user); // "this" here is undefined.
});
}
};
obj5.greetUser("Katty"); //undefined: Katty
Run Code Online (Sandbox Code Playgroud)
注意:传递给setTimeout的回调是一个ES5函数,它有自己的this,在use-strict环境中是未定义的,因此我们得到输出:
undefined: Katty
Run Code Online (Sandbox Code Playgroud)
ES6版本:
var obj6 = {
greet: "Hi, Welcome ",
greetUser : function(user) {
setTimeout(() => console.log(this.greet + ": " + user));
// this here refers to outer context
}
};
obj6.greetUser("Katty"); //Hi, Welcome: Katty
Run Code Online (Sandbox Code Playgroud)
注:传递给回调setTimeout是一个ES6箭头功能,它不具有它自己的this,因此它需要从它的外部背景下,是greetUser具有this被obj6因此我们得到的输出:
Hi, Welcome: Katty
Run Code Online (Sandbox Code Playgroud)
杂项:
我们不能使用new箭头功能.箭头功能没有prototype属性.this当通过apply或调用箭头函数时,我们没有绑定call.
我仍然支持我在这个线程的第一个答案中写的所有内容。然而,我对代码风格的看法从那时起就发展了,所以我对这个问题有了一个新的答案,这个答案建立在我上一个问题的基础上。
关于词汇 this
在我的最后一个回答中,我故意回避了我对这种语言持有的潜在信念,因为它与我提出的论点没有直接关系。尽管如此,如果没有明确说明这一点,我可以理解为什么许多人在发现箭头如此有用时,只是拒绝我不使用箭头的建议。
我的信念是:我们不应该首先使用this。因此,如果一个人this在他的代码中刻意避免使用,那么this箭头的“词法”特征几乎没有价值。而且,在this坏事的前提下,箭的治疗this也算不上什么“好事”;相反,它更像是另一种糟糕的语言功能的损害控制形式。
我认为有些人不会发生这种情况,但即使对那些会发生这种情况的人来说,他们也必须发现自己在this每个文件出现一百次的代码库中工作,并且一点(或很多)损害控制就是全部一个通情达理的人可以希望。因此,在某种程度上,当箭头使糟糕的情况变得更好时,它们可能是好的。
即使this使用箭头编写代码比没有它们更容易,使用箭头的规则仍然非常复杂(请参阅:当前线程)。因此,正如您所要求的那样,指南既不“清晰”也不“一致”。即使程序员知道箭头的歧义,我认为他们仍然耸耸肩并接受它们,因为词法的价值使this它们黯然失色。
所有这些都是以下认识的序言:如果不使用this,那么this通常由箭头引起的歧义就变得无关紧要。在这种情况下,箭头变得更加中性。
关于简洁的语法
当我写下我的第一个答案时,我认为即使是对最佳实践的盲目遵守也是值得付出的代价,如果这意味着我可以生成更完美的代码。但我最终意识到,简洁也可以作为一种抽象形式,可以提高代码质量——足以证明有时偏离最佳实践是合理的。
换句话说:该死的,我也想要单行函数!
关于指南
考虑到this-neutral 箭头函数的可能性,以及值得追求的简洁性,我提供以下更宽松的指导方针:
this.除了到目前为止的大答案之外,我想提出一个非常不同的理由,为什么箭头函数在某种意义上比"普通"JavaScript函数更好.为了便于讨论,让我们暂时假设我们使用类型检查器,如TypeScript或Facebook的"Flow".考虑以下玩具模块,它是有效的ECMAScript 6代码加上流类型注释:(我将包含无类型代码,这将在Babab的实际结果中,在此答案的最后,因此它实际上可以运行.)
export class C {
n : number;
f1: number => number;
f2: number => number;
constructor(){
this.n = 42;
this.f1 = (x:number) => x + this.n;
this.f2 = function (x:number) { return x + this.n;};
}
}Run Code Online (Sandbox Code Playgroud)
现在看看当我们从不同的模块使用C类时会发生什么,如下所示:
let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!Run Code Online (Sandbox Code Playgroud)
正如你所看到的,类型检查器在这里失败:f2应该返回一个数字,但它返回一个字符串!
更糟糕的是,似乎没有可想象的类型检查器可以处理普通(非箭头)JavaScript函数,因为f2的"this"不会出现在f2的参数列表中,因此无法添加"this"所需的类型作为f2的注释.
这个问题是否也会影响不使用类型检查器的人?我是这么认为的,因为即使我们没有静态类型,我们也会认为它们就在那里.("第一个参数必须是一个数字,第二个参数必须是一个字符串"等.)隐藏的"this"参数可能会或可能不会在函数体内使用,这会使我们的心理记录变得更难.
这是可运行的无类型版本,由Babel生成:
class C {
constructor() {
this.n = 42;
this.f1 = x => x + this.n;
this.f2 = function (x) { return x + this.n; };
}
}
let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
80289 次 |
| 最近记录: |