Home > Web Front-end > JS Tutorial > JavaScript Advanced Programming (3rd Edition) Study Notes 7 js Functions (Part 1)_Basic Knowledge

JavaScript Advanced Programming (3rd Edition) Study Notes 7 js Functions (Part 1)_Basic Knowledge

WBOY
Release: 2016-05-16 17:49:10
Original
910 people have browsed it
Variable type

Before talking about functions, let’s talk about variable types first.

1. Variables: Variables are essentially named memory spaces.

2. The data type of the variable: refers to the data type of the value that the variable can store, such as Number type, Boolean type, Object type, etc. In ECMAScript, the data type of the variable is dynamic and can be run at runtime When changing the data type of a variable.

3. Variable type: refers to the type of the variable itself. In ECMAScript, there are only two types of variables: value type and reference type. When the data type of the variable is a simple data type, the variable type is a value type. When the data type of the variable is an object type, the variable type is a reference type. Without causing ambiguity, the data type of a variable can also be called a variable type.

So, what is the difference between value types and reference types? The most important one is that when the variable type is a value type, the variable stores the variable value itself, and when the variable type is a reference type, the variable stores not the variable value, but just a pointer to the variable value, accessing the reference When the variable value of the type is obtained, the pointer is first obtained, and then the variable value is obtained based on the pointer. If you assign a reference type variable value to another variable, the end result is that both variables point to the same variable value at the same time. Modifying one of them will also modify the other:
Copy code The code is as follows:

var a = {
name:'linjisong',
age:29
};
var b = a;//Assign reference type variable a to variable b. Both a and b point to the object that a starts pointing to.
b.name = 'oulinhai';//Modify the object pointed to by b. Object, that is, the object pointed to by a has been modified
console.info(a.name); //oulinhai
b = {//Reassign the variable, but the object pointed by b has not changed, that is, a The pointed object has not changed
name:'hujinxing',
age:23
};
console.info(a.name);//oulinhai

Okay Okay, let’s talk about variable types first. If we continue to store data structures in memory, we are afraid that we will sink and be unable to float.

Function

If the object is a room, then the function is a room with magical effects. A function is an object first, and then this function object also has many magical functions...

1. Function

(1) Function is an object

Function It is also an object, and the function used to create a function object instance is the built-in Function() function (creating an object instance requires a function, and a function is an object instance. Does it make you decide which comes first, the chicken or the egg? Confused? Don't get too confused, as long as the chicken can lay eggs and the eggs can hatch chickens, let's leave it to philosophers), but objects like functions are so different from ordinary objects that When using typeof on a function object instance, it returns function instead of object.

(2) The function name is a reference type variable pointing to the function object
Copy code The code is as follows:

function fn(p){
console.info(p);
}
console.info(fn);//fn(p), you can use fn as a general variable To access
var b = fn;
b('function');//function, you can use function call to b, indicating that the object pointed to by b (that is, the object pointed to by fn) is a function

Note: Regarding function names, in the strict mode of ES5, the use of eval and arguments is no longer allowed. Of course, these two cannot be used for parameter names (I think unless you are a professional hacker, otherwise You won't use these as identifiers either).

2. Function creation

(1) As an object, functions are created in a similar way to ordinary objects. Use new to call the constructor Function(), which It can accept any number of parameters. The last parameter is used as the function body, and all the previous parameters are used as formal parameters of the function. The previous formal parameters can also be passed in as one parameter separated by commas. The general form is:
Copy code The code is as follows:

var fn = new Function(p1, p2, ..., pn, body);
//or
var fn = Function(p1, p2, ..., pn, body);
//or
var fn = new Function("p1, p2, ..., pn", q1, q2, ..., qn, body);
//or
var fn = Function("p1, p2, ..., pn", q1, q2, ..., qn, body);

For example:
Copy code The code is as follows:

var add = new Function('a','b','return a b;');
console.info(add(2,1));//3
var subtract = Function('a','b','return a - b;');
console.info(subtract(2,1));//1
var sum = new Function('a, b','c','return a b c;');
console.info(sum(1,2,3));//6

Creating a function in this way will Parsing the code twice, once for normal parsing and once for the function body, will affect the efficiency, but it is more suitable for situations where the function body needs to be compiled dynamically.

(2) Due to the special nature of the function object itself, we can also use the keyword function to create a function:
Copy code The code is as follows:

function add(a, b){
return a b;
}
console.info(add(2,1));/ /3
var subtract = function(a, b){
return a - b;
};
console.info(subtract(2,1));//1

As you can see from the above, there are two ways to create a function using the function keyword: function declaration and function expression. Both methods can achieve the effect we want, so what is the difference between them? This is what we will talk about next.

3. Function declaration and function expression

(1) From the formal distinction, in the ECMA-262 specification, you can see:
Copy code The code is as follows:

Function declaration: function Identifier (parameter list (optional)){function body }
Function expression: function Identifier (optional) (parameter list (optional)) {function body}

Except the identifier (function name) of the function expression is optional There is no difference except that, but we can also know from it: anything without a function name must be a function expression. Of course, if there is a function name, we can only judge it from the context.

(2) Distinguish from the context, this is simple to say, that is: the context that only allows expressions to appear must be function expressions, and the context that only allows statements to appear must be function declarations. Give some examples:
Copy code The code is as follows:

function fn(){};/ /Function declaration
//function fn(){}(); // Exception, function declaration cannot be called directly
var fn = function fn(){};//Function expression
(function fn (){});//Function expression, within the grouping operator
function fn(){console.info(1);}();//1, function expression, appears after the operator, Therefore, it can be called directly. Here, other operators can also be used, such as new
new function fn(){console.info(2);}();//2, function expression, after the new operator
(function(){
function fn(){};//Function declaration
});

(3) Difference: Why do we spend so much effort to distinguish What about function declarations and function expressions? Naturally, it is because of their differences. The biggest difference between them is that declarations will be promoted. Regarding declaration promotions, in the previous article on basic grammar, we have discussed the declaration promotions in the global scope. We Review the conclusion there:

A. When the engine parses, it will first parse the function declaration, then parse the variable declaration (the type will not be overwritten during parsing), and finally execute the code;

B. When parsing a function declaration, the type (function) will be parsed at the same time, but it will not be executed. When parsing a variable declaration, only the variable will be parsed and will not be initialized.

There are also some examples to demonstrate (recall), but there are no declaration examples with the same name. I will add them here:
Copy code The code is as follows:

console.info(typeof fn);//function, declaration promotion, subject to function
var fn = '';
function fn(){
}
console.info(typeof fn);//string, since the code has been executed, the type of fn here becomes string
try{
fn(); //It is already of string type and cannot be called. A type exception is thrown
}catch(e){
console.info(e);//TypeError
}
fn = function(){ console.info('fn');};//If you want to call fn, you can only use a function expression to assign it to fn
fn();//fn, you can call

console.info (typeof gn);//function
function gn(){
}
var gn = '';
console.info(typeof gn);//string

It can be seen that no matter whether the variable declaration is before or after, the function declaration takes precedence when the declaration is promoted. However, after the declaration is promoted, due to the need to perform variable initialization, the function declaration is no longer initialized (function type has been parsed during promotion), so it becomes a String type when output later.

A function is defined in line 3 above, and then called immediately on line 7, but it doesn’t work! You should understand the importance of keeping the global namespace clean. Otherwise, you may encounter such ghosts as "I clearly defined a function in the code but cannot call it." On the other hand, if you want to ensure that you define functions are available, it is best to use function expressions to define them. Of course, by doing so you risk breaking other people's code.

There is another question. How do we determine that the variable type is changed during initialization and not when the variable declaration is promoted? Look at the code below:
Copy the code The code is as follows:

console.info(typeof fn) ;//function
function fn(){
}
var fn;
console.info(typeof fn);//function

As you can see, the statement The promoted type is function, and since there is no initialization code, the final type has not changed.

Regarding function declarations and function expressions, there is one more thing to note. Look at the following code:
Copy code The code is as follows:

if(true){
function fn(){
return 1;
}
}else{
function fn() {
return 2;
}
}
console.info(fn());// Output 1 in Firefox, output 2 in Opera, declare promotion in Opera, subsequent statements will overwrite The previous declaration of the same level

if(true){
gn = function(){
return 1;
};
}else{
gn = function() {
return 2;
};
}
console.info(gn());// 1, all browser output is 1

in ECMAScript In the specification, identifiers for named function expressions belong to the inner scope, while identifiers for function declarations belong to the defining scope.
Copy code The code is as follows:

var sum = function fn(){
var total = 0,
l = arguments.length;
for(; l; l--)
{
total = arguments[l-1];
}
console. info(typeof fn);
return total;
}
console.info(sum(1,2,3,4));//function, 10
console.info(fn(1 ,2,3,4));//ReferenceError

The above is the result of running a named function expression in FireFox. This name can be accessed in the function scope, but in the global scope A reference exception occurred during access. However, named function expressions will be parsed as both function declarations and function expressions in IE browsers before IE9, and two objects will be created. Fortunately, IE9 has corrected this.

In addition to the global scope, there is also a function scope. In the function scope, the parameters of the function also participate in the declaration promotion competition. The first thing to make clear is that function scope does not exist when the function is defined. Function scope only exists when the function is actually called.
Copy code The code is as follows:

// Parameters and internal variables, parameters take precedence
function fn(inner){
console.info(inner);// param
console.info(other);// undefined
var inner = 'inner';
var other = 'other ';
console.info(inner);// inner
console.info(other);// other
}
fn('param');

// Parameters and internal functions, internal functions take priority
function gn(inner){
console.info(inner);// inner() function
console.info(inner());// undefined
function inner(){
return other;
}
var other = 'other';
console.info(inner);// inner() function
console.info(inner ());// other
}
gn('param');

Through the above output, we get the priority: internal function declaration > function parameters > internal variable declaration.

One of the processes here is: first, the internal function declaration is promoted, and the type of the function name is set to the function type, then the function parameters are parsed, the actual parameter values ​​passed in are assigned to the formal parameters, and finally the internal Variable declaration promotion only promotes the declaration without initialization. If there are duplicate names, the ones with the same priority will overwrite the previous ones, and those with different priorities will not be overwritten (the ones with high priority have been parsed, and the ones with low priority will no longer be parsed).
To explain, this is just my inference based on the output results. As for the background implementation, it is also possible that the steps are completely opposite, and each step covers the results of the previous step, or even starts from the middle, and then makes a priority mark to determine whether it is needed. Coverage, of course, from an efficiency point of view, should be the process I reasoned would be better. In addition, the global scope is actually a simplified version of the function scope, without function parameters.

I won’t give a comprehensive example here. It is recommended to read this article together with the previous basic grammar article, which may have better results. Regarding priority and coverage, it also leads to a question to be discussed below.

4. Function overloading

Function is an object, and the function name is a reference type variable pointing to the function object, which makes it impossible for us to do the same thing as in general object-oriented languages. Implement overloading:
Copy code The code is as follows:

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

console.info(fn(1)); // NaN
console.info(fn(1,2));// 3

Don’t wonder why line 8 outputs NaN, because the function name is just a variable, and the two function declarations will be parsed in sequence. The function that this variable ultimately points to is the second function, and line 8 only passes in 1 parameter. Inside the function, b is automatically assigned to undefined, and then added to 1, and the result is NaN. It may be easier to understand if it is replaced by a function expression. It is just assigned twice. Naturally, the later assignment will overwrite the previous one:
Copy code The code is as follows:

var fn = function (a){ return a; }
fn = function (a,b){ return a b;}

So, how to implement overloading in ECMAScript? Recall that simple data type wrapper objects (Boolean, Number, String) can be used as constructors to create objects and as conversion functions to convert data types. This is a typical overload. In fact, we have discussed this overloading in the previous article:

(1) Overloading according to the function. The general format of this method is:
Copy code The code is as follows:

function fn(){
if(this instanceof fn)
{
/ / Function 1
}else
{
// Function 2
}
}

Although this method is feasible, its effect is obviously limited. For example, you can only overload twice, and you can only overload the constructor. Of course, you can combine apply() or call() or even the new bind() in ES5 to dynamically bind the this value inside the function to extend overloading, but this already means overloading based on the internal properties of the function.
(2) Overload
Copy code The code is as follows:

function fn(){
var length = arguments.length;
if(0 == length)//Placing the literal on the left is a habit brought over from Java, because if the comparison operator is written as If the assignment operator (0=length) is missing, the compiler will prompt me with an error. Please forgive me if you are not used to this method
  {
return 0;
}else if(1 == length)
{
return arguments[0];
} else{
return ( arguments[0]) ( arguments[1]);
}
}

console.info(fn());//0
console. info(fn(1));//1
console.info(fn(true));//1
console.info(fn(1,2));//3
console. info(fn('1','2'));//3

Here we use the function’s internal attributes arguments to implement overloading. Of course, there are many ways to overload internally, and you can also combine operators such as typeof and instanceof to achieve the functions you want. As for what exactly are the internal property arguments? That’s what I’m going to talk about below.

5. Function internal attributes arguments

To put it simply, function internal attributes are attributes that can only be accessed within the function body, because the function body can only be accessed when the function is called It will be executed when the function is called, so the internal properties of the function will only be parsed when the function is called. Each call will have corresponding parsing, so it has dynamic characteristics. Such attributes include: this and arguments. Let’s look at arguments first, and then talk about this in the next article.

(1) The parameter list in the function definition is called formal parameters, and the parameters actually passed in when the function is called are called actual parameters. Generally, C-like languages ​​require that the actual parameters must be consistent with the formal parameters when calling a function. However, in ECMAScript, there is no restriction between the two. You can have two formal parameters when defining and pass them when calling. Enter 2 actual parameters, but you can also pass in 3 actual parameters, you can also pass in only 1 actual parameter, or you can even pass in no parameters. This feature is the basis for using the internal properties of functions to implement overloading.

(2) The formal parameters can even have the same name, but when actually passed in, the later value will be used as the value of the formal parameter (in this case, arguments can be used to access the previous actual parameters):
Copy code The code is as follows:

function gn(a,a){
console .info(a);
console.info(arguments[0]);
console.info(arguments[1]);
}
gn(1,2);//2, 1,2
gn(1);//undefined, 1, undefined

This can actually be explained by the conclusion about statement promotion earlier in this article: the later ones with the same priority overwrite the previous ones. , and the value is parsed at the same time when the function parameters are parsed. Of course, in this way, security is very problematic, so in the strict mode of ES5, formal parameters with duplicate names are prohibited.

(3) The values ​​of actual parameters are accepted by formal parameters, but what if the actual parameters and formal parameters are inconsistent? The answer is to use arguments to store. In fact, even if the actual parameters and formal parameters are consistent, the arguments object still exists and is synchronized with the formal parameters that have accepted the actual parameters. Let’s refine this sentence to understand:

•arguments is an array-like object, and argument elements can be accessed through square brackets and indexes like array elements, such as arguments[0], arguments[1] .
•arguments is an array-like object. In addition to the properties and methods inherited from Object (some methods are overridden), it also has some of its own properties, such as length, callee, and caller. Here length represents the actual parameters. The number (the number of formal parameters? That is the function attribute length), callee represents the current function object, and caller is only defined to distinguish it from the function attribute caller, and its value is undefined.
•arguments is an array-like object, but it is not a real array object. You cannot directly call the array object method on arguments. If you want to call it, you can first use Array.prototype.slice.call(arguments) to convert it to an array. object.
•arguments stores the actual parameters passed in when the function is called. The 0th element stores the first actual parameter, the 1st element stores the second actual parameter, and so on.
•arguments saves the actual parameter value, and the formal parameter also saves the actual parameter value. There is a synchronization relationship between the two. If one is modified, the other will be modified accordingly.
•The synchronization between arguments and formal parameters only exists when the formal parameters actually receive actual parameters. For formal parameters that do not receive actual parameters, there is no such synchronization relationship.
Although the arguments object is very powerful, it also has a certain loss in performance, so if it is not necessary, do not use it. It is recommended to give priority to formal parameters.
Copy code The code is as follows:

fn(0,-1);
function fn(para1,para2,para3,para4){
console.info(fn.length);//4, the number of formal parameters
console.info(arguments.length);//2, the actual number of parameters
console.info(arguments.callee === fn);//true, the callee object points to fn itself
console.info (arguments.caller);//undefined
console.info(arguments.constructor);//Object(), not Array()
try{
arguments.sort();//Array-like After all, it is not an array, and the array method cannot be called directly. An exception is thrown
}catch(e){
console.info(e);//TypeError
}
var arr = Array.prototype.slice .call(arguments);//Convert to array first
console.info(arr.sort());//[-1,0], already sorted

console.info( para1);//0
arguments[0] = 1;
console.info(para1);//1, modifying arguments[0] will simultaneously modify the formal parameter para1

console. info(arguments[1]);//-1
para2 = 2;
console.info(arguments[1]);//2, modifying the formal parameter para2 will simultaneously modify arguments[1]

console.info(para3);//undefined, the formal parameters without actual parameters passed in are undefined
arguments[2] = 3;
console.info(arguments[2]);// 3
console.info(para3);//undefined, formal parameters that do not accept actual parameters have no synchronization relationship

console.info(arguments[3]);//undefined, no actual parameters are passed in , the value is undefined
para4 = 4;
console.info(para4);//4
console.info(arguments[3]);//undefined, which is the actual parameter passed in and will not be synchronized
}

After testing, the synchronization between arguments and formal parameters is two-way, but page 66 of "JavaScript Advanced Programming (3rd Edition)" says it is one-way: Modifying the formal parameters does not change the arguments. This may be another bug in the original book, or it may be that FireFox has expanded the specification. However, this also lets us know that even if it is a classic, there is still the possibility of bugs, and everything should be subject to actual operation.

• Combined with arguments and its attribute callee, you can decouple it from the function name when calling itself inside the function, so that even if the function is assigned to another variable, the function name (don’t forget, it is also a variable) In addition, being assigned a value can also ensure correct operation. Typical examples include finding factorial functions, Fibonacci sequences, etc.
Copy code The code is as follows:

//Find factorial
function factorial(num) {
if(num <= 1)
{
return 1;
}else{
return num * factorial(num - 1);
}
}
var fn = factorial;
factorial = null;
try{
fn(2);//Because factorial is called recursively inside the function, and factorial has been assigned a value of null, an exception is thrown
}catch(e){
console.info(e);//TypeError
}

//Fibonacci sequence
function fibonacci(num){
if(1 == num || 2 == num){
return 1;
}else{
return arguments.callee(num - 1) arguments.callee(num - 2);
}
}
var gn = fibonacci;
fibonacci = null;
console.info(gn(9));//34, using arguments.callee, realizes the function object and function name Decoupled, it can be executed normally

The recursive algorithm is very simple, but because of the need to maintain the running stack, the efficiency is not very good. Regarding recursive optimization, there are also many very hearty algorithms, so I won’t go into depth here.

It should be noted that arguments.callee has been banned in the strict mode of ES5. At this time, you can use named function expressions to achieve the same effect:
Copy code The code is as follows:

//Fibonacci sequence
var fibonacci = (function f(num){
return num <= 2 ? 1 : (f(num - 1) f(num - 2));
});
var gn = fibonacci;
fibonacci = null;
console.info(gn(9));//34, the use of named function expressions realizes the decoupling of function objects and function names, and can be executed normally
Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template