任务17-函数

问答:

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

在日常的任务中,JavaScript主要使用下面两种方式创建函数:

  • 函数声明:
function sayName(){
   console.log('hunger');
}
sayName();
  • 函数表达式
var sayName = function(){
   console.log('hunger');
};

区别:
a. 函数声明必须以function开头,否则,则为表达式。
b. 函数表达式在语句的结尾要加上分号,表示语句结束,而函数声明则不需要在结尾加上分号。
c. 函数声明在JS解析的时候会进行函数提升,即在同一个作用域内,不管函数声明在哪里创建,它会提升至作用域顶部。


函数声明示例

但是函数表达式不会被提升,它的表现和声明变量提升的规则相同,也就是说,函数名(这里指var后面定义的变量名)会声明前置,但是函数表达式的赋值没有被提前,操作还是在原来的位置。

函数表达式示例

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

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

上面运行的代码会报错,是因为我们没有声明变量xxx。
接下来我们将声明变量xxx


我们发现,变量是在语句调用之后定义的,但是结果并没有报错。这就是变量声明前置的作用。JS解析器会把当前作用域内声明的所有变量和函数都放到作用域的开始。但是对于变量来说,它只是把变量的声明提到了作用域的开始,而变量的赋值仍然按照顺序执行。那么没有被赋值的变量会自动赋值为undefined,所以此处运行的结果为初始值undefined。它等同于如下代码:

var xxx;
console.log(xxx);
xxx = 2;

再来看看如下的列子:


我们先声明一个全局变量xxx,然后再函数内部定义一个局部变量。我们希望是第一次打印出来的结果是全局范围内定义的xxx变量,第二次打印出来的是局部变量xxx的值。但是输出结果并没有跟我们设想的一样,原因就是定义的局部变量在其作用域内声明提前了。故第一次打印出来的是没有赋值的undefined,第二次打印出12。它等同于如下代码:

   xxx = 2;
(function(){
         var xxx;
         console.log(xxx);
         xxx = 12;
         console.log(xxx);
})();
  • 函数的声明前置:和变量的声明会前置一样,函数声明同样会前置,如果我们使用函数表达式那么规则和变量一样;如果我们使用函数声明的方式,那么即使函数写在最后也可以在前面语句调用,前提是函数声明部分已经被下载到本地。
  • 函数表达式:

    在上面代码中,变量sayAge被前置了,但是它的赋值并没有被提前,这样就印证了函数表达式的提前和变量提前是一回事了。上面的代码等同于:

var sayAge;
sayAge(10);
sayAge = function(age){
      console.log(age);
}
  • 函数声明:


    函数声明并不仅仅是函数名被提前了,整个函数的定义都被提前了。它等同于如下代码:

function fn(){
  console.log('1');
}
fn();

总结:

  • 变量声明会提前到作用域的顶部,而赋值会被保留在原地,仍然按照次序执行。
  • 函数表达式是变量前置了,但是赋值没有提前。和变量提升一样。
  • 函数声明整个会被前置到变量声明的后面,即使函数写在最后面也可以在前面语句调用。
    那么,我们在分析代码的时候,可以把变量和函数声明放在作用域的顶部,这样分析出来的结果一般不容易出错。

3. arguments 是什么 (*)

arguments是JS里的一个内置对象,它只在函数内部有效,可以在函数内部通过使用arguments对象来获取函数的所有参数。这个对象为传递给函数的每个参数建立一个条目,条目的索引从0开始。参数也可以重新被赋值。

arguments对象就像数组(array),但是它却不是真正的数组,除了length,它没有数组所特有的属性和方法。
当一个函数的参数数量比它显示声明参数数量更多时候,我们就可以使用arguments对象获取到该函数的所有传入参数。
如:

4. 函数的重载怎样实现 (**)

重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载。
在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的。
在JavaScript中可以通过arguments来实现函数的重载,如:

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

立即执行函数表达式(Immediately-Invoked Function Expression),是将函数定义放在一个圆括号里,让JavaScript引擎将其理解为一个表达式,再在函数的定义后面加一个(),以达到定义函数后立即调用该函数的效果。有下面两种写法:
<pre>
(function(){ /code/ }());
(function(){ /code/ })();</pre>

如:

作用:

  • 封装大量的工作而不会在背后遗留任何全局变量。
  • 定义的所有变量都会成为立即执行函数的局部变量,所以不用担心这些临时变量会污染全局空间。
  • 可以将独立的功能封装在自包含模块中。
    注意:立即执行函数通常作为一个单独模块使用。一般没有问题,但是,建议在自己写的立即执行函数前加分号,这样可以有效地与前面代码进行隔离。否则,可能出现意想不到的错误。

6. 什么是函数的作用域链 (****)

  • 作用域:作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
  • 全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
    a. 最外层函数和在最外面定义的变量拥有全局作用域
var a = 2;
function fn(){
   var b;
   var c;
   function fn2(){
      console.log(a);
      console.log(b);
    }
    b = 3;
    fn2();
    c = 4;
} 
fn();
//在这段代码中,变量a 和函数fn时拥有全局作用域,而在函数里面的变量和函数并不拥有全局作用域

b. 所有未定义直接赋值的变量自动声明为拥有全局作用域

function fn(){
   var a = 2;
   b = 3;
   console.log(a);
}//变量b用于全局作用域,而变量a则不是。

c. 所有window对象的属性拥有全局作用域

  • 局部作用域:和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的如函数内部,所以在一些地方也会看到有人把这种作用域称为函数作用域。
function fn(){
   var a = 2;
   function fn2(){
      console.log(a);
    }
  fn2();
} //在此代码中变量a和函数fn2用于局部作用域
  • 作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
    参考:
    JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

代码:

1. 以下代码输出什么? (难度**)

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('hunger', 28, '男'); 
    getInfo('hunger', 28); 
    getInfo('男');

输出结果:

//getInfo('hunger', 28, '男'); 
name:hunger
age:28
sex:男
["hunger", 28, 男]
name valley

//getInfo('hunger', 28); 
name:hunger
age:28
sex:undefined
["hunger", 28]
name valley

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

2. 写一个函数,返回参数的平方和?如 (难度**)

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

3. 如下代码的输出?为什么 (难度*)

console.log(a); //输出undefined,因为变量提升了,但是没有赋值
 var a = 1; 
 console.log(b);//输出b is not defined,因为变量b没有被申明`

4. 如下代码的输出?为什么 (难度*)

sayName('world'); //输出结果为hello world
sayAge(10); //输出结果为Uncaught TypeError: sayAge is not a function
function sayName(name){ 
      console.log('hello ', name); 
} 
var sayAge = function(age){ 
      console.log(age); 
};

第一行代码由于函数声明提升,整个sayName函数提到代码头部,则在执行sayName(“world”)时输出正常结果;第二航代码由于sayAge是函数表达式,则JS引擎在解析代码时,只是把var sayAge提到代码的头部,并未把整个函数部分提到代码头部,所以在执行时会报错。

5. 如下代码的输出?为什么 (难度**)

function fn(){} 
var fn = 3; 
console.log(fn);//输出3,在同一个作用域内定义了名字相同的变量和方法的话,无论其顺序如何,变量的赋值会覆盖方法的赋值。

6. 如下代码的输出?为什么 (难度***)

function fn(fn2){ 
     console.log(fn2); 
     var fn2 = 3; 
     console.log(fn2); 
     console.log(fn); 
     function fn2(){ 
           console.log('fnnn2'); 
     } 

}
fn(10);

上面代码相当于:

function fn(fn2){
    var fn2;//变量声明提升
    function fn2(){
         console.log('fnnn2');
   }//函数声明提升
   console.log(fn2);//当函数执行命令有冲突时,函数载入的顺序是变量>函数>参数,此时肯定是输出函数。
  fn2=3;
  console.log(fn2);//此时fn2被赋值3,因为在同一作用域内,定义了同一个名字的变量和方法时,无论顺序如何,变量的赋值会覆盖方法的赋值。
  console.log(fn);//当函数执行命令有冲突时,函数载入的顺序是变量>函数>参数,此时肯定是输出函数。
}
fn(10); 

输出结果为:

function fn2(){
        console.log('fnnn2');
    }
 3
function fn(fn2){
   console.log(fn2);
   var fn2 = 3;
   console.log(fn2);
   console.log(fn);
   function fn2(){
        console.log('fnnn2');
    }
 }

7. 如下代码的输出?为什么 (难度***)

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

上述代码相当于:

var fn;
function fn(fn){
       console.log(fn);
}
fn = 1;
console.log(fn(fn));//输出结果:Uncaught TypeError: fn is not a function(…)。在执行时,正确代码应该是函数(参数),在这个习题中,由于变量命名提升,代码形式为变量(变量),那么交给浏览器去执行时会输出:fn is not a function(…)。

8. 如下代码的输出?为什么 (难度**)

 //作用域 
console.log(j); 
console.log(i); 
for(var i=0; i<10; i++){ 
      var j = 100; 
} 
console.log(i); 
console.log(j);

上述代码相当于:

var i;
 var j;
//变量提升,将其 提到代码头部
 console.log(i);//undefined,此时变量i还未被赋值
 console.log(j);//undefined,此时变量j还未被赋值
 for(var i = 0; i<10; i++){
       var j = 100;
 console.log(i);//10 在for循环执行后,i为10,for循环语句不会前置,其定义的变量自然就是全局变量,所以能够被解析,正常顺序执行并显示。
 console.log(j);`//100 在for循环执行后,j为100。

9. 如下代码的输出?为什么 (难度****)

fn(); 
  var i = 10; 
  var fn = 20; 
  console.log(i); 
  function fn(){ 
       console.log(i); 
       var i = 99; 
       fn2(); 
       console.log(i); 
       function fn2(){ 
            i = 100; 
       } 
 }

上述代码相当于:

var i;
 var fn;
 //变量提升,将var i和var fn提升到代码头部
 function fn(){
     var i;
     function fn2(){
          i = 100;
     }
    console.log(i);//输出undefined,因为变量i声明了但是没有赋值
    i = 99;
    fn2();//执行后i为100,覆盖了i = 99
    console.log(i);//输出100
}
fn();
i = 10;
fn = 20;
console.log(i);//输出为10.
   

10. 如下代码的输出?为什么 (难度*****)

var say = 0; 
 (function say(n){ 
       console.log(n); 
       if(n<3) return; 
       say(n-1); 
 }( 10 )); //输出10,9,8,7,6,5,4,3,2
/*function前后加了圆括号,表示该函数为立即执行函数,因此会马上执行,尾部赋值n=10,得到n-1=9,然后n=9,继续循环,直到n=2时,满足if条件,return终止,不执行n-1;所以最终结果为2*/
 console.log(say);`//输出0
/*因为在该作用域中,变量say已经被赋值了0,在同一个作用域中,变量和方法同名时,无论顺序如何,变量的赋值会覆盖方法的赋值*/

本文版权归本人及饥人谷所有,转载请注明出处

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

推荐阅读更多精彩内容