函数&声明前置&作用域链

1. 函数声明和函数表达式有什么区别

JavaScript函数是指一个特定代码块,可能包含多条语句,可以通过名字来供其它语句调用以执行函数包含的代码语句。
ECMAScript规定了三种声明函数方式:

  • 构造函数(不推荐):
    首先函数也是对象的一种,我们可以通过其构造函数,使用new来创建一个函数对象。
    var sayHello = new Function("console.log('hello world');");

  • 函数声明:
    使用function关键字可以声明一个函数。

  //函数声明
  function sayHello(){
    console.log('hello');
  }
  //函数调用
  sayHello();
  • 函数表达式:
var sayHello = function(){
    console.log('hello');
  }
//函数调用
sayHello();

区别:

函数声明:function functionName(){} ; //函数声明会提前,声明不必放到调用的前面。
函数表达式:var fn = function(){} ; //函数表达式可以省略函数名,声明必须放到调用的前面。

2. 什么是变量的声明前置?什么是函数的声明前置?

  • 变量声明前置
    JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部。
    例如:
console.log(a);
var a = 3;
console.log(a); 

经过变量的声明前置,可以理解为:

var a;
console.log(a);   // undefined
a = 3 ;
console.log(a);  // 3

注意:
var重复声明一个已经存在的变量,原变量值不变。
不写var会声明一个全局的变量,在编程中应该要避免的,即使真的需要全局变量,也应该在最外层作用域使用var声明。

  • 函数声明前置
    JavaScript引擎将函数名视同变量名,所以采用function声明函数时,整个函数会像var声明变量一样,被提升到代码头部。所以,形如下面的代码是不会报错的。
sayHello();
function sayHello(){
  console.log('hello');
}

声明前置后,即:

function sayHello(){
  console.log('hello');
}
sayHello();  //'hello'

3. arguments 是什么?

arguments是一个类数组对象。代表传给一个function的参数列表。
可以在函数内部通过使用 arguments 对象来获取函数的所有参数。这个对象为传递给函数的每个参数建立一个条目,条目的索引号从0开始:arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

function printPersonInfo(name, age, sex){
    //console.log(name);
    //console.log(age);
    //console.log(sex);
    console.log(arguments);
}

4. 函数的"重载"怎样实现的?

  • 重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载。
  • 在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的,但可以在函数体针对不同的参数调用执行相应的逻辑。
function userInfo(name,age,sex){
      if(name){
            console.log(name);
      }
      if(age){
            console.log(age);
      }
      if(sex){
            console.log(sex);
      }
}
userInfo("andy",22);    //参数依次传递值,缺少的参数返回的是undefined
userInfo("andrea",20,"female");

5. 立即执行函数表达式是什么?有什么作用?

立即执行函数表达式(Immediately-Invoked Function Expression),简称IIFE。表示定义函数之后,立即调用该函数。

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

其他写法:

    (function fn1() {});
  • 当圆括号出现在匿名函数的末尾想要调用函数时,它会默认将函数当成是函数声明。
  • 当圆括号包裹函数时,它会默认将函数作为表达式去解析,而不是函数声明。
  • 作用:隔离作用域。

6. 求n!,用递归来实现

function factor(n){
     if(n === 1 || n === 0){
          return 1;
     }
     if(n > 1){
          return n*factor(n-1);
     }else{
          console.log('Input Error');
     }  
}
factor(5);   //120

7. 以下代码输出什么?

function getInfo(name, age, sex){
      console.log('name:',name)
      console.log('age:', age)
      console.log('sex:', sex)
      console.log(arguments)
      arguments[0] = 'valley'
      console.log('name', name)
}
getInfo('谷哥', 20, '女');    
getInfo('小谷', 30);
getInfo('男');

答案如下:

getInfo('谷哥', 20, '女');
       name : 谷哥
       age : 20
       sex : 女
       [name:谷哥, age:20,sex:女]
       name valley
       name valley

getInfo('小谷', 30);
       name:小谷
       age:30
       sex:undefined
       [name:小谷,age:30,sex:undefined]
       name valley
       name valley

getInfo('男');
       name:男
       age:undefined
       sex:undefined
       [name:男,age:undefined,sex:undefined]
       name valley
       name valley

8. 写一个函数,返回参数的平方和

function sumOfSquares(){
      var sum = 0;
      for(var i = 0 ; i < arguments.length ; i++){
           sum = sum + arguments[i] * arguments[i] ;
      }
     return sum ;
}
var result = sumOfSquares(2,3,4);
var result2 = sumOfSquares(1,3);
console.log(result);       //29
console.log(result2);     //10

9. 如下代码的输出是什么?为什么?

console.log(a);
var a = 1;
console.log(b);

变量声明前置后,即:

var a
console.log(a) ; //undefined
a = 1
console.log(b) ; // b is not defined 没有声明变量b

10. 如下代码的输出是什么?为什么?

sayName('world');
sayAge(10);
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};

函数声明前置后,而函数表达式则不会前置,所以结果即:

sayAge(10);  // sayAge is not a function(报错)。 (因为函数表达式不会被前置)
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};
sayName('world');  // hello world

11. 如下代码的输出是什么?写出作用域链查找过程伪代码

var x = 10;
bar() ;
function foo() {
     console.log(x);
}
function bar(){
     var x = 30;
     foo();
}

声明提前,如下:

var x;
function foo() {
     console.log(x);
}
function bar(){
     var x ;
     x = 30;
     foo();
}
x = 10 ;
bar() ;

作用域链代码,如下:

1. 
   globalContext = {
         AO {
               x : 10
               foo : function
               bar : function
         },
        Scope: null
   }
   foo.[[scope]] = globalContext.AO
   bar.[[scope]] = globalContext.AO
  全局变量 x = 10
2. 调用函数bar() 
   barContext = {
        AO:{
              x : 30
              foo : function
        },
        Scope:bar.[[scope]] = globalContext.AO
    }
    函数内局部变量 x = 30

3. 调用函数foo() 
   fooContext = {
        AO:{ };
   },
   Scope:foo.[[scope]] = globalContext.AO
   //因为作用域链的关系, 全局变量 x = 10 , 所以 console.log(x) 的结果也是 10 。

12. 如下代码的输出是什么?写出作用域链查找过程伪代码

var x = 10;
bar() ;
function bar(){
     var x = 30;
     function foo(){
          console.log(x) ;
     }
     foo();
}

声明提前,如下:

var x;
function bar(){
     var x;
     function foo(){
          console.log(x);
     }
     x = 30;
     foo();
}
x = 10 ;
bar() ;

作用域链代码,如下:

1.
   globalContext = {
        AO:{
              x : 10;
              bar : function
        },
        Scope: null
   }
   bar.[[scope]] = globalContext.AO
2. 调用函数bar()
   barContext = {
        AO:{
              x : 30;
             foo: function
        },
       Scope:bar.[[scope]] = globalContext.AO
   }
   foo.[[scope]] = barContext.AO
3. 调用函数foo()
   fooContext = {
        AO:{},
   }
   foo.[[scope]] = barContext.AO
   //调用foo时会在barContext.AO中找到x = 30。

13. 如下代码的输出是什么?写出作用域链查找过程伪代码

var x = 10;
bar() ;
function bar(){
     var x = 30;
     (function (){
           console.log(x)
     })();
}

声明提前,如下:

var x ;
function bar(){
     var x = 30 ;
     (function (){
           console.log(x)
     })();
}
x = 10 ;
bar() ;

作用域链代码,如下:

1.
    globalContext = {
        AO:{
            x:10
            bar:function
        },
        Scope:null
    }
    bar.[[scope]] = globalContext.AO
2.调用bar()
    barContext = {
        AO:{
            x:30
            function
        },
        Scope:bar.[[scope]] = globalContext.AO
    }
    function.[[scope]] = barContext.AO
3.调用立即执行函数
    functionContext = {
        AO:{},
        Scope:function.[[scope]] = barContext.AO
    }
调用bar函数时,由于它的活动对象中 x=30,所以当自执行函数执行时,会先在它自己的AO中查找,找不到再向bar()函数的AO中查找,所以结果为30。

14. 如下代码的输出是什么?写出作用域链查找过程伪代码

var a = 1;

function fn(){
     console.log(a);
     var a = 5;
     console.log(a);
     a++ ;
     var a;
     fn3();
     fn2();
     console.log(a);

      function fn2(){
            console.log(a);
            a = 20;
      }
}

function fn3(){
  console.log(a)
  a = 200
}

fn();
console.log(a);

作用域链代码,如下:

1.
    globalContext = {
        AO:{
            a:1
            fn:function
            fn3:function
        },
        Scope:null
    }
    fn.[[scope]] = globalContext.AO
    fn3.[[scope]] = globalContext.AO

2.调用fn()
    fnContext = {
        AO:{
            a:undefined
            fn2:function
        },
        Scope:fn.[[scope]]  = globalContext.AO
    }
    fn2.[[scope]] = fnContext.AO

3.
    fn3Context = {
        AO:{
            a:200
        },
        Scope:fn3Context.[[scope]] = globalContext.AO
    }
    fn2ConText = {
        AO:{
            a:20
        },
        Scope:fn2ConText.[[scope]] = fnContext.AO
    }
    开始执行
    console.log(a);    //undefined 打印
    var a = 5;            //fnContext中a变成5
    console.log(a);    //5
    a++ ;                  //fnContext.AO中a变为6

    调用fn3()
    fn3()中
    console.log(a);    //globalContext.AO中的a值为1,打印
    a = 200;              //globalContext.AO中的a变为200

    调用fn2()
    console.log(a);   //fnContext.AO中的a值为6,打印
    a = 20;                //fnContext.AO中的a变为20

    继续执行fn()
    console.log(a);    //fnContext.AO中的a值为20,打印

    fn()结束
    console.log(a);     //globalContext.AO中a值为200,打印

输出的结果: undefined  5  1  6  20  200
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容

  • 1.函数声明和函数表达式有什么区别 (*) 区别: 函数声明后面的分号可加可不加,不加也不影响接下来语句的执行,但...
    Sheldon_Yee阅读 391评论 0 1
  • 1. 函数声明和函数表达式有什么区别 使用function关键字声明一个函数时,声明不必放到调用的前面。//函数声...
    _李祺阅读 257评论 0 0
  • 函数声明和函数表达式有什么区别? 函数声明和函数表达式是EMACScript规定的两种不同的声明函数的方法。1.函...
    LeeoZz阅读 339评论 0 1
  • 声明前置和作用域也是JS 部分面试常考点 1.函数声明和函数表达式有什么区别 函数声明:使用function关键字...
    湖衣阅读 193评论 0 0
  • 创造意义 想举我的例子:我从三月中旬开始兼职,一周有四五天都在兼职,都是体力活,经常站着脚疼,有时候真的很累,但是...
    一只正在成长的狮子阅读 155评论 0 1