Declaration of functions

The 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 with the arguments passed to the function. The body of the function is in curly braces.

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

The above code names a print function that can be called later using the form print(). This is called a Function Declaration.

Function expression

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

This notation assigns an anonymous function to a variable. In this case, the anonymous Function is also called a Function Expression because only expressions can be placed on the right-hand side of the equals 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 you add a function name, the function name is valid only inside the function body, but not outside the function body.

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 within the body of the function, referring to the expression itself, and is not available anywhere else. This is useful both for calling itself inside the function body and for facilitating debugging (when the debugger shows the function call stack, it will show the name of the function instead of showing that it is an anonymous function). Therefore, the following form declaration function is also very common.

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

Note that the expression of a function requires a semicolon at the end of the statement to indicate the end of the statement. Function declarations, on the other hand, do not use a semicolon after the closing brace. In general, these two ways of declaring functions are so subtly different that they can be approximated as equivalent.

Function constructor

This is a very unintuitive way to declare functions, and almost nobody uses it, so I won’t explain it here.

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 last function declaration overrides the previous one. Also, it is important to note that the previous declaration is not valid at any time due to the promotion of the function name (see below).

First class citizen

The JavaScript language treats a function as a value, with the same status as other values (numbers, strings, booleans, and so on). Where values can be used, functions can be used. For example, functions can be assigned to attributes of variables and objects, passed as arguments to other functions, or returned as the result of a function. A function is nothing special except a value that can be executed.

Functions are called first class citizens in the JavaScript language because they are equal to other data types.

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

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

// Take the function as an argument and return value
function a(op){
  return op;
}
a(add)(1.1)
/ / 2
Copy the code

Function name promotion

The JavaScript engine treats function names like variable names, so when a function is declared with the function command, the entire function is promoted to the code header just like a variable declaration. Therefore, the following code will not report an error.

f();

function f() {}
Copy the code

On the surface, the above code appears to call the function f before it is declared. But in fact, because of “variable promotion”, the function f is promoted to the code header, which is declared before it is called. However, if you define a function with an assignment statement, JavaScript will report an error.

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

The above code is equivalent to the following form.

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

The second line above, when we call f, f is just declared, not assigned, equals undefined, so we get an error.

Note that if you declare the same function using the function command and the var assignment statement, as in the following example, the definition of the var assignment statement will eventually be used because of function promotion.

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

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

f() / / 1
Copy the code

In the example above, it appears that the function f declared later should override the previous var assignment statement, but because of function promotion, the reverse is actually true.

Properties and methods of a function

The name attribute

The name attribute of a function 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 variable name.

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 name of the function 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 f3, and 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 is being passed as an argument.

Length attribute

The length property of a function returns the number of arguments that the function expects to pass in, 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 null function f, whose length property is the number of arguments to be defined. The length property is always equal to 2, no matter how many arguments are entered on the call.

The length property provides a mechanism for determining the difference between the parameters at definition time and at call time in order to achieve the “method overload” of object-oriented programming.

toString

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

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

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

In the above example, the toString() method of function f returns the source code for f, including newlines.

For those native functions, the toString() method returns function (){[native code]}.

Math.sqrt.toString()
// "function sqrt() { [native code] }"
Copy the code

In the above code, math.sqrt () is a native function provided by the JavaScript engine, and the toString() method returns a hint of the native code.

Comments inside functions can also be returned.

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

f.toString()
// "function f(){/*
// This is one
// Multiline comments
/ / * /}"
Copy the code

Function scope

define

Scope refers to the scope in which a variable exists. In the ES5 specification, there are only two scopes for JavaScript: a global scope, where variables exist throughout the program and can be read everywhere; The other is function scope, where variables exist only within a function. ES6 adds block-level scopes, which are not covered in this article.

For top-level functions, variables declared outside the function are global variables that 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 inside the function f.

Variables defined inside a function that cannot be read 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 and cannot be read outside the function.

A variable defined inside a function that 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 the var command, local variables can only be declared inside the function. If they are declared in other blocks, they are all global variables.

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

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

The variables inside the function are promoted

As with global scope, “variable promotion” occurs within function scope. The variable declared by the var command is promoted to the head of the function body regardless of its location.

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

The scope of the function itself

The function itself is a value and has its own scope. Its scope is the same as that of a variable: 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 internal variable A is not valued inside function f, so it prints 1 instead of 2.

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

It’s easy to make A mistake if function A calls function B without considering that function B doesn’t refer to an internal variable 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. Function x is declared outside function y, so the internal variable a of function y cannot be found.

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 is internally declared a function bar, which is bound to foo in scope. When we pull bar outside foo for execution, the variable x refers to the x inside foo, not the x outside foo. It is this mechanism that constitutes the phenomenon of “closures”.

parameter

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

Omission of parameters

Function arguments are not required; 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 nO matter how many arguments (or none) are provided at runtime, JavaScript will never report an error. The value of the omitted parameter becomes undefined. Note that the length property of a function is independent of the number of arguments actually passed, only the number of arguments expected to be passed by the function.

However, there is no way to omit the first arguments and leave the latter ones. If you must omit the first argument, only undefined is passed explicitly.

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

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

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

delivery

Passes by value when a function argument is of a primitive type (value, string, or Boolean). This means that changes to parameter values in the body of the function do 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 pass-by. Therefore, inside the function, the value of p is a copy of the original value, and no modification will affect the original value.

However, if the function argument is a value of a compound type (array, object, other function), the pass is pass by Reference. That is, passing in the address of the original value of the function, so modifying parameters 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, the function f is passed with 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 changes, instead of a property of the argument object, the entire argument 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 the function f(), the argument object obj is completely replaced with another value. It doesn’t affect the original value. This is because the value of the formal argument (o) is actually the address of the argument obj, and reassigning o causes O to point to another address, leaving the value stored at the original address unaffected.

The same parameters

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, both named a. When valuing, the following A takes precedence, 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 first value of a, use the Arguments object.

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

f(1) / / 1
Copy the code

The arguments object

Since JavaScript allows functions to have an indefinite number of arguments, you need a mechanism to read all the arguments inside the body of the function. This is where the Arguments object comes from.

The Arguments object contains all arguments for the function runtime. Arguments [0] is the first argument, arguments[1] is the second argument, and so on. This object can only be used if it is 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 runtime.

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 modified inside the function to 3 and 2.

In strict mode, the Arguments object is not associated with function arguments. That is, modifying the Arguments object does not affect the actual function arguments.

var f = function(a, b) {
  'use strict'; // Enable strict mode
  arguments[0] = 3;
  arguments[1] = 2;
  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 will not affect the actual arguments a and b.

The length attribute of the Arguments object allows you to determine how many arguments to take in a function call.

function f() {
  return arguments.length;
}

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

Relation to an array

Note that arguments is like an array, but it is an object. Array-specific methods, such as Slice and forEach, cannot be used directly on arguments objects.

If you want the Arguments object to use array methods, the real solution is to convert arguments into a true array. There are two common conversion methods: the slice method and filling in the new array.

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

The callee properties

The Arguments object takes a callee property that returns its corresponding antifunction.

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

f() // true
Copy the code

You can call the function itself using arguments.callee. This property is disabled in strict mode and is therefore not recommended.