您的位置:首页 > Web前端 > JavaScript

JavaScript 之有趣的函数(函数声明、调用、预解析、作用域)

2019-07-08 15:24 1896 查看

前言:“函数是对象,函数名是指针。”,函数名仅仅是指向函数的指针,与其他包含函数指针的变量没有什么区别,话句话说,一个函数可能有多个名字。

 

-1.函数声明,function+函数名称。调用方法:函数名(参数);

function f1(x,y){
return x+y;     //函数体
}
console.log(f1(2,3));

这是最常见的指定函数名声明函数,在函数体内返回参数值,函数调用时才会输出结果。既然说到函数,那就免不了提一提它的预解析以及作用域。

此类方法定义的函数,在代码开始执行之前会通过解释器进行一个函数声明提前的过程,并将其添加到执行环境中, JavaScript 引擎会将其提升到代码树的顶端,率先执行,所以即使声明函数的代码在函数调用代码的后面,也能正确访问,上述过程就被称为函数的预解析。例如:

console.log(sum);          //控制台输出函数源代码,证明函数可以被调用
console.log(sum(3,2));     // 5
function sum(x,y){
return x+y;
}

执行环境定义了变量或者函数有权访问其他数据,每个环境中都有一个与之关联的变量对象,环境中定义的变量和函数都保存在这个对象中,解析器在处理数据时就会使用这个对象。

 

- 2.匿名函数,即没有命名的函数,通过给将函数赋值给变量的形式声明函数,也称之为函数表达式。调用方法:函数名(参数);

var f2 = function(x,y){
return x+y;
}
f2(4,5);

此类方法定义的函数不能在函数声明前调用函数,因为在预解析机制中:

1.把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。 

2.把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。

3.先提升var,在提升function 

所以上述代码如果提前调用函数,在预解析的执行环境中是:

var f2;       //变量声明提升,但赋值没有
f2(4,5);     //报错
console.log(f2(4,5));
f2 = function(x,y){
return x+y;
}

匿名函数是用一个变量去接收函数,因此预解析只将变量的声明提前了,此时的函数位于一个初始化语句中,在执行到函数体代码之前变量不会保存对函数的引用,所以直接报错。

 

-3.自调用函数,顾名思义就是自己调用自己的函数,在声明的同时进行调用,虽然方便但只能执行一次。

(function(){
console.log("这是一个自调用函数");
})();
/* * * 而它的演变过程也很简单,是由函数表达式演变而来。 * var f1 = function(){ * console.log("这是一个自调用函数"); * } * f1(); * 这里把调用时的 f1 替换成 function(){} * 所以调用时就是function(){}(); * 为了保持代码的整体性,所以在最外层加了一个括号。 * * */

//这里还有一个点需要注意,自调用函数前的一个函数如果没有输出调用,需要在函数表达式的结尾加上分号,以和自调用函数区分开来,防止将上一个函数也解析成自调用函数。

 

 

-4.Function 构造函数,Function 构造函数可以接受任意数量的参数,但最后一个参数始终被看做是函数体,而前面则看作是函数体的参数。调用:函数名();

var sum = new Function("sum1","sum2","return sum1+sum2");

 但这种定义函数的方法并不推荐使用,因为在此类函数在执行时会先解析一次常规ECMAscript 代码,再解析传入函数中的字符串,反复调用时将会影响性能。

 函数也是有数据类型的,所有函数的类型都是 function 。 

 

关于作用域链

作用域链的用途是,保证执行环境中,有权访问的所有函数和变量的有序访问。简单来说就是变量的使用范围。

 

全局变量:在函数以外,用 var 声明的变量都是全局变量,在全局执行环境中都可以使用。 !!! 但需要注意,全局变量只有在整个程序退出或者销毁后才会被释放,否则就一直占内存。

局部变量:在函数内部定义的变量就是局部变量,只能在函数内部使用。

隐式全局变量:没有var 声明的,也是作用于全局,但是可以使用delete删除。

全局作用域:全局变量的使用范围,始终是作用域链中的最后一个对象。

局部作用域:局部变量的使用范围。

块级作用域:指的是在一对大括号内声明的变量,就只能在这对大括号中使用,但是js中全部都可以使用,所以js没有块级作用域,函数除外。

 

使用过程:1.作用域链的前端始终是当前执行代码所在环境的变量对象,解析过程是按照作用域链一级一级搜索的过程。

     2.始终是从作用域的前端开始的,然后逐级向后回溯。

     3.内部变量可以通过作用域链访问外部变量,但外部变量不能访问任何内部的变量或者函数。(自内向外访问)

 

最后给大家举个栗子,综合解说函数调用、预解析以及作用域:

f1();
console.log(c);
console.log(b);
console.log(a);
function f1(){
   var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}

输出的结果为:9  9  9  9  9  报错,原因如下

function f1(){
// var a = b = c = 9;   变量的声明也提升
var a;
a = 9;  // 此时的 a 是被 var 声明的,作为函数内的局部变量,只能在函数内被调用,所以最后的a会报错
b = 9;
c = 9;  // b,c 则是隐式全局变量
console.log(a);
console.log(b);
console.log(c);
}
f1();  //调用f1函数,预解析后f1函数的声明提升到作用域的最前面,此时的代码为
console.log(c);
console.log(b);
console.log(a); 

第二个小栗子

f1();
var f1 = function (){
console.log(a);
var a = 10;
};
//结果是报错 //因为预解析的存在,所以函数和变量提前 //由于是函数表达式的形式,所以预解析后的代码为:

// var f1; // f1(); // function (){ // console.log(a); // var a = 10; // };

 

  

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐