jacascript 函数声明、函数表达式与声明提高(hoisting机制)

前言:这是笔者学习以后本身的理解与整理。若是有错误或者疑问的地方,请你们指正,我会持续更新!javascript

声明、定义、初始化

  声明的意思是宣称一个变量名的存在,定义则为这个变量分配存储空间,初始化则是给该变量名的存储空间赋予初始值;java

  javascript 中,变量没有固定类型,其存储空间会随着初始化(并赋值)而变化;面试

        <script type="text/javascript">
            var a;//声明a
            console.log(a);//undefined;   只声明,没有赋值,返回undefined
            
            a = 'hello world!';//(定义)初始化变量a而且赋值'hello world!'
            console.log(a);//hello world!
            
            var a;//再次声明a,但没有赋值,a的值不会被清空
            console.log(a);//hello world!
        </script>

hoisting 机制

  javascript 代码在执行时,通常是一行一行往下执行的,可是这里面又有一个变量声明提高的概念;express

  javascript 引擎在执行时,会把全部变量声明和函数声明都提高到当前做用域的最前面(hoisting 机制);浏览器

变量声明提高

        <script type="text/javascript">
//            console.log(a);//a is not defined   a没有声明
            console.log(b);//undefined   没找到b(没有初始化赋值),但已经声明了
            var b = 'hello world!';   //声明并初始化b赋值'hello world!'
            console.log(b);//hello world!
        </script>

  上面代码中 a 和 b 的区别就是b声明提早了,a 没有声明;因此上面代码至关于:函数

        <script type="text/javascript">
            var b;//声明b
//            console.log(a);//a is not defined  a没有声明
            console.log(b);//undefined
            b = 'hello world!';  //初始化并赋值'hello world!'
            console.log(b);//hello world!
        </script>    

 

函数声明提高

  一直对一些基本的概念的理解有些模糊,先梳理如下:学习

  1. 函数声明(function statement),使用function关键字声明一个函数,再指定一个函数名,叫函数声明。如:function fnName(){};
  2. 匿名函数anonymous functions),使用function关键字声明一个函数,可是没有命名,这个函数就叫匿名函数,写法就是:function(){};
  3. 函数表达式(function expression),把一个匿名函数当作值传给一个变量,叫函数表达式,这是最多见的函数表达式语法形式。如:var fnName = function(){};

函数声明

  函数声明(function statement),使用function关键字声明一个函数,再指定一个函数名,叫函数声明。如:function fnName(){};spa

  但须要注意的是:code

  1. 函数的声明能够是有条件的,好比嵌套在 if 语句中,有的浏览器会将这种有条件的声明当作无条件的,不论条件是true仍是false,咱们最好不要这样写;
  2. 在严格模式下,只容许在全局做用域或函数做用域的顶层声明函数。也就是说,不容许在非函数的代码块内声明函数。

  非严格模式下:blog

        <script type="text/javascript"> //非严格模式 if(true){ function find(){ console.log(456); } } find();//456 if(false){ function cantFind(){ console.log(123); } } cantFind();//TypeError: cantFind is not a function </script>

  严格模式下:

        <script type="text/javascript"> 'use strict' //严格模式下 if(true){ function find(){ console.log(456); } } find();//ReferenceError: cantFind is not defined </script>

 

函数表达式

  函数表达式(function expression),把一个匿名函数当作值传给一个变量,叫函数表达式,这是最多见的函数表达式语法形式。如:var fnName = function(){};

  1. 在 function 前面加!、+、 -、=甚至是逗号等或者把函数用()包起来均可以将函数声明转换成函数表达式;
  2. 可是!、+、-等运算符还会和函数的返回值进行运算,有时形成没必要要的麻烦,因此咱们通常用()把函数声明包起来或者用 = ,达到转换函数表达式的目的;
        <script type="text/javascript">
            //在function前面加!、+、 -、=甚至是逗号等到均可以将函数声明转换成函数表达式,咱们通常用()或者 = 
            var a = function b(){  
                return b; 
            }
            console.log(a());//function b(){console.log(b);}
            console.log(b());//ReferenceError: b is not defined   
                             //函数表达式的标识符在外部做用域是看不到的,只能在内部做用域看到
        </script>

 

  函数声明提高有两种状况:1.函数声明;2.把函数做为一个值传给一个变量(函数表达式);

  和变量声明提高同样,函数声明也会提高,而且定义也会提高(这个说法也不太准确,看下面的例子)

        <script type="text/javascript">
            play();//函数声明提高了,之因此能够正常运行,是由于定义也被提早了 
            function play(){
                console.log('hello world!');//hello world!
            }
        </script>

  以上代码中函数play()声明提高了,之因此能够正常运行,是由于函数声明提高了,定义也被提高了(这个说法也不太准确,看下面的例子),以上代码至关于:

        <script type="text/javascript">
            function play(){
                console.log('hello world!');//hello world!
            }        
            play();
        </script>

  咱们有时候会把一个函数做为值传给一个变量,这时变量会被声明提高,但函数不会,它只是一个函数表达式,咱们只须要它的返回值给变量;

  若是它是一个具名函数表达式,那么函数表达式的标识符(函数名)在外部做用域是找不到的,只在内部做用于能找到;

        <script type="text/javascript">
            //console.log(play);//ReferenceError: play is not defined   声明错误
            //play();//ReferenceError: play is not defined 声明错误

            console.log(games);//undefined  变量声明了,但没赋值,因此没找到
            //games();//TypeError: games is not a function   类型错误
            
            var games = function play(){  //函数表达式
                console.log(typeof play);
            }
            //play();//ReferenceError: play is not defined 声明错误
            games();//function  函数表达式的标识符(函数名)在外部做用域是找不到的,只在内部做用于能找到;
        </script>

  上面写的函数声明的定义也被提高了,这个说法并不太准确,

<script type="text/javascript">
    if (!("a" in window)) {   
        function a() { 
            return '为何下面弹出的a是undefined';
        }
    }
    console.log(a);  //undefined
    //若是定义也被提早,这里应该是function a(){},因此这里定义并无被提早,只有函数名被提早了,而后if语句进不去了,
    //由于函数声明能够和函数表达式相互转换,只是写法不一样而已,
    //因此上面的代码有个说法,这里的函数a能够理解成var a = function(){},函数声明的方式提早的是函数名,然而这又不能解释为何函数能在函数声明以前能被调用;
    
    b();
    function b(){
        console.log('函数声明的疑问,为何b能执行');
    }
</script>

  由于函数声明能够和函数表达式相互转换,只是写法不一样而已,因此上面的代码有个说法,函数a能够理解成 var a = function(){},函数声明的方式提早的是函数名,然而这又不能解释为何函数能在函数声明以前能被调用;

变量名解析顺序

  同一做用域下,相同变量名,后面的赋值会覆盖前面的;

  同一做用域下,若是变量名和函数名相同,那么函数名会覆盖变量名,不论它们在代码的顺序如何;可是它们的初始化赋值是按顺序执行的;

  因此,同一做用域下应避免使用相同变量名;

        <script type="text/javascript">
            console.log(a);//function a(){console.log('我是函数');}  函数名和变量名相同,函数名会覆盖变量名
            
            var a = 'hello world!';
            function a(){
                console.log('我是函数');
            };
            
            console.log(a);//hello world!
            a();//TypeError: a is not a function  类型错误
        </script>

  上面代码中,a的值最后是字符串,缘由就在于:

  1. 变量的赋值是按顺序执行的;
  2. 函数声明提高会把表达式也提早;
  3. 相同变量名,后面的赋值会覆盖前面的;

  上面的代码至关于:

        <script type="text/javascript">
            console.log(a);//function a(){console.log('我是函数');}  函数名和变量名相同,函数名会覆盖变量名
            
            function a(){
                console.log('我是函数');
            };
            var a = 'hello world!';
            
            console.log(a);//hello world!
            a();//TypeError: a is not a function  类型错误
        </script>

 

有一道经典面试题:

        <script type="text/javascript">
            b = c;
            console.log(b); 
            console.log(c);  
            b();
            console.log(a);    
            function c() {
                a = 1, b = 2, c = 3; 
            };
        </script>

  函数c还没运行,因此里面的变量是不知道的,可是函数c已经声明提早了,又由于第一行b=c,因此b和c都是函数c ;

  函数c运行以后,里面的变量a,b,c被发现是全局变量,而且给它们赋值,因此外部能找到a=1,而且你还会发现b=2; c=3;

  若是函数c未曾运行过,那么是找不到它们的 ;

 

这道题换一下:

        <script type="text/javascript">
            b = function c() {
                a = 1, b = 2, c = 3;
            };
            
            b();
            console.log(a); 
            console.log(b); 
            console.log(c); 
        </script>

  函数 b 运行以后,发现了里面的全局变量,而且给它们赋值 a=1; b=2;

  可是有个特殊的变量 c,c 是函数表达式的名字,函数的名字,规范说明函数名不能被改变,因此这里设置 c=3 实际上是对函数名赋值,是无效的,也不会被当作全局变量;并且函数表达式的标识符只有内部做用域能够找到,因此外部是找不到的,报错 ReferenceError: c is not defined 引用错误;

相关文章
相关标签/搜索