问答部分
一、函数声明和函数表达式有什么区别?
- 二者表示函数的方式不一样,如下
- 函数声明(函数名称不可少)
function funName(){
statement;
}
- 函数表达式(函数名称可以没有,末尾有一个分号)
var funName = function(){
statement;
};
- 二者在函数名提升的时候不一样(函数名提升是指JS引擎在解析代码时,整个函数会像变量提升一样提到代码头部,但此时并未执行)
- 对于函数声明,如下代码
printName("jirengu");
function printName(name){
console.log(name);
}
此时显示的结果如下图所示
此时执行调用函数时不会报错,因为函数名的提升,整个函数被提升到代码头部
- 对于函数表达式,如下代码
printName("jirengu");
var printName = function(name){
console.log(name);
};
此时显示结果如下图所示
这个时候会报错是因为此时JS引擎是如下处理代码的
var printName;
printName("jirengu");
printName = function(name){
console.log(name);
};
以上代码中,第一行表示函数变量声明提前,但还未被赋值,等于undefined;所以在执行第二行时则会发生错误;即函数表达式只是函数声明提前,但是函数部分并未提前
二、什么是变量的声明前置?什么是函数的声明前置?
- 变量的声明前置是指Javascript引擎解析代码,获取所有被声明的代码变量,然后再一行一行的执行。这样造成的结果是所有变量的声明语句,都将被提升到代码头部,如下情况
console.log(a);
var a = 1;
此时并不会报错,结果如下图所示
JS引擎在解析代码时相当于以下过程
var a;//变量声明提升到代码头部
console.log(a);
a = 1;
- 函数声明前置指JS引擎在解析代码时,整个函数会像变量提升一样提到代码头部,如下情况
printNumber(12);
function printNumber(num){
console.log(num);
}
此时不会报错,显示结果如下图所示
此过程相当于一下代码
function printNumber(num){
console.log(num);
}//函数声明前置会让整个函数提到代码头部
printNumber(12);
还有函数表达式的前置,见问题1
三、arguments 是什么?
- arguments~函数运行时,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据即为参数,比如如下代码
function getSquare(x){
return x * x;
}
console.log(getSquare(4));//16
其中x即为getSquare函数的参数,每次运行getSquare函数时,都要提供x值,否则不会得到结果
- arguments不是必须的,JS中允许省略arguments,被省略的参数的值就变为undefined
- 在函数体内可以通过arguments对象来访问这个数组,从而获取传递给函数的每一个参数,如下代码
function visitArg(){
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
visitArg("ji","ren","gu");
显示结果如下图
- 通过访问arguments对象的length属性可以获知有多少个参数传递给了函数,如下
function howmanyArg(){
console.log(arguments.length);
}
howmanyArg(1,2,3,4);
howmanyArg(12);
howmanyArg();
howmanyArg("hello",4,false);
输出结果为下图所示
四、函数的重载怎样实现?
- 在一些语言中(如Java)中,相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载。但是在Javascript中没有函数的重载
- 在Javascript中可以通过arguments来实现函数的重载,如下代码
function sum(){
var x = 0;
for(var i = 0;i < arguments.length;i++){
x += arguments[i];
}
return x;
}
console.log(sum(1,2,3));//6
五、立即执行函数表达式是什么?有什么作用 ?
- 立即执行函数模式(没有声明前置)是一种语法,可以让你的函数在定义后立即被执行,这种模式本质上就是函数表达式(命名的或者匿名的),在创建后立即执行,其代码格式如下
//在最前最后加括号
(function atOnce(){
console.log("abc");
}());
//在function外面加括号
(function atOnce(){
console.log("abc");
})();
- 立即执行函数表达式的作用:
- 立即执行函数模式被广泛使用,它可以帮你封装大量的工作而不会在背后遗留任何全局变量
- 定义的所有变量都会成员立即执行函数的局部变量,所以你不用担心这些临时变量会污染全局空间
- 这种模式经常被使用在书签工具(bookmarklets)中,因为书签工具在任何页面上运行并且保持全局命名空间干净是非常必要的
- 这种模式也可以让你将独立的功能封装在自包含模块中
更多知识
六、什么是函数的作用域链?
- 函数的作用域链~任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种
- 全局作用域~在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域
- 最外层函数和在最外层函数外面定义的变量拥有全局作用域,如下
var num = 1;
function example(){
var ourAuthor = "jirengu";
function printName(){
console.log(ourAuthor);
}
printName();
}
//在这段代码中,变量num和函数example是拥有全局作用域;
而在函数example里面的变量和函数并不拥有全局作用域
- 所有末定义直接赋值的变量自动声明为拥有全局作用域,如下
function example(){
var a = "jirengu";
b ="quanju";
console.log(a);
}
//在以上代码中,变量b拥有全局作用域;而变量a则不是
- 所有window对象的属性拥有全局作用域
- 局部作用域~和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域
function example(){
var author = "jirengu";
function printName(){
console.log(ourAuthor);
}
printName();
}
//在以上代码中,变量author和函数printName都只拥有局部变量
- 作用域链~在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问(如果变量在该作用域中没有,则它会逐级向上寻找,直至最顶层)
- 以上内容引用自JavaScript 开发进阶:理解 JavaScript 作用域和作用域链
代码部分
一、以下代码输出什么?
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
在chrome上运行如下图所示
二、写一个函数,返回参数的平方和?
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
在chrome中一下效果图
三、如下代码的输出?为什么?
console.log(a);//输出结果为undefined
var a = 1;//输出结果为1
console.log(b);// Uncaught ReferenceError: b is not defined
造成以上结果的原因是变量提升,即JS引擎在解析代码时会把所有的变量声明提到代码头部,再一行一行的执行代码,所以第一行代码输出结果为undefined;而第三行代码是因为b并不是变量,所以会报错;以上代码相当于以下过程
var a;//变量声明提升
console.log(a);
a = 1;
console.log(b);
四、如下代码的输出?为什么?
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
以上代码的输出结果为
//sayName("world");输出结果为hello world
//sayAge(10);输出结果为Uncaught TypeError: sayAge is not a function
第一行代码由于函数声明提升,整个sayName函数提到代码头部,则在执行sayName("world")时输出正常结果;第二行代码由于sayAge是函数表达式,则js引擎在解析代码时,只是把var sayAge提到代码头部,并未把整个函数部分提到代码头部,所以在执行时会报错,以上内容相当于以下过程
function sayName(name){
console.log('hello ', name);
}//函数声明前置将整个函数提到代码头部
var sayAge;//函数表达式只是把var sayAge提到代码头部
sayName('world');//输出结果为hello world
sayAge(10);//sayAge并不是一个函数,则会报错
sayAge = function(age){
console.log(age);
};//将右边的函数赋值给sayAge
五、如下代码的输出?为什么?
function fn(){}
var fn = 3;
console.log(fn);//输出结果为3
由于变量声明前置和函数声明前置,以上代码相当于一下过程
var fn;//变量声明前置,将var fn提到代码头部
function fn(){}//函数声明前置
fn = 3;//将数值3赋值给fn
console.log(fn);//最终输出结果为3
六、如下代码的输出?为什么?
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
fn(10);
最终出现的结果是
//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');
}
}
出现以上结果相当于js引擎在解析代码时做了以下事情
function fn(fn2){
var fn2;//变量声明提升
function fn2(){
console.log('fnnn2');
}//函数声明提升
console.log(fn2);//输出结果为fn2函数
fn = 3;//fn被重新赋值
console.log(fn2);//输出结果为3
console.log(fn);//输出结果为fn函数
}
fn(10);
七、如下代码的输出?为什么?
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
以上代码输出结果为
//console.log(fn(fn));
Uncaught TypeError: fn is not a function
出现以上结果是因为JS引擎在解析代码时做了以下事情
var fn;//变量声明提升
function fn(fn){
console.log(fn);
}//函数声明提升
fn = 1;//把数值1赋值给函数fn
console.log(fn(fn));//会报错,因为fn不是函数,已经被赋值为1
八、如下代码的输出?为什么?
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);
console.log(j);
最终输出结果为
console.log(i);//输出结果为10
console.log(j);//输出结果为100
相当于以下过程
var i;//变量提升,将var i提到代码头部
var j;//变量提升,将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);//在for循环里执行完后,i为10
console.log(j)//在for循环执行后,j被赋值为100
九、如下代码的输出?为什么 ?
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;
}
}
最终输出结果为
undefined
100
10
相当于以下过程
var i;//变量提升,将var i提到代码头部
var fn;//变量提升,将var i提到代码头部
function fn(){
var i;//函数体内部的变量提升
function fn2(){
i = 100;
}//函数体内部的函数声明提升
console.log(i);//undefined,因为此时的i只是声明,但是还未赋值
i = 99;//把99赋值给变量i
fn2();//执行fn2函数,执行完后结果是i的值为100
console.log(i);//100
}//函数声明提升,将整个函数提到代码头部
fn();//执行函数fn,执行完后,得到undefined和100l两个结果
i = 10;//把10赋值给ifn = 20;//把20赋值给fn
console.log(i);//10
十、如下代码的输出?为什么?
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
最终输出结果如下图
- 首先,立即执行函数时没有函数声明前置的,则以上代码会按照顺序来执行,解释如下
var say = 0;//把0赋值给变量say
(function say(n){
console.log(n);//输出给定的参数n~10,9,8,7,6,5,4,3,2
if(n<3) return;//当n<3时,跳出函数,故当n为2时跳出函数
say(n-1);
}( 10 ));
console.log(say);//输出为0,因为立即执行函数执行完了就完了,这里是变量say
版权声明:本教程版权归邓攀和饥人谷所有,转载须说明来源!!!!