任务17-函数和作用域


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

函数声明

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

printName();

函数表达式

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

函数声明:函数调用可以发生在函数声明之前,例如下面这种情况下不会报错

printName();//输出console.log('1')
function printName(){
    console.log('1');
}
//正常,因为‘提升’了函数声明,函数调用可在函数声明之前

函数表达式:函数调用只能在函数表达式后面,不能再函数表达式前面,例如

printName();//浏览器提示Uncaught TypeError: printName is not a function(…)
var printName = function(){
    console.log('1');
};
//报错,变量printName还未保存对函数的引用,函数调用必须在函数表达式之后

浏览器提示Uncaught TypeError: printName is not a function(…).原因:类似变量提升,函数作为一个变量赋值给printName,等价于

var printName;//此时printName为undefined
printName();
printName = function(){
    console.log('1');
};

所以函数声明和函数表达式不同之处在于,

一、Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,
二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用

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

变量的声明前置

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

我们写了一个赋值语句
var a = 2;

实际上执行过程是解释器在未执行的时候先解析出变量声明,然后给他初始值 undefined ,然后才逐句执行程序

var a;
a = 2;

这样看起来没什么区别,但是在多语句的情况下会有差别,我们知道一个变量如果不存在我们就使用会报错

console.log(xxx); // Uncaught ReferenceError: xxx is not defined

我们在使用一个变量之前必须声明变量,但是由于变量提升,我们如果声明了变量,即使在声明语句前使用也是可以的,只不过其值是初始值 undefined

var xxx = 2;```

函数的声明前置
>和变量的声明会前置一样,函数声明同样会前置,如果我们使用函数表达式那么规则和变量一样

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



#### 3.arguments 是什么 (*)
>在函数内部,可以使用 arguments 对象获取到该函数的所有传入参数

function printPersonInfo(name, age, sex){
console.log('name : '+name);
console.log('age : '+age);
console.log('sex : ' +sex);
console.log(arguments);
}

printPersonInfo('liu', 21, 'boy')

name : liu
age : 22
sex : boy
["liu", 22, "boy"]

printPersonInfo('liu', 21, 'boy')

name : liu
age : boy
sex : undefined
["liu", "boy"]


#### 4.函数的重载怎样实现 (**)
>重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载

>在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的,这是不是意味着JavaScript不能通过重载功能实现一个函数,参数不同功能不同呢?

在JavaScript中,函数调用没必要把所有参数都传入,只要你函数体内做好处理就行,但前提是传的参数永远被当做前几个

function printPeopleInfo(name, age, sex){
if(name){
console.log(name);
}

if(age){
    console.log(age);
}

if(sex){
    console.log(sex);
}

}

printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');


#### 5.立即执行函数表达式是什么?有什么作用 (***)
立即执行函数表达式```( function(){…} )()和( function (){…} () )```

var fnName=function(){
alert('Hello World');
}();
//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数
function fnName(){
alert('Hello World');
}();
//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用
function(){
console.log('Hello World');
}();
//语法错误,虽然匿名函数属于函数表达式,但是未进行赋值操作,
//所以javascript引擎将开头的function关键字当做函数声明,报错:要求需要一个函数名

>在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明

(function(a){
console.log(a); //firebug输出123,使用()运算符
})(123);

(function(a){
console.log(a); //firebug输出1234,使用()运算符
}(1234));

!function(a){
console.log(a); //firebug输出12345,使用!运算符
}(12345);

+function(a){
console.log(a); //firebug输出123456,使用+运算符
}(123456);

-function(a){
console.log(a); //firebug输出1234567,使用-运算符
}(1234567);

var fn=function(a){
console.log(a); //firebug输出12345678,使用=运算符
}(12345678)

>可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。

>加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。

>不过这样的写法有什么用呢?

>javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

>JQuery使用的就是这种方法,将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。

参考http://dengo.org/archives/1004

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

>作用域是针对变量的,比如我们创建了一个函数,函数里面又包含了一个函数,那么现在就有三个作用域
  全局作用域==>函数1作用域==>函数2作用域

作用域的特点就是,先在自己的变量范围中查找,如果找不到,就会沿着作用域往上找。
如:

var a = 1;
function b(){
var a = 2;
function c(){
var a = 3;
console.log(a);
}
c();
}
b();


最后打印出来的是3,因为执行函数c()的时候它在自己的范围内找到了变量a所以就不会越上继续查找,如果在函数c()中没有找到则会继续向上找,一直会找到全局变量a,这个查找的过程就叫作用域链。

 
>不知道你有没有疑问,函数c为什么可以在函数b中查找变量a,因为函数c是在函数b中创建的,也就是说函数c的作用域包括了函数b的作用域
,当然也包括了全局作用域,但是函数b不能向函数c中查找变量,因为
作用域只会向上查找
参考:
http://www.cnblogs.com/pssp/p/5204324.html
http://www.cnblogs.com/dolphinX/p/3280876.html

### 代码
##### 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 sum=0;
for (var i = 0; i < arguments.length; i++) {
sum+=arguments[i]*arguments[i];
}
return sum;
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10

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

console.log(a); //undefined
var a = 1;
console.log(b); //报错Uncaught ReferenceError: b is not defined(…)

a输出undefined是因为变量提升,b没有定义,类似如下代码

var a;
console.log(a); //undefined
a = 1;
console.log(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);
};

Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,

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

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

当在同一个作用域内定义了名字相同的变量和方法的话,无论其顺序如何,变量的赋值会覆盖方法的赋值

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

function fn(fn2){
console.log(fn2);//1
var fn2 = 3;
console.log(fn2);//2
console.log(fn);//3
function fn2(){
console.log('fnnn2');
}//4
}
fn(10);

输出

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

在函数体内```console.log(fn2);//1```与

function fn2(){
console.log('fnnn2');
}//4

在同一作用于内,在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,因此

console.log(fn2);//1

输出

function fn2(){
console.log('fnnn2');
}

然后```var fn2=3;```覆盖函数声明,下面```console.log(fn2);```
输出3

console.log(fn);

输出函数fn自身
##### 7.如下代码的输出?为什么 (难度***)

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

输出:报错fn不是一个函数

首先,这种情况

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

如果改为

var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn());//输出:报错fn不是一个函数

所以console.log(fn());无论传不传参数都会报错

##### 8..如下代码的输出?为什么 (难度**)
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
    var j = 100;
}
console.log(i);
console.log(j);
输出结果

undefined
undefined
10
100

原因:
//作用域
console.log(j);//循环体内变量提升undefined
console.log(i);//循环体内变量提升undefined
for(var i=0; i<10; i++){
    var j = 100;
}//循环结束i=10;j=100;
console.log(i);//10
console.log(j);//100
for(var i=0; i<10; i++){
        var j = 100;
    }
for循环相当于
var i=0;
if(0<10){ var j = 100;}
i+=1;//i=1
if(1<10){ var j = 100;}
i+=1;//i=2
if(2<10){ var j = 100;}
i+=1;//i=3
.......................
if(9<10){ var j = 100;}
i+=1;//i=10
if(10<10){ var j = 100;}

理解js语句是一行一行执行的就很好理解

##### 9.如下代码的输出?为什么 (难度****)
fn();//1.
var i = 10;
var fn = 20;
console.log(i);//2.
function fn(){
    console.log(i);
    var i = 99;
    fn2();
    console.log(i);
    function fn2(){
        i = 100;
    }
}
预想输出

>fn()//1.

undefined
100
99

 console.log(i);//2

10

实际输出结果

fn()//1.输出
undefined
100
console.log(i);//2.输出
10

分析:

function fn(){
console.log(i);//变量提升i为undefined
var i = 99;
fn2();//函数执行但不返回结果
console.log(i);//执行fn2();后i值变为100
function fn2(){
i = 100;
}

console.log(i);//2.输出
10//作用域原因,函数内的i不影响外部的i

##### 10.如下代码的输出?为什么 (难度*****)
var say = 0;
(function say(n){
    console.log(n);
    if(n<3) return;
    say(n-1);
}( 10 ));
console.log(say);
自执行函数输出结果为
10 9 8 7 6 5 4 3 2 
当n=2时跳出自执行
console.log(say);输出结果为0
自己没做对,不知道自执行函数运行机制,根据结果分析出来的,自执行函数只运行自己不影响外部任何变量,运行完结束返回结果,拥有自己独立的作用域,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量
例如

var say = 0;
var i=4;
(function say(n){
console.log(n+i);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);

输出结果为14 13 12 11 10 9 8 7 6 0
而

var say = 0;
(function say(n){
var i=4;
console.log(n+i);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
console.log(i);

输出结果为
14 13 12 11 10 9 8 7 6//自执行函数输出结果console.log(n+i);
 0// console.log(say);输出结果
Uncaught ReferenceError: i is not defined// console.log(i);输出结果

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

推荐阅读更多精彩内容