17.函数

一、函数声明和函数表达式有什么区别?(*)

** 1.ECMAScript里面规定了三种声明函数的方式**
①构造函数
首先函数也是对象的一种,我们可以通过其构造函数,使用new来创建一个函数对象(不推荐使用)

  var printName = new Function("console.log('Byron');"); 

②函数声明
函数声明通过关键字function来声明,关键词后面是函数名,名称后面有个小括号,括号里面放的是函数的参数,最后是一对花括号,函数的代码块就放在这个花括号里面。

function functionName(){
    console.log('abc')
}
functionName();

③函数表达式

  • 省略标识符(函数名)
var printName = function(){}

说明:这种写法是将一个匿名函数赋值给变量。这时这个匿名函数又称函数表达式(Function Expression),因为赋值语句等号后面只能放表达式。

  • 带有标识符
var printName = function functionName(){};

注意:采用函数表达式声明函数时,function命令后面一般不带有函数名。如果带上函数名,该函数名只在函数体内部有效,在函数体外部无效。

Paste_Image.png

2.函数声明和函数表达式比较容易混淆,它们的区别主要有:
①函数声明是以function关键词开始,如果不是,那么它就是函数表达式。
②函数声明最后面一般不写分号,而函数表达式有分号
③函数声明和变量声明都有声明提前的特点,函数声明是函数名称和函数体均提前声明了,可以在声明之前调用它;
但函数表达式的规则和变量的声明提前一样,只是函数变量声明提前了,但是它所赋的值仍然在原来的位置,不能够在函数表达之前调用它,否则就会报错;
例如:函数声明

Paste_Image.png

就相当于
Paste_Image.png

函数表达式
Paste_Image.png

就相当于
Paste_Image.png

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

①变量前置就是把变量的声明提前到当前作用域的最前面,但变量的赋值仍然按照原来的顺序执行,如果变量声明但未被赋值,变量会自动赋值为undefined。
②函数的声明前置有两种情况,一个是使用函数声明,则整个声明都前置,而且会被前置到变量声明的后面;另一个是使用函数表达式,那么规则和变量的声明前置一样。

变量的声明前置
例如:

Paste_Image.png

在上面的代码中,我们首先声明了一个全局变量name,同样的在函数内部声明一个局部变量name。本来是希望第一次打印输出的是全局变量name的值,第二次打印输出局部变量name的值。但是输出并没有和我们设想的一样,原因是函数内定义的局部变量name在其作用域内声明提前了,还与函数作用域的规则有关。
它其实相当于如下的代码:


Paste_Image.png

如果想按原来的设想输出,可以这样写:


Paste_Image.png

函数声明的前置
例如:

Paste_Image.png

就相当于
Paste_Image.png

函数表达式的声明前置

Paste_Image.png

Paste_Image.png

这种情况就报错了,因为函数表达式的声明前置和变量的声明前置是一样的。就相当于


Paste_Image.png

三、arguments 是什么?(*)

①arguments是一个类数组对象,代表传给一个function的参数列表,只在函数内部起作用
arguments的值与函数传入参数有关,与函数定义参数无关
②在函数内部可以使用arguments对象获取到该函数的所有传入参数。这个对象为传递给函数的每个参数建立一个条目,条目的索引号从 0 开始。
③使用arguments能够给实参重新赋值。
④可以用 arguments 能够检测函数的参数个数,使用属性arguments.length 。

Paste_Image.png

作用:函数声明时不用详细写清楚传入的参数的个数和名称,也能使用arguments获取参数
Paste_Image.png

还可以用arguments来改变实参的值
Paste_Image.png

四、函数的重载怎样实现?(**)

①重载是很多面向对象语言实现多态的手段之一,相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载。
②在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的
③javascrip可以通过自身属性去模拟函数重载,可以用arguments来实现函数的重载。

Paste_Image.png

因为js没有重载的概念所以第二个名字相同函数覆盖了第一个函数,所以运行结果就相当于"1+2+undefined",所以结果是NaN。

参数不确定的函数,用arguments来实现函数的重载

Paste_Image.png

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

1.在Javascript中,一对圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。
有时,我们希望能在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。

 function(){ /* code */ }();
 // SyntaxError: Unexpected token

产生这个错误的原因是,function这个关键字既可以当作语句,也可以当作表达式

 // 语句
 function f() {}
 // 表达式
 var f = function f() {}

为了避免解析上的歧义,JavaScript引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式,最简单的处理,就是将其放在一个圆括号里面。

  //正确的写法:
  ( function(){ /* code */ }() );
  // 或者
  ( function(){ /* code */ } )();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称IIFE。
注意:上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个IIFE,可能就会报错。

// 报错
(function(){ /* code */ }())
(function(){ /* code */ }())
//上面代码的两行之间没有分号,JavaScript会将它们连在一起解释,将第二行解释为第一行的参数。

立即执行函数表达式一般不需要给函数命名,因为只需要立刻执行就行。如果要命名,一般是用于递归函数。例如:

var say = 0;
(function say(n){ 
    var i = 20;
    console.log(n);
    console.log('hello hunger' ,i);
    if(i<0) return;
    say(n-1);
})(10);
console.log(say);

demo

2.IIFE的作用
①不必为函数命名,避免了污染全局变量;
②IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量

参考立即调用的函数表达式(IIFE)

六、什么是函数的作用域链 (****)

1.作用域:
①作用域:作用域就是变量与函数的可访问范围,作用域控制着变量与函数的可见性和生命周期
②JS中变量的作用域分为全局作用域和局部作用域。只有函数才有自己的作用域,局部作用域就是一个函数内定义的变量函数外不可以访问,全局作用域就是在代码中任何地方都能访问到。
③没有在函数内声明或者声明的时候没有带var就是全局变量,拥有全局作用域;window对象的所有属性拥有全局作用域,在代码任何地方都可以访问。
④函数内部声明并且以var修饰的变量就是局部变量,只能在函数体内使用;函数的参数虽然没有使用var但仍然是局部变量。
2.作用域链:
作用域链是函数内部上下文所有变量对象(包括父变量对象)的列表,用来变量查询。在代码执行的过程中,所用到的变量会在当前作用域中进行寻找,如果找不到,就会往沿着作用域链向上一级进行寻找,一直到全局作用域为止。

参考
作用域链1
作用域链2

作用域链3

七、代码

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('男');
Paste_Image.png

打印结果:

Paste_Image.png

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

function sumOfSquares(){
 } 
sumOfSquares(2,3,4); // 29 
sumOfSquares(1,3); // 10

可以用利用arguments对象求平方和

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

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

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

根据变量提升可以写成

var a;  //undefined
console.log(a);  //打印为undefined
a = 1;  
console(b);  //没有声明变量b,报错
Paste_Image.png

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

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

上面的代码相当于

var sayAge;  //undefined
function sayName(name){   //跳过
      console.log('hello ', name);
} 
sayName('world');   //hello world
sayAge(10);   //就相当于undefined(10),肯定会报错
sayAge = function(age){  //报错
     console.log(age); 
 };
Paste_Image.png

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

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

上面的代码相当于

var fn;  
function fn(){}  //声明提升,函数声明在变量的后面,函数覆盖变量,
                        fn变为函数
fn = 3;  //变量的赋值覆盖函数的声明
console.log(fn);  //3

输出结果

Paste_Image.png

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(){ //函数声明覆盖变量的声明,fn2变为函数
        console.log('fnnn2'); 
    } 
    console.log(fn2);  //打印上面函数fn2
    fn2 = 3;  //赋值语句覆盖函数声明,fn变为数字3
    console.log(fn2); //3 
    console.log(fn);   //fn是最外层的函数名,打印最外层的函数fn
  
} 
fn(10);

输出的结果

Paste_Image.png

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

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

上面的代码相当于

 var fn ; 
 function fn(fn){   //函数声明覆盖变量声明,此时fn是函数
    console.log(fn); 
}
fn = 1; //变量的赋值覆盖函数声明,fn变为数字
console.log(fn(fn)); //相当于1(1);所以会报错

输出结果

Paste_Image.png

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

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

上面的代码相当于

var i; 
var j;
cosole.log(i);  //变量提前,所以打印是undefined
for(i=0;i<10;i++){  //只有函数才有作用域,for循环没有作用域,所以里面的变量是全局变量
  j = 100;
 }
 console.log(i);  //for里面i循环变成了10,所以打印是10
 console.log(j);  //上面的j是100,所以打印是100

输出结果

Paste_Image.png

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;  //跳过
function fn(){  //跳过,函数声明不执行,函数调用才执行
    var i;  //函数内声明前置 ,i为undefined
    function fn2(){  //跳过,函数只声明所以不会执行
      i = 100; 
    } 
    console.log(i); //i会在自己的作用域里找i,var i;为undefined,所以                                            
                               打印出undefined
    i = 99; 
    fn2();   //执行fn2函数,i是全局变量,在变量声明的后面,它会把前
                    面的i设为100,所以var i;的值是100
  console.log(i)
}
fn(); //然后执行函数fn,
i = 10;
fn = 20;  
console.log(i)  //这个打印的是10

输出结果

Paste_Image.png

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

var say = 0; // say等于0
(function say(n){ 
    console.log(n);//立即执行函数,没有声明前置,传入参数10,这里打印的是10
    if(n<3) return;  
    say(n-1);  //if循环n从10开始依次递减,到n<3时也就是n=2退出循环,所以打印的n是10到2
}( 10 )); 
console.log(say); //这个就是自己作用域里面的值var say=0,是0
Paste_Image.png

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

推荐阅读更多精彩内容

  • 问答: 1. 函数声明和函数表达式有什么区别 (*) 在日常的任务中,JavaScript主要使用下面两种方式创建...
    小木子2016阅读 313评论 0 0
  • 问答题 函数声明和函数表达式有什么区别 (*)答://函数声明function hello(){ conso...
    饥人谷_桶饭阅读 241评论 0 0
  • 1.函数声明和函数表达式有什么区别 (*) 函数声明 函数表达式 函数声明:函数调用可以发生在函数声明之前,例如下...
    TimeLesser阅读 386评论 4 4
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,114评论 0 13
  • 1.函数声明和函数表达式的区别? 在ECMAScript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者...
    GarenWang阅读 524评论 0 0