Definition of a function

Functions are defined in four ways:

  • 1. Function declaration
  • 2. Function expression
  • 3. Arrow function
  • 4. Use Function to create objects

Note: The fourth method is not recommended; But it’s helpful to understand that a function is an object, and that the function name refers to that object;

Arrow function

New in ES6, simplified writing, is very embedded function scenarios;

You can omit the parentheses if you pass an argument.

If the return value is a line, the return statement can be omitted.

It is worth noting:

You can’t use arguments, super, and new.target. You can’t use constructors.

The function name

A function name is a pointer to a function, so a function can have multiple Pointers to it;

All functions of ESMAScript6 expose a read-only name attribute; Is the identifier of a Function, which returns an empty string even if it has no name, or “anonymous” if it was created by the Function constructor.

If the function is a fetch function, a set function, or an instantiation using bind(), the identifier is prefixed;

Such as: bound foo; get age; set age;

Understanding parameters:

You can pass in one, three, or any number of arguments, and the interpreter will not get an error. Why?

Answer: Because arguments to ECMAScript functions are internally represented as an array. A function call always accepts an array, but does not care what the array contains. If the array has nothing, no problem; If the array has more elements than required, that’s fine.

The arguments object

When you use the function keyword to define (non-arrow) functions, you can access the Arguments object inside the function and get each argument passed in from it.

You can use the arguments.lenth attribute to check the number of arguments passed in; According to the different number of functions to achieve overload;

Parameters of synchronous

Consider the following example:

function doAdd(num1, num2) {
    arguments[1] = 10;
    console.log(arguments[0] + num2);
}

doAdd(20.30) / / 30
Copy the code

It says 30. What is it?

Arguments object values are automatically synchronized to the corresponding named arguments, so changing arguments[1] also changes num2, so both arguments are 10; But that doesn’t mean they both access the same memory address, they’re separate in memory, they just stay in sync.

Here’s what it says:

This synchronization is one-way and changing the values of named arguments does not affect the corresponding values in the Arguments object;

However, after my verification, this conclusion is one-sided:

function doAdd2(num1 = 1, num2 = 2) {
    num2 = 10;
    console.log(arguments[0] + arguments[1]);
}
doAdd2(20.30)  / / 50
Copy the code

If you add default values to functions, then the book is right;

function doAdd2(num1, num2) {
    num2 = 10;
    console.log(arguments[0] + arguments[1]);
}
doAdd2(20.30)  / / 30

Copy the code

It’s still 30, it should be 50 according to the book;

function doAdd3(num1, num2) {
    arguments[1] = 10;
    console.log(num1 + num2);
}

doAdd3(20)  // NaN
Copy the code

The code above prints NaN.

If arguments[1] is passed in as a parameter and then set to a value, the second named parameter is not reflected. This is because argumenets objects are long based on the number of arguments passed in, rather than the number of named arguments given when the function is defined;

Arguments to the arrow function:

Arrow functions cannot be accessed using the arguments keyword, but only by defined named arguments.

The arrow function doesn’t have a arguments object, but you can look for one in the wrapper function and provide it to the arrow function;

function foo() {
    let bar = () = > {
        console.log(arguments);
    }
    return bar()
}
foo(8)
Copy the code
Function not overloaded

How to understand the js function is not overloaded?

ECMAScript functions have no signatures because arguments are represented by arrays containing zero or more values. No function signature, no overloading; A function defined later with the same name overwrites a function defined earlier. You can, however, manipulate the arguments object length or argument type accordingly;

The default parameters

In ES5 and before, the default argument was to determine whether the parameter was undefined. If so, you would manually give a default value. If not, you would do nothing.

ES6 now supports default arguments when defining functions;

There is a requirement that there is a function that defines two default arguments, the first is num1 = 10, the second is num2 = 20; The default value is used for the first argument, and the specified value is passed for the second argument. What should I do?

Try to 1:

function add (num1 = 10, num2 = 20) {
    return num1 + num2
}
console.log(add(num2 = 40))  / / 60
Copy the code

Instead of passing in num2=40, this expression assigns the value 40 to the first argument, num2, so it prints 60;

Try 2:

function add (num1 = 10, num2 = 20) {
    return num1 + num2
}
console.log(add(,40))  // SyntaxError: Unexpected token ','
Copy the code

Syntax errors;

The add(undefined,40) parameter is not passed, so you can skip the first parameter and pass the second parameter.

With default arguments, the values of the Arguments object do not reflect the default values of the arguments, only the arguments passed to the function.

Default parameters are not limited to the original value or object type, but can also be the value returned by the calling function;

let romanNumerals = ['I'.'II' ,'III'.'IV' , 'V']

let ordinality = 0

function getNumerals() {
    return romanNumerals[ordinality++]
}

function makeKing(name = "Henry", numerals = getNumerals()) {
    return `King ${name} ${numerals}`
}

console.log(makeKing())   // I
console.log(makeKing('lsj'.'IV')) // IV
console.log(makeKing()) // II
console.log(makeKing()) // III
console.log(makeKing()) // IV
Copy the code

As shown in the code above:

  • The function is evaluated only when the function is called, not when the function is defined.
  • The function that calculates the default value will only be called if the function is called and no arguments are passed to clear;

Arrow functions can also use default arguments. () is mandatory;

Default parameter scope with temporary dead zone

Parameters are initialized in order, so the default parameters defined later can refer to the first parameters.

For example,

function makeKing(name = 'lsj', numerals = name) {... }Copy the code

The initialization order of parameters follows the “temporary dead zone” rule, that is, those defined earlier cannot be referenced later.

Parameters also exist in their own scope. They cannot refer to the scope of the function body.

Parameter expansion and collection

Extension parameters

When passing parameters to a function, sometimes you do not need to pass an array, but rather pass the elements of the array separately. Before ES6, you needed to use the apply () method. In ES6, it was very easy to find various operations through extension operators.

Collect parameters

When defining a function, you can use the extension operator to combine separate arguments of different lengths into an array.

Note:

  • If there are named parameters in front of the collection parameter, only the remaining parameters are collected. If not, you get an empty array;
  • Because the result of collecting a parameter is variable, it can only be used as the last parameter;

Arrow functions don’t support arguments objects, but they do support the way arguments are defined, so you can implement the same logic as arguments;

Conclusion:

  • Use… inside functions. Operators, which collect parameters;
  • When passing arguments to a function call, use… Operators, which are extension arguments;

Function declarations and function expressions

The difference between:

Function declaration: Before any code is executed, the JS engine reads the function declaration and generates the function definition in the execution context;

Function expressions: Function definitions are not generated in the execution context until the code executes on its line;

It is worth noting that the function declaration is promoted. When executing the code, the JS engine will first perform a scan and promote the found function declaration to the top of the source code book. So there is a reason why a call to a function before it is declared will not report an error;

Function as value

Because function names are variables in ECMAScript, functions can be used anywhere variables can be used. This means that you can not only pass a function as an argument to another function, but also return another function within one function.

Consider the following example:

// Sort by attribute name
function createComparisonFunction(prototypeName) {
    return function (object1, object2) {
        let val1 = object1[prototypeName]
        let val2 = object2[prototypeName]

        if (val1 < val2) {
            return -1
        } else if (val1 > val2) {
            return 1
        } else {
            return 0}}}var data = [
    {
        id: 1.name: 'lsj'
    }, {
        id: 2.name: 'zm'
    }, {
        id: 3.name: 'applyI}]var data2 = data.sort(createComparisonFunction('name'))
console.log(data2)
Copy the code

A very practical sorting method;

By default, the sort() method performs toString() on both objects and then determines their order, but this makes no sense. You can use the above method to create a comparison function to compare properties;

Function of the internal

There are two special arguments inside the function: arguments and this; ES6 added a new. Target attribute.

arguments

The Arguments object also has a callee property, which is a pointer to the function the arguments object belongs to.

this

It has a different meaning in standard functions and arrow functions;

  • In standard functions, this refers to the context object that treats the function as a method call;
  • In the arrow function, this refers to the context in which the arrow function is defined;
caller

This property refers to the function calling the current function, or null if called from global scope;

new.target

New in ES6 is detecting whether functions call the new.target attribute using the new keyword. If the function is called normally, the value of new.target is undefined. If it is called using the new keyword, new.target will refer to the invoked constructor.

Function properties and methods

Properties:

Each function has two attributes: Length and prototype

The length attribute holds the number of named arguments defined by the function.

Prototype is where all instance methods of a reference type are stored. ToString (),valueOf() and other methods are stored on Prototype and shared by all instances.

methods

Call,apply,bind

Both change the direction of this;

Functional expression

Why do function declarations have function expressions?

Take a look at the following scenario, which applies to function expressions:

// Function declaration
if (falg) {
    function sayHi () {
        console.log('Hi')}}else {
    function sayHi () {
        console.log('Yo')}}Copy the code

Browsers correct this problem differently. Most browsers ignore falg and return the second declaration. Firefox returns the first declaration when flag is true. This is dangerous, so use the following:

var sayHi;
if (falg) {
    sayHi = function () {
        console.log('Hi')}}else {
    sayHi = function () {
        console.log('Yo')}}Copy the code

Another use scenario is closures, where a function returns another function and accepts it in a variable, also a function expression;

recursive

A recursive function is a function that calls itself by name;

Note: When writing recursive functions, arguments.callee is the preferred reference to the current function;

However, code running in strict mode cannot access argument. Callee, which will cause an error. You can use named function expressions to do this.

Tail-call optimization

Tail call: The return value of an external function is the return value of an internal function;

Take a look at the code:

function outFun() {
    return innerFun() / / end call
}
Copy the code
The final call optimizes the condition
  • Code executes in strict mode;
  • The return value of an external function is a call to the tail-calling function;
  • No additional logic needs to be performed after the function called last returns;
  • Tail-calling functions are not closures that reference free variables in the scope of an external function

This tail-call optimization is very effective in recursive scenarios.

closure

Closure definitions: functions that refer to variables in the scope of another function, usually implemented in nested functions;

This object

In the absence of closures;

1. If the inner function has no arrow function definition, this object is bound to the context in which the function is executed at run time. If it is called globally, in non-strict mode this refers to a window, in strict mode this refers to undefined;

If called as a method of an object, thisz points to the value of that object;

3. Anonymous functions are not bound to an object, which means that this refers to the window except for undefined in strict mode.

Closure cases:

Each function is automatically created with two special variables, this and arguments, when called. An inner function can never directly access these two variables of an outer function.

Instead of using the arrow function, you save this in another variable that is accessible to the closure, and it is possible to call this variable inside the function

window.identity = 'The Window'
let obj = {
    identity:'My Object'.getIdentity() {
        return this.identity
    }
}
console.log(obj.getIdentity());   // My Object
console.log((obj.getIdentity)());  // My Object
console.log((obj.getIdentity = obj.getIdentity)());  // The Window
Copy the code

The key here is the second and third lines; The second line calls (obj.getidEntity)(), which looks like a reference to a function when parenthesized, but the this value does not change.

Line 3: An assignment is performed, and then the result of the assignment is called. Since The value of The assignment expression is The function itself, this is not bound to any object, so “The Window” is returned.

Memory leaks caused by closures

Take a look at the full code:

function assignHandle() {
    let element = document.querySelector(".textDom")
    element.onclick = function() {
        console.log(element.className)
    }
}
Copy the code

The above code creates a closure, the Element’s event handler, that creates a circular reference; Anonymous functions refer to live objects in assignHandle, preventing element’s reference count from being cleared. As long as this anonymous function exists, Element’s reference count is at least 1, meaning that memory is not reclaimed;

This can be avoided by modifying the example slightly:

function assignHandle() {
    let element = document.querySelector(".textDom")
    let className = element.className
    element.onclick = function () {
        console.log(className)
    }
    element = null
}
Copy the code

After modification: The closure refers to an ID that holds the element.id variable, eliminating circular references. However, this step alone is not enough to solve the memory problem; Because closures still refer to the live object that contains the function, which contains Element. Even if the closure does not use Element directly, a reference to it is stored on the live object that contains the function. Therefore, setting Element to NULL removes references to the COM pair and reduces its reference count, ensuring that its memory can be reclaimed at the appropriate time.

A function expression that is invoked immediately

Also known as self-executing functions, that is, you define an anonymous function, and then you call it with curly braces

Prior to ES6, it was often used to simulate block-level scopes;

Private variables

Strictly speaking, JavaScript has no concept of private members; all object attributes are public. However, there is a concept of private variables. Any variable defined in a function or block is considered private because it cannot be accessed from outside the function or block. Private variables include function parameters, local variables, and other functions defined inside the function

A privileged method is a public method that has access to a function’s private variables (and private functions).

function Person(name) { 
    this.getName = function() { 
    return name; 
 }; 
 this.setName = function (value) { 
    name = value; 
 }; 
} 
let person = new Person('Nicholas'); 
console.log(person.getName()); // 'Nicholas' 
person.setName('Greg'); 
console.log(person.getName()); // 'Greg'
Copy the code

The constructor in this code defines two privileged methods: getName() and setName(). Each method can be called outside the constructor to read and write the private name variable. Outside of the Person constructor, there is no other way to access the name. Because both methods are defined inside constructors, they are closures that can access name through the scope chain. The private variable name is unique to each Person instance because each constructor call recreates a set of variables and methods. There’s a catch, though: you must implement this isolation through constructors.

The downside of the constructor pattern is that the new method is recreated for each instance.

Static private variable
(function() {
  let name = "";
  Person = function(value) {
    name = value;
  };
  Person.prototype.getName = function() {
    return name;
  };
  Person.prototype.setName = function(value) { name = value; }; }) ();let person1 = new Person("Nicholas");
console.log(person1.getName()); // 'Nicholas'
person1.setName("Matt");
console.log(person1.getName()); // 'Matt'
let person2 = new Person("Michael");
console.log(person1.getName()); // 'Michael'
console.log(person2.getName()); // 'Michael'
Copy the code

The Person constructor here has access to the private variable name, just like the getName() and setName() methods. With this pattern, name becomes a static variable and is available to all instances. This means that calling setName() on any instance to modify this variable will affect other instances. Calling setName() or creating a new Person instance sets the name variable to a new value. All instances return the same value

Creating static private variables like this allows for better code reuse with prototypes, except that each instance does not have its own private variable. Ultimately, whether to put a private variable in an instance or as a static private variable depends on your needs.

The module pattern

A singleton is an object that has only one instance. By convention, JavaScript creates singleton objects from object literals, as shown in the following example:

let singleton = {
  name: value,
  method() {
    // The code for the method}};Copy the code

The modular pattern is an extension of the singleton. Object literals define the common interface of the singleton. This pattern can be used if the singleton needs some kind of initialization and needs to access private variables

// Private variables and functions
let components = new Array(a);/ / initialization
components.push(new BaseComponent());
// Public interface
return {
  getComponentCount() {
    return components.length;
  },
  registerComponent(component) {
    if (typeof component == 'object') { components.push(component); }}}; } ();Copy the code
Module enhancement mode

Another way to take advantage of the modular pattern is to enhance an object before returning it. This is suitable for scenarios where the singleton object needs to be an instance of a particular type, but you must add additional properties or methods to it.

let singleton = (function() {
  // Private variables and functions
  let privateVariable = 10;
  function privateFunction() {
    return false;
  }
  // Create an object
  let object = new CustomType();
  // Add privileges/public attributes and methods
  object.publicProperty = true;
  object.publicMethod = function() {
    privateVariable++;
    return privateFunction();
  };
  // Return the object
  returnobject; }) ();Copy the code