1. An overview of the

1.1 Declaration of functions

There are three ways to declare functions in JS.

(1) function command

The block of code declared by the function command is a function. The function command is followed by the function name, which is followed by a pair of parentheses containing the parameters of the function passed in. The body of the function is enclosed in braces.

function print(s) {
  console.log(s);
}
Copy the code

The above code names a print function, which you can call later using the form print(). This is called Function Declaration.

(2) Function expression

In addition to declaring functions with the function command, you can also write variable assignments.

var print = function(s) {
  console.log(s);
};
Copy the code

This writing assigns an anonymous function to a variable. In this case, the anonymous Function is also called Function Expression because only expressions can be placed on the right side of the equal sign of an assignment statement.

When a function is declared using a function expression, the function command is not followed by the function name. If a function name is added, the function name is only valid inside the function body, not outside it.

var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function
Copy the code

The above code adds the function name x to the function expression. This x is only available inside the function body and refers to the function expression itself, not anywhere else. This is useful both for calling itself from within the function body and for debugging (when the debugger displays the function call stack, it displays the function name instead of an anonymous function). Therefore, the following form of function declaration is also very common.

var f = function f() {};
Copy the code

Note that the function expression requires a semicolon at the end of the statement to indicate the end of the statement. Function declarations do not follow closing braces with a semicolon. In general, these two ways of declaring functions are so slightly different that they can be considered approximately equivalent.

(3) Function constructor

The third way to declare a Function is the Function constructor.

var add = new Function(
  'x'.'y'.'return x + y'
);

/ / is equivalent to
function add(x, y) {
  return x + y;
}
Copy the code

In the above code, the Function constructor takes three arguments, all of which are arguments to the add Function except that the last argument is the “body” of the add Function.

You can pass as many arguments to the Function constructor as you want. Only the last argument is considered the Function body. If there is only one argument, that argument is the Function body.

var foo = new Function(
  'return "hello world"'
);

/ / is equivalent to
function foo() {
  return 'hello world';
}
Copy the code

The Function constructor can omit the new command and return exactly the same result.

In general, this way of declaring functions is very unintuitive and rarely used.

1.2 Repeated declarations of functions

If the same function is declared more than once, the later declaration overrides the previous one.

function f() {
  console.log(1);
}
f() / / 2

function f() {
  console.log(2);
}
f() / / 2
Copy the code

In the above code, the latter function declaration overrides the previous one. Also, it is important to note that the previous declaration is invalid at all times due to the promotion of the function name (see below).

1.3 Parenthesis operators, return statements, and recursion

When calling a function, use the parenthesis operator. In parentheses, you can add parameters to the function.

function add(x, y) {
  return x + y;
}

add(1.1) / / 2
Copy the code

In the above code, the function name is followed by a pair of parentheses to call the function.

A return statement inside the function body. When the JavaScript engine encounters a return statement, it simply returns the value of the expression following the return statement. That is, the expression in the return statement is the return value of the function. The return statement is not required. If it is not, the function returns no value or returns undefined.

Functions can call themselves, which is called recursion. The following is a recursive calculation of the Fibonacci sequence of the code.

function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

fib(6) / / 8
Copy the code

In the code above, fib is called from inside the fib function and the sixth element of the Fibonacci sequence is calculated to be 8.

1.4 First-class citizen

The JavaScript language treats a function as a value, with the same status as any other value (numeric, string, Boolean, and so on). Where values can be used, functions can be used. For example, functions can be assigned to variables and attributes of objects, passed as arguments to other functions, or returned as the result of functions. A function is just a value that can be executed, there is nothing special about it.

Functions are called first-class citizens in the JavaScript language because they are on an equal footing with other data types.

function add(x, y) {
  return x + y;
}

// Assign a function to a variable
var operator = add;

// take functions as arguments and return values
function a(op){
  return op;
}
a(add)(1.1)
/ / 2
Copy the code

1.5 Promotion of function names

JavaScript engines treat function names as if they were variable names, so when you declare a function with the function command, the entire function is promoted to the header of the code just like the variable declaration. Therefore, the following code does not report an error.

f();

function f() {}
Copy the code

On the surface, the above code appears to call f before the declaration. But in reality, function F is promoted to the code header due to “variable promotion”, which is declared before the call. However, iF you define a function using an assignment statement, JavaScript will report an error.

f();
var f = function (){};
// TypeError: undefined is not a function
Copy the code

The code above is equivalent to the form below.

var f;
f();
f = function () {};
Copy the code

In line 2 of the code above, when I call f, it’s just declared, it’s not assigned yet, it’s equal to undefined, so I get an error. Therefore, if you declare the same function using both the function command and an assignment statement, you always end up using the assignment statement definition.

var f = function () {
  console.log('1');
}

function f() {
  console.log('2');
}

f() / / 1
Copy the code

1.6 Functions cannot be declared in conditional statements

According to the ES5 specification, you cannot declare a function in a block of code that is not a function. The most common cases are if and try statements.

if (foo) {
  function x() {}}try {
  function x() {}}catch(e) {
  console.log(e);
}
Copy the code

The above code declares two functions in the if block and the try block respectively, which is illegal according to the language specification. However, the reality is that browsers tend to run without error.

However, because of the promotion of function names, declaring functions in conditional statements can be invalid, which is a very error-prone area.

if (false) {
  function f() {}
}

f() / / is not an error
Copy the code

The original intent of the above code is not to declare f, but the promotion of f causes the if statement to be invalid, so the above code does not report an error. The only way to define a function in a conditional statement is to use function expressions.

if (false) {
  var f = function () {};
}

f() // undefined
Copy the code

2. Function properties and methods

2.1 name attribute

The function’s name property returns the name of the function.

function f1() {}
f1.name  //"f1"
Copy the code

If the function is defined by variable assignment, the name attribute returns the name of the variable.

var f2 = function () {};
f2.name // "f2"
Copy the code

However, this is only true if the value of the variable is an anonymous function. If the value of the variable is a named function, the name attribute returns the function name after the function keyword.

var f3 = function myName() {};
f3.name // 'myName'
Copy the code

In the above code, f3. Name returns the name of the function expression. Note that the real function name is still F3, and that the name myName is only available inside the function body.

One use of the name attribute is to get the name of the parameter function.

var myFunc = function () {};

function test(f) {
  console.log(f.name);
}

test(myFunc) // myFunc
Copy the code

In the above code, the name attribute inside the test function tells you what function was passed as an argument.

2.2 length attribute

The length attribute of the function returns the number of arguments that the function expects to pass, that is, the number of arguments in the function definition.

function f(a, b) {}
f.length / / 2
Copy the code

The above code defines the empty function f, whose length attribute is the number of arguments at definition. The length property is always equal to 2, no matter how many arguments are entered during the call.

The Length attribute provides a mechanism for determining the difference between defining-time and call-time parameters for object-oriented programming “method overload.”

2.3 the toString ()

The toString method of the function returns a string containing the source code of the function.

function f() {
  a();
  b();
  c();
}

f.toString()
// function f() {
// a();
// b();
// c();
// }
Copy the code

Comments inside a function can also be returned.

function f() {/* This is a multi-line comment */}

f.toString()
// "function f(){/*
// This is one
// Multi-line comments
/ / * /}"

Copy the code

Using this, you can implement multi-line strings in disguise.

var multiline = function (fn) {
  var arr = fn.toString().split('\n');
  return arr.slice(1, arr.length - 1).join('\n');
};

function f() {/* This is a multi-line comment */}

multiline(f);
// "This is one
// multi-line comment"
Copy the code

3. Function scope

3.1 define

Scope refers to the scope within which a variable exists. In the ES5 specification, Javascript has only two scopes: a global scope, where variables exist throughout the program and can be read anywhere; The other is function scope, where variables exist only inside a function. ES6 has added block-level scopes that are not covered in this tutorial.

Variables declared outside a function are called global variables, which can be read inside the function.

var v = 1;

function f() {
  console.log(v);
}

f()
/ / 1

Copy the code

The above code shows that the global variable v can be read from inside function f.

Variables defined inside a function that cannot be read from outside are called local variables.

function f(){
  var v = 1;
}

v // ReferenceError: v is not defined

Copy the code

In the above code, the variable v is defined inside the function, so it is a local variable that cannot be read outside the function.

A variable defined inside a function overrides a global variable of the same name in that scope.

var v = 1;

function f(){
  var v = 2;
  console.log(v);
}

f() / / 2
v / / 1
Copy the code

In the above code, the variable v is defined both outside and inside the function. As a result, defined inside the function, the local variable v overrides the global variable v.

Note that for var commands, local variables can only be declared inside a function, in other blocks, and are always global variables.

if (true) {
  var x = 5;
}
console.log(x);  / / 5

Copy the code

In the code above, the variable X is declared in the conditional judgment block, and the result is a global variable that can be read outside the block.

3.2 Variable promotion within the function

As with global scopes, “variable promotion” occurs within function scopes. A variable declared by the var command is promoted to the head of the function body no matter where it is located.

function foo(x) {
  if (x > 100) {
    var tmp = x - 100; }}/ / is equivalent to
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}
Copy the code

3.3 Scope of the function itself

The function itself is also a value and has its own scope. Its scope, like that of a variable, is the scope in which it is declared, independent of the scope in which it is run.

var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() / / 1
Copy the code

In the above code, function X is declared outside of function F, so its scope is bound outside. The inner variable A is not evaluated inside function F, so it prints 1 instead of 2.

In short, the scope in which a function executes is the scope in which it was defined, not the scope in which it was called.

It is easy to make A mistake if function A calls function B without considering that function B does not refer to the internal variables of function A.

var x = function () {
  console.log(a);
};

function y(f) {
  var a = 2;
  f();
}

y(x)
// ReferenceError: a is not defined
Copy the code

The above code takes function x as an argument and passes in function y. However, function x is declared outside of function y, so the internal variable A of function Y cannot be found, resulting in an error.

Similarly, functions declared inside the function body are scoped inside the function body.

function foo() {
  var x = 1;
  function bar() {
    console.log(x);
  }
  return bar;
}

var x = 2;
var f = foo();
f() / / 1

Copy the code

In the above code, function foo declares a function bar within its scope bound to foo. When we fetch bar outside foo, the variable x points to x inside foo, not x outside foo. It is this mechanism that makes up the “closure” phenomenon described below.

Parameters of 4.

4.1 an overview of the

When a function is running, it sometimes needs to provide external data. Different external data will get different results. This external data is called parameter.

function square(x) {
  return x * x;
}

square(2) / / 4
square(3) / / 9
Copy the code

X is the argument to the square function. You need to supply this value every time you run it, or you won’t get the result.

4.2 Parameter omission

Function arguments are not required and Javascript allows them to be omitted.

function f(a, b) {
  return a;
}

f(1.2.3) / / 1
f(1) / / 1
f() // undefined

f.length / / 2
Copy the code

The function f in the above code defines two arguments, but JavaScript does not report an error no matter how many arguments (or no arguments) are provided at runtime. The value of the omitted parameter becomes undefined. Note that the length attribute of the function has nothing to do with the actual number of arguments passed in, but only the expected number of arguments passed in.

However, there is no way to omit only the front arguments and keep the back ones. If you must omit the first argument, only pass undefined explicitly.

function f(a, b) {
  return a;
}

f( , 1) // SyntaxError: Unexpected token,(...)
f(undefined.1) // undefined
Copy the code

In the above code, an error is reported if the first argument is omitted

4.3 Transfer Mode

Function parameters that are primitives (numeric, string, Boolean) are passed by passes by value. This means that changing parameter values inside the function does not affect the outside of the function.

var p = 2;

function f(p) {
  p = 3;
}
f(p);

p / / 2

Copy the code

In the above code, the variable p is a value of primitive type, and the function f is passed by value. So, inside the function, the value of p is a copy of the original value, and no matter how you change it, it doesn’t affect the original value.

However, if the function argument is a value of a compound type (array, object, other function), the pass is passed by reference. That is, the address of the original value passed to the function, so modifying arguments inside the function will affect the original value.

var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p / / 2

Copy the code

In the above code, f is passed the address of the argument object obj. Therefore, modifying the property P of obj inside the function affects the original value.

Note that if the function internally modifiers, instead of a property of the parameter object, the entire parameter is replaced, the original value is not affected.

var obj = [1.2.3];

function f(o) {
  o = [2.3.4];
}
f(obj);

obj / / [1, 2, 3]

Copy the code

In the above code, inside function f, the argument object obj is replaced entirely with another value. This does not affect the original value. This is because the value of the formal argument (o) is actually the address of the argument obj, and reassigning to o causes o to point to another address. The value stored at the original address is of course not affected.

4.4 Parameters with the Same Name

If there is an argument with the same name, the value that appears last is taken.

function f(a, a) {
  console.log(a);
}

f(1.2) / / 2
Copy the code

In the above code, the function f takes two arguments named A. When the value is set, the following a prevails, even if the following A has no value or is omitted.

function f(a, a) {
  console.log(a);
}

f(1) // undefined
Copy the code

When f is called without a second argument, the value of a becomes undefined. At this point, if you want to get the value of the first a, you can use the Arguments object.

function f(a, a) {
  console.log(arguments[0]);
}

f(1) / / 1

Copy the code

4.5 the arguments object

(1) the definition

Because JavaScript allows functions to have an indefinite number of arguments, you need a mechanism to read all of the arguments inside the function body. That’s where arguments objects come from.

The arguments object contains all arguments to the function run time, arguments[0] being the first argument, arguments[1] being the second, and so on. This object can only be used inside the function body.

var f = function (one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

f(1.2.3)
/ / 1
/ / 2
/ / 3
Copy the code

In normal mode, the Arguments object can be modified at run time.

var f = function(a, b) {
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1.1) / / 5

Copy the code

In the above code, the arguments passed in when f is called are changed inside the function to 3 and 2.

In strict mode, the Arguments object is a read-only object and changes to it are invalid but do not report errors.

var f = function(a, b) {
  'use strict'; // Enable strict mode
  arguments[0] = 3; / / is invalid
  arguments[1] = 2; / / is invalid
  return a + b;
}

f(1.1) / / 2
Copy the code

In the above code, the body of the function is in strict mode, so modifying the Arguments object is invalid.

You can determine how many arguments a function call takes from the length property of the Arguments object.

function f() {
  return arguments.length;
}

f(1.2.3) / / 3
f(1) / / 1
f() / / 0

Copy the code

(2) Relation with array

Note that arguments is an object, although it looks a lot like an array. Array-specific methods, such as slice and forEach, cannot be used directly on arguments objects.

If you want arguments objects to use array methods, the real solution is to convert Arguments to a real array. Here are two common conversion methods: slice and filling in new arrays one by one.

var args = Array.prototype.slice.call(arguments);

/ / or
var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}

Copy the code

(3) the callee properties

The Arguments object takes a callee property and returns its corresponding function.

var f = function () {
  console.log(arguments.callee === f);
}

f() // true
Copy the code

Arguments.callee can be used to call the function itself. This property is disabled in strict mode and is therefore not recommended.

5. Other knowledge points about functions

5.1 the closure

Closures are a difficult and characteristic feature of Javascript, and many advanced applications rely on closures.

To understand closures, you must first understand variable scope. As mentioned earlier, JavaScript has two types of scope: global scope and function scope. Global variables can be read directly from inside functions.

var n = 999;

function f1() {
  console.log(n);
}
f1() / / 999

Copy the code

In the above code, the function f1 can read the global variable n.

However, variables declared inside the function cannot be read outside the function.

function f1() {
  var n = 999;
}

console.log(n)
// Uncaught ReferenceError: n is not defined

Copy the code

In the above code, the variable n declared inside the function f1 is unreadable outside the function.

If, for whatever reason, you want to get local variables in the function. Normally, this is not possible and can only be achieved through workarounds. That is, inside the function, define another function.

function f1() {
  var n = 999;
  function f2() {console.log(n); / / 999}}Copy the code

In the code above, the function f2 is inside the function f1, and all local variables inside f1 are visible to F2. But the other way around, local variables inside F2 are invisible to F1. This is the JavaScript language’s unique “chain scope” structure, in which child objects look up, level by level, to all of the parent objects’ variables. Therefore, all variables of the parent object are visible to the child object, and vice versa.

Since F2 can read local variables of F1, if f2 is returned, we can read internal variables outside of F1.

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); / / 999
Copy the code

In the above code, the return value of function f1 is function f2. Since F2 can read f1’s internal variables, it can obtain F1’s internal variables externally.

The closure is the function F2, which reads variables inside other functions. Because in the JavaScript language, only child functions inside a function can read internal variables, closures can be understood simply as “functions defined inside a function.” The best thing about a closure is that it can “remember” the environment in which it was created. For example, F2 remembers the environment in which it was created, F1, so f2 can be used to get the internal variables of F1. In essence, a closure is a bridge between the inside and outside of a function.

A closure is useful for reading variables inside a function and for keeping those variables in memory at all times. In the example below, closures cause internal variables to remember the result of the last call.

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() / / 5
inc() / / 6
inc() / / 7
Copy the code

In the above code, start is the internal variable of the function createIncrementor. With closures, the state of start is preserved, and each call is evaluated on the basis of the previous call. As you can see, the closure Inc makes the function createIncrementor’s internal environment always exist. So, a closure can be thought of as an interface to a function’s internal scope.

Why is that? The reason is that inc is always in memory, and inc depends on createIncrementor and therefore is always in memory and will not be collected by the garbage collection mechanism after the call ends.

Closures are also useful for encapsulating the private properties and methods of an object.

function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person('Joe');
p1.setAge(25);
p1.getAge() / / 25
Copy the code

In the code above, the internal variable _age of the function Person, via the closures getAge and setAge, becomes the private variable of the return object P1.

Note that each time the outer function is run, a new closure is generated, which in turn preserves the inner variables of the outer function, so memory consumption is high. Therefore, you should not abuse closures, which can cause performance problems for your web pages.

5.2 Immediately Called Function Expression (IIFE)

In Javascript, parentheses () are operators that follow a function name to indicate that the function is called. For example, print() calls the print function.

Sometimes we need to call a function immediately after it is defined. In this case, you should not enclose parentheses after the function definition, as this would cause a syntax error.

function(){ /* code */} ();// SyntaxError: Unexpected token (
Copy the code

The reason for this error is that the function keyword can be used as either a statement or an expression.

/ / statements
function f() {}

/ / expression
var f = function f() {}
Copy the code

To avoid parsing ambiguity, the JavaScript engine dictates that if the function keyword appears at the beginning of a line, it should be interpreted as a statement. Therefore, the JavaScript engine sees the function keyword at the beginning of the line and thinks that this paragraph is all function definitions and should not end with parentheses, so it reports an error.

The solution is to keep function out of the beginning of the line and have the engine interpret it as an expression. The easiest way to do this is to put it in parentheses.

(function(){ /* code */} ());/ / or
(function(){ /* code */}) ();Copy the code

In both cases, the engine will assume that it is followed by a representation, not a function definition statement, so it avoids errors. This is called the immediate-Invoked Function Expression, or IIFE.

Note that the semicolon at the end is necessary in both cases. If the semicolon is omitted, an error may be reported when two IIFE are connected.

/ / an error
(function(){ /* code */} ()) (function(){ /* code */} ())Copy the code

There is no semicolon between the two lines of the code above; JavaScript interprets them together, interpreting the second line as an argument to the first line.

By extension, any method that allows the interpreter to process function definitions as expressions, such as the following three, will have the same effect.

var i = function(){ return 10; } ();true && function(){ /* code */} ();0.function(){ /* code */} ();Copy the code

Even if I write it like this, it works.

!function () { /* code */} (); ~function () { /* code */} (); -function () { /* code */} (); +function () { /* code */} ();Copy the code

Normally, this “immediate function expression” is used only for anonymous functions. Its purpose is twofold: first, there is no need to name functions to avoid polluting global variables; Second, a separate scope is formed inside IIFE, which can encapsulate some private variables that cannot be read externally.

/ / write one
var tmp = newData;
processData(tmp);
storeData(tmp);

/ / write two
(function () {
  vartmp = newData; processData(tmp); storeData(tmp); } ());Copy the code

In the above code, method 2 is better than method 1 because it completely avoids polluting global variables.

6. The eval command

The eval command executes a string as a statement.

eval('var a = 1; ');
a / / 1
Copy the code

The above code runs the string as a statement, generating the variable A.

Strings placed in eval should have a meaning of their own and should not be used with commands other than eval. For example, the following code will report an error.

eval('return; ');
Copy the code

Eval has no scope of its own and is executed within the current scope, so it is possible to modify the value of a currently scoped variable, causing security problems.

var a = 1;
eval('a = 2');

a / / 2
Copy the code

In the above code, the eval command modifies the value of the external variable A. For this reason, Eval is a security risk.

To prevent this risk, JavaScript states that if you use strict mode, eval internally declared variables will not affect the external scope.

(function f() {
  'use strict';
  eval('var foo = 123');
  console.log(foo);  // ReferenceError: foo is not defined}) ()Copy the code

In the above code, the inside of f is in strict mode, so that the foo variable declared inside eval does not affect the outside.

However, even in strict mode, eval can still read and write variables of the current scope.

(function f() {
  'use strict';
  var foo = 1;
  eval('foo = 2');
  console.log(foo);  / / 2}) ()Copy the code

In the code above, the external variables are overwritten inside eval in strict mode, so security risks remain.

In addition, the eval command string is not optimized by the JavaScript engine and runs slowly. This is another reason not to use it.

In general, eval is most commonly used to parse JSON data strings, but the correct way to do it is to use the browser-provided json.parse method.

Inside the JavaScript engine, eval is actually a reference that calls an internal method by default. This leaves the use of eval in two cases. One is a call to eval(expression) like the one above, which is called “direct use,” in which the scope of eval is the current scope. Other methods are called “indirect calls,” and eval is always globally scoped.

var a = 1;

function f() {
  var a = 2;
  var e = eval;
  e('console.log(a)');
}

f() / / 1
Copy the code

In the above code, eval is called indirectly, so even though it is in a function, its scope is global, so output A is a global variable.

Indirect calls to Eval can take a variety of forms, and any call that is not direct is an indirect call.

eval.call(null.'... ')
window.eval('... ')
(1.eval) ('... ')
(eval.eval) ('... ')
Copy the code

These forms are indirect calls to eval, so they are globally scoped.

Similar to eval is the Function constructor. You can use it to generate a function and then call that function. You can also execute a string as a command.

var jsonp = 'foo({"id": 42})';

var f = new Function( 'foo', jsonp );
// define the following function
// function f(foo) {
// foo({"id":42});
// }

f(function (json) {
  console.log( json.id ); / / 42
})
Copy the code

In the above code, jsonp is a string, and the Function constructor turns the string into the Function body. When this function is called, jSONP is executed. The essence of this writing is to put the code in the function scope and avoid global scope.

However, new Function() is also written to read and write global scopes, so it should also be avoided.

Refer to the link

From ruan Yifeng website content. Javascript.ruanyifeng.com/grammar/fun…