问答
1.函数声明和函数表达式有什么区别?
在日常的任务中,JavaScript主要使用下面两种方式创建函数:
- 函数声明(Function Declaration):
function funDec(){
console.log("Function Declaration");
}
- 函数表达式(Funciton Expression):
var funExp= function() {
console.log("Funciton Expression");
};
主要区别:
1.函数声明必须以关键字function开头,如不是,则为表达式。
2.函数表达式在语句的结尾需要加上分号,表示语句结束,而函数声明在结尾不需要加分号。
3.函数声明在JS解析的时候会进行函数提升,即在同一个作用域内,不管函数声明在哪里创建,它会提升至作用域顶部。
举例:
funDec();//Function Declaration
function funDec(){
console.log("Function Declaration");
}
而函数表达式不会被提升,它的表现和变量提升的规则相同,函数名(这里是指的是var后面定义的变量名)会声明前置,但是函数表达式的赋值操作还是在原来的位置。
举例:
console.log(funExp);//undefined
var funExp = function() {
console.log("Funciton Expression");
};
2.什么是变量的声明前置?什么是函数的声明前置?
- 变量的声明前置
JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行的运行,这造成的结果就是,所有被声明的语句,都会被提升到代码的头部,这就是变量提升。需要注意的是,变量提升只是提升变量的声明,并不会把赋值也提升上来。
看例子:
console.log(name);
var name = "Hello World";
实际上由于变量提升的机制,运行上面代码的时候会变成这样:
var name;
console.log(name);//undefined
name = "Hello World";
- 函数的声明前置
函数在声明的时候也会像变量一样被提升,不同的是,只有使用函数声明(Function Declaration)的方式创建的函数才能提升,而使用函数表达式函数表达式(Funciton Expression)创建的函数不会被提升。
看列子:
foo();
function foo() {
console.log(name);
var name ="Hello World";
}
上面这段代码实际表现为:
function foo() {
var name;
console.log(name);
name = "Hello World";
}
foo();//undefined
函数foo()
的作用域内的变量name
提升到了作用域顶部,全局作用域里的foo()
的函数声明会被提升到所处的作用域顶部,即全局作用域的顶部。
- 函数声明会在变量声明的后面
同时存在函数声明和变量声明时,函数声明的提升会在变量声明的提升后面,
看列子:
console.log(typeof a);
var a=1;
function a(){};//function
上面的代码实际表现为:
var a;
function a(){};
console.log(a);//function
a =1;
3.arguments 是什么?
arguments是JavaScript里的一个内置的类数组对象,它包含了函数调用的所有参数。这个对象为传递给函数的每个参数建立一个条目,条目的索引号从0开始,这个对象只有在函数体内部才可以使用,利用arguments可以对参数重新赋值,例如:
var f = function (a,b,c) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]=4);
}
f(1,2,3)
//1
//2
//4
arguments对象并不是一个真正的数组,它类似于数组,但是没有数组特有的属性和方法,除了length。
arguments使用场景:
对于一个参数数量是一个可变量的函数来说比较有用,可以用arguments.length来得到参数的数量,然后可以用arguments objects来对每个参数进行处理。
4.函数的重载怎样实现
重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或顺序不同都被认为是不同的函数,称为函数重载。
在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的。
但是JavaScript却可以使用自身属性去模拟函数重载:
function sum(){
var sum = 0;
for (var i=0;i<arguments.length;i++){
sum = sum +arguments[i];
}
return sum;
}
console.log(sum(1,2));//3
console.log(sum(1,2,10));//13
5.立即执行函数表达式(IIFE)是什么?有什么作用?
在JavaScript中,一对圆括号()
是一种运算符,跟在函数名后面,表示调用该函数。比如,print()
就表示调用print
函数。
有时,我们需要定义函数之后,立即调用该函数,但是你不能在函数的定义之后加上圆括号,这会产生语法错误。
function(){
/*body*/
}();
//Uncaught SyntaxError: Unexpected token (
原因是function
这个关键字即可以当作语句,也可以当作表达式。为了避免解析上的歧义,JavaScript引擎规定,如果function
关键字出现在行首,就认为这是一个函数声明,而不是函数表达式,就不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function
出现在行首,让引擎将其理解为一个表达式,最简单的方法就是将其放在一个圆括号里面,有下面两种写法:
(function(){
/*body*/
}());
//或者
(function(){
/*body*/
})();
这样写引擎就会认为后面是一个表达式,添加圆括号后会立即执行,这就叫做"立即调用的函数表达式(Immediately-Invoked Function Expression)",简称IIFE。
IIFE的作用:
通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染了全局变量,而是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
6.什么是函数的作用域链
函数的作用域链是由一系列对象(函数的活动对象+0个到多个的上层函数的活动对象+最后的全局对象)组成的,在函数执行的时候,会按照先后顺序从这些对象的属性中寻找函数体中用到的标识符的值(标识符解析),函数会在定义时将它们各自所处的环境(全局上下文或者函数上下文)的作用域链存储到自身的[scope]内部属性中,在需要使用返回这个属性。
举例:
var a = 1;
var x = function () {
console.log(a);
};
function f() {
var a = 2;
x();
}
f() // 1
上面代码中,函数f()
的值来自于另一个函数x()
的值,而函数x()
的值又来自于变量a的赋值。
参考博客:JavaScript 开发进阶:理解 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 sumOfSquares = 0;
for (var i = 0; i < arguments.length; i++) {
sumOfSquares= sumOfSquares+arguments[i]*arguments[i];
}
return console.log(sumOfSquares);
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
3.如下代码的输出?为什么
console.log(a);//undefined
var a = 1;
console.log(b);//报错
上面代码实际运行如下:
var a;
console.log(a);//undefined
a=1;
consolo.log(b);//报错
4.如下代码的输出?为什么。
sayName('world');//hello world
sayAge(10);//报错
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
上面代码实际运行如下;
var sayAge;
function sayName(name){
console.log('hello ', name);
}
sayName('world');//hello world
sayAge(10);//报错
sayAge = function(age){
console.log(age);
}
5.如下代码的输出?为什么
function fn(){}
var fn = 3;
console.log(fn);
//3
上面代码实际运行如下:
var fn;
function fu(){}
fn =3;
console.log(fn);
//3
6.如下代码的输出?为什么
function fn(fn2){
console.log(fn2);//function fn2()
var fn2 = 3;
console.log(fn2);//3
console.log(fn);//function fn(fn2)
function fn2(){
console.log('fnnn2');
}
}
fn(10);
代码实际运行如下:
function fn(fn2){
var fn2;
function fn2(){
console.log('funn2');
}
console.log(fn2)//function fn2()
fn2 =3;
console.log(fn2);//3
console.log(fn);//function fn(fn2)
}
fn(10);
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));//报错
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(j);//undefined
console.log(i);//undefined
for(i = 0;i<10;i++){
j= 100;
}
console.log(i);//10
console.log(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;
function fn(){
var i;
function fn2(){
i =100;
}
console.log(i);
i = 99;
fn2();
console.log(i);
}
fn();
i = 10;
fn= 20;
console.log(i);
//undefined
// 100
//10
这个代码比较绕,这里一步一步的解析下:
-
先将全局的变量声明和函数声明提升到代码的顶部,然后将函数内部的变量声明和函数声明提升到函数内部的顶部。
-
接着运行到代码
fn()
,函数fn
的内部开始解析和求值:- 首先求值的是
console.log(i)
,变量i
此时只有声明,还未赋值,而函数fn2()
内部的i
值外面是无法访问的,所以输出为undefined
。
- 首先求值的是
-
接着求值的是
fn2()
,求值的结果是i=100
,覆盖了上一句的i=99
。 -
最后一个求值的是
console.log(i)
,结果很明显为100。 -
所以整个函数
fn
运行来下的结果为undefined
和100
。 -
接着运行到
console.log(i)
,此时i
被赋值为10,结果输出为10。
所以整个代码运行下来的结果为undefined,100,10
。
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
//0
函数say被一个圆括号包裹起来,所以函数say是一个表达式,不会有函数声明提升,而且会立刻执行函数表达式。
函数say的内部有一行代码say(n-1)
,这意味着函数say会发生递归,每个传入函数say的参数会减一再继续运行下去,直到参数n小于3。
所以在传入参数10之后,函数say就会从10开始打印,直到打印2为止。
最后console.log(say),打印出变量say的值,即为0。
——————————
本文版权归本人和饥人谷所有,转载请注明来源。
网友评论