shorthand to make anonymous functions.
Since ES6 arrow functions are shorthand for anonymous functions, you can substitute arrow functions anywhere you create an anonymous function.
shorthand anonymous functionWell, it turns out that we’re not just substituting function with =>. An arrow function’s syntax can change depending on two factors:
-The number of arguments required
-Whether you’d like an implicit return.
一个参数可省略();简洁函数体implicit return,块函数体不能省略
arrow function vs function
Arrow function expressions
An arrow function expression is a compact alternative to a traditional function expression, with some semantic differences and deliberate limitations in usage:
-Arrow functions don't have their own bindings to this, arguments, or super, and should not be used as methods.
-Arrow functions cannot be used as constructors. Calling them with new throws a TypeError. They also don't have access to the new.target keyword. They also do not have a prototype property.
const Foo = () => {};
const foo = new Foo(); // TypeError: Foo is not a constructor
console.log("prototype" in Foo); // false
-Arrow functions cannot use yield within their body and cannot be created as generator functions.
Rest parameters, default parameters, and destructuring within params are supported, and always require parentheses:
(a, b, ...r) => expression
(a = 400, b = 20, c) => expression
([a, b] = [10, 20]) => expression
({ a, b } = { a: 10, b: 20 }) => expression
// Traditional anonymous function
(function (a, b) {
const chuck = 42;
return a + b + chuck;
});
// Arrow function
(a, b) => {
const chuck = 42;
return a + b + chuck;
};
Arrow functions are always unnamed. If the arrow function needs to call itself, use a named function expression instead. You can also assign the arrow function to a variable so it has a name.
// Traditional Function
function bob(a) {
return a + 100;
}
// Arrow Function
const bob2 = (a) => a + 100;
Arrow function expressions should only be used for non-method functions because they do not have their own this. Let's see what happens when we try to use them as methods:
"use strict";
const obj = {
i: 10,
b: () => console.log(this.i, this),
c() {
console.log(this.i, this);
},
};
obj.b(); // logs undefined, Window { /* … */ } (or the global object)
obj.c(); // logs 10, Object { /* … */ }
Another example involving Object.defineProperty():
"use strict";
const obj = {
a: 10,
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this); // undefined 'undefined' Window { /* … */ } (or the global object)
return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
},
});
lexical this
As you can see in the above situations, the value of this is set by the function that calls it. Every function defines it’s own this value.
In fat arrow functions, this never gets bound to a new value, no matter how the function is called. this will always be the same this value as its surrounding code. (By the way, lexical means relating to, which I guess, is how the lexical this got its name).
First, you never want to use arrow functions to declare object methods, because you can’t reference the object with this anymore.
never use arrow functions to declare object methodsLexical Scoping just means that it uses this from the code that contains the Arrow Function.
Arrow function's this just belongs to outer context.
So, if your arrow functions will be declared inside of correct object, this will be correct(almost) too.
Because a class's body has a this context, arrow functions as class fields close over the class's this context, and the this inside the arrow function's body will correctly point to the instance (or the class itself, for static fields). However, because it is a closure, not the function's own binding, the value of this will not change based on the execution context.
class C {
a = 1;
autoBoundMethod = () => {
console.log(this.a);
};
}
const c = new C();
c.autoBoundMethod(); // 1
const { autoBoundMethod } = c;
autoBoundMethod(); // 1
// If it were a normal method, it should be undefined in this case
Arrow function properties are often said to be "auto-bound methods", because the equivalent with normal methods is:
class C {
a = 1;
constructor() {
this.method = this.method.bind(this);
}
method() {
console.log(this.a);
}
}
Note: Class fields are defined on the instance, not on the prototype, so every instance creation would create a new function reference and allocate a new closure, potentially leading to more memory usage than a normal unbound method.
For similar reasons, the call(), apply(), and bind() methods are not useful when called on arrow functions, because arrow functions establish this based on the scope the arrow function is defined within, and the this value does not change based on how the function is invoked.
The call(), apply(), and bind() methods work as expected with traditional functions, because we establish the scope for each of the methods:
const obj = {
num: 100,
};
// Setting "num" on globalThis to show how it is NOT used.
globalThis.num = 42;
// A simple traditional function to operate on "this"
const add = function (a, b, c) {
return this.num + a + b + c;
};
console.log(add.call(obj, 1, 2, 3)); // 106
console.log(add.apply(obj, [1, 2, 3])); // 106
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3)); // 106
With arrow functions, since our add function is essentially created on the globalThis (global) scope, it will assume this is the globalThis.
const obj = {
num: 100,
};
// Setting "num" on globalThis to show how it gets picked up.
globalThis.num = 42;
// Arrow function
const add = (a, b, c) => this.num + a + b + c;
console.log(add.call(obj, 1, 2, 3)); // 48
console.log(add.apply(obj, [1, 2, 3])); // 48
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3)); // 48
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Perhaps the greatest benefit of using arrow functions is with methods like setTimeout() and EventTarget.prototype.addEventListener() that usually require some kind of closure, call(), apply(), or bind() to ensure that the function is executed in the proper scope.
With traditional function expressions, code like this does not work as expected:
const obj = {
count: 10,
doSomethingLater() {
setTimeout(function () {
// the function executes on the window scope
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater(); // logs "NaN", because the property "count" is not in the window scope.
With arrow functions, the this scope is more easily preserved:
const obj = {
count: 10,
doSomethingLater() {
// The method syntax binds "this" to the "obj" context.
setTimeout(() => {
// Since the arrow function doesn't have its own binding and
// setTimeout (as a function call) doesn't create a binding
// itself, the "obj" context of the outer method is used.
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater(); // logs 11
Second, you may not want to use arrow functions to create event listeners because this no longer binds to the element you attached your event listener to.
However, you can always get the right this context with event.currentTarget. Which is why I said may not.
arrow function in event listenersThird, you may want to use the lexical this in places where the this binding changes without you wanting it to. An example is the timeout function, so you never have to deal with the this, that or self nonsense.
auto-bound in setTimeoutThis use case is particularly helpful if you needed to add or remove a class after some time has elapsed:
example in mongoose:
// define a schema
var animalSchema =new Schema({ name:String, type:String});
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function(cb){
return this.model('Animal').find({ type:this.type }, cb);
};
The example above uses the Schema.methods object directly to save an instance method. You can also use the Schema.method() helper as described here.
Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document and the above examples will not work.
There are two things worth mentioning with regard to adding function functionality to your models. Both the static and method functions on a model must be a) defined before the model is created (before you call “mongoose.model”) and b) declared using the function keyword, not arrow function notation. The reason behind this can be a little confusing, but it has to do with the keyword “this.”
Functions declared using the keyword “function” bind the keyword “this” to whatever calls the function. The functions you are attaching to the model aren’t attached to the instances that the model creates, they stay attached to the model itself, like when you define a class and then create a new object with that class (see last week’s post). The child object does not contain the function, but it has access to the function through the prototype property on the parent class, and the functions stored on the “methods” property of a Mongoose model work similarly to functions stored on the prototype property for classes. And since “this” is defined by whatever called the function, rather than pointing to “Hero” where the function is stored, it will point to the specific hero— Brizzt, Karagorn, or Dianne— that called the function.
Arrow notation on the other hand does not bind a “this”, and works more like lexical scoping does with variables. When a method is being added to a parent object and then is called by the child object, the arrow function’s “this” will point to the global object and return “undefined” if you refer to a method or property that exists in the Hero model, but not on the global object.
So now I have a basic hero model established, and I’ve at least tested how adding functions to the model and the instances of the model (statics and methods respectively) works and I’m ready to create more as the project moves forward. From here I will need to create Schemas for each class type (rather than the simple string currently on the hero schema) which will be nested inside of the Hero model, and will then need a way to connect each hero to the user who created it. All (or at least some) of that process will be the subject of next week’s post.
What About this?
The handling of this is also different in arrow functions compared to regular functions.
In short, with arrow functions there are no binding of this.
In regular functions the this keyword represented the object that called the function, which could be the window, the document, a button or whatever.
With arrow functions, the this keyword always represents the object that defined the arrow function.
Let us take a look at two examples to understand the difference.
Both examples call a method twice, first when the page loads, and once again when the user clicks a button.
The first example uses a regular function, and the second example uses an arrow function.
The result shows that the first example returns two different objects (window and button), and the second example returns the Header object twice.
网友评论