javascript - Är 'Arrow Functions' och 'Functions' ekvivalenta/utbytbara?
Arrow-funktioner i ES2015 ger en mer koncis syntax.
- Kan jag ersätta alla mina funktionsdeklarationer/uttryck med pilfunktioner nu?
- Vad måste jag se upp för?
Exempel:
Konstruktörfunktion
function User(name) {
this.name = name;
}
// vs
const User = name => {
this.name = name;
};
Prototypmetoder
User.prototype.getName = function() {
return this.name;
};
// vs
User.prototype.getName = () => this.name;
Objekt (bokstavliga) metoder
const obj = {
getName: function() {
// ...
}
};
// vs
const obj = {
getName: () => {
// ...
}
};
Återuppringningar
setTimeout(function() {
// ...
}, 500);
// vs
setTimeout(() => {
// ...
}, 500);
Variadiska funktioner
function sum() {
let args = [].slice.call(arguments);
// ...
}
// vs
const sum = (...args) => {
// ...
};
Svar #1
tl;dr: Nej! Arrow-funktioner och funktionsdeklarationer/-uttryck är inte ekvivalenta och kan inte ersättas blint.
Om funktionen du vill ersätta inte använder this, arguments och inte anropas med new, då ja.
Som så ofta: det beror på. Pilfunktioner har ett annat beteende än funktionsdeklarationer/uttryck, så låt oss först titta på skillnaderna:
1. Lexikalt this och arguments
Arrow-funktioner har inte sin egen this- eller arguments-bindning. Istället löses dessa identifierare i det lexikala omfånget som vilken annan variabel som helst. Det betyder att inuti en arrow-funktion refererar this och arguments till värdena av this och arguments i den miljö där arrow-funktionen är definierad (d.v.s. "utanför" arrow-funktionen):
// Example using a function expression
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: function() {
console.log('Inside `bar`:', this.foo);
},
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject// Example using a arrow function
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: () => console.log('Inside `bar`:', this.foo),
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObjectI fallet med funktionsuttryck refererar this till objektet som skapades inuti createObject. I fallet med pilfunktion refererar this till this för createObject själv.
Detta gör pilfunktioner användbara om du behöver komma åt this i den aktuella miljön:
// currently common pattern
var that = this;
getData(function(data) {
that.data = data;
});
// better alternative with arrow functions
getData(data => {
this.data = data;
});
Observera att detta också innebär att det inte är möjligt att sätta en pilfunktion's this med .bind eller .call.
Om du inte är mycket bekant med this, överväg att läsa
- MDN - det här
- YDKJS - detta & objektprototyper
2. Pilfunktioner kan inte anropas med new
ES2015 skiljer mellan funktioner som är anropbara och funktioner som är konstruerbara. Om en funktion är konstruerbar kan den anropas med new, t.ex. new User(). Om en funktion är anropbar kan den anropas utan new (dvs. normalt funktionsanrop).
Funktioner som skapas genom funktionsdeklarationer/uttryck är både konstruerbara och anropbara.
Pilfunktioner (och metoder) är endast anropbara. class-konstruktörer är endast konstruerbara.
Om du försöker anropa en icke-anropbar funktion eller konstruera en icke-konstruerbar funktion kommer du att få ett körfel.
Med detta vetskap kan vi säga följande.
Utbytbar:
- Funktioner som inte använder
thisellerarguments. - Funktioner som används med
.bind(this)
Inte utbytbar:
- Konstruktörsfunktioner
- Funktioner/metoder tillagda på ett prototyp (eftersom de vanligtvis använder
this) - Variadiska funktioner (om de använder
arguments(se nedan)) - Generatorfunktioner, som kräver
function*-notationen
Låt oss ta en närmare titt på detta med hjälp av dina exempel:
Konstruktörsfunktion
Det här kommer inte att fungera eftersom pilfunktioner inte kan anropas med new. Fortsätt använda en funktionsdeklaration/uttryck eller använd class.
Prototypmetoder
Troligtvis inte, eftersom prototypmetoder vanligtvis använder this för att komma åt instansen. Om de inte använder this, då kan du ersätta det. Men om du främst bryr dig om kortfattad syntax, använd class med dess kortfattade metoddsyntax:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
Objektmetoder
På samma sätt för metoder i ett objektsliteral. Om metoden vill referera till objektet självt via this, fortsätt använda funktionsuttryck, eller använd den nya metodsynaxen:
const obj = {
getName() {
// ...
},
};
Återuppringningar
Det beror på. Du bör definitivt ersätta det om du aliaserar den yttre this eller använder .bind(this):
// old
setTimeout(function() {
// ...
}.bind(this), 500);
// new
setTimeout(() => {
// ...
}, 500);
Men: Om koden som anropar callbacken uttryckligen sätter this till ett specifikt värde, vilket ofta är fallet med eventhanterare, särskilt med jQuery, och callbacken använder this (eller arguments), kan du inte använda en arrow-funktion!
Variadiska funktioner
Eftersom pilfunktioner inte har sina egna arguments, kan du inte helt enkelt ersätta dem med en pilfunktion. Däremot introducerar ES2015 ett alternativ till att använda arguments: restparametern.
// old
function sum() {
let args = [].slice.call(arguments);
// ...
}
// new
const sum = (...args) => {
// ...
};
Relaterad fråga:
- När ska jag använda pilfunktioner i ECMAScript 6?
- Har ES6-pilfunktioner sina egna arguments eller inte?
- Vilka är skillnaderna (om några) mellan ES6-pilfunktioner och funktioner bundna med Function.prototype.bind?
- Hur man använder pilfunktioner (offentliga klassfält) som klassmetoder?
Ytterligare resurser:
- MDN - Pilarfunktioner
- YDKJS - Pilfunktioner
Svar #2
Arrow-funktioner => bästa ES6-funktionen hittills. De är en otroligt kraftfull tillägg till ES6, som jag använder konstant.
Vänta, du kan inte använda pilfunktion överallt i din kod, det kommer inte att fungera i alla situationer som this där pilfunktioner inte kan användas. Utan tvekan är pilfunktionen ett fantastiskt tillskott som ger kodens enkelhet.
Men du kan inte använda en pilfunktion när ett dynamiskt sammanhang krävs: definiera metoder, skapa objekt med konstruktörer, få målet från this när du hanterar händelser.
Funktioner med pil ska INTE användas eftersom:
De har inte
thisDet använder "lexikal skopning" för att lista ut vad värdet av "
this" ska vara. Med enkla ord använder lexikal skopning "this" från insidan av funktionens kropp.De har inte
argumentPilfunktioner har inget
arguments-objekt. Men samma funktionalitet kan uppnås med hjälp av restparametrar.
let sum = (...args) => args.reduce((x, y) => x + y, 0);
sum(3, 3, 1) // output: 7
De kan inte användas med
newArrow-funktioner kan inte vara konstruktörer eftersom de inte har en prototype-egenskap.
När man ska använda pilfunktion och när man inte ska:
- Använd inte att lägga till en funktion som en egenskap i ett objektliteral eftersom vi inte kan komma åt detta.
- Funktionsuttryck är bäst för objektmetoder. Pilfunktioner är bäst för callback-funktioner eller metoder som
map,reduceellerforEach. - Använd funktionsdeklarationer för funktioner som du skulle kalla vid namn (eftersom de är hoistade).
- Använd pilfunktioner för callbacks (eftersom de tenderar att vara mer kortfattade).
