1.函数声明和函数表达式有什么区别 (*)
函数声明和函数表达式都可以创建函数。
函数声明写法:
function 函数名称 (参数:可选) {函数体 }
举例:
function foo() {}
函数表达式写法:
var 变量 = function 函数名称(可选) (参数:可选) {函数体 }
举例:
var foo=function(){};
这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。
采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。
二者区别主要在于,函数声明有声明提前的特点,函数声明会被提到被提升到作用域的最前面,即使写代码的时候是写在最后面,也还是会被提升至最前面。
举例:
这段代码的实际执行顺序是:
var btn;
function fo(){}
console.log(fo);
console.log(btn);
btn=function(){};
所以输出结果,是function fo(){}
undefined
。
2.什么是变量的声明前置?什么是函数的声明前置 ?(**)
变量的声明前置,把所有变量声明放到代码的头部。
函数的声明前置是把函数声明放到代码的头部。如果我们使用函数声明的方式,那么即使函数写在最后也可以在前面语句调用,前提是函数声明部分已经被下载到本地。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>声明提前</title>
</head>
<body>
<script>
console.log(a);
function fn(){
console.log(b);
console.log(foo);
var b=4;
function foo(){}
}
var a = 9;
fn();
</script>
</body>
</html>
结果:
这就是变量的声明前置和函数的声明前置的结果。
3.arguments 是什么 (*)
arguments 是一个类数组对象。代表传给一个function的参数列表。arguments 对象是函数内部的本地变量;arguments 已经不再是函数的属性了。
在函数内部可以用arguments对象来获取函数的所有参数。而且arguments对象仅在函数内部有效,在函数外部调用 arguments 对象会出现一个错误。
它的作用有:
1.无须明确指出参数名,可以访问函数。
2.无需明确命名参数,就可以重写函数。
可以看到name的值被改变了。
3.可以用 arguments 对象检测函数的参数个数,引用属性 arguments.length 即可。
可以看到实际函数调用了几个参数,arguments.length就等于几。
4.用arguments 对象判断传递给函数的参数个数,可以模拟函数重载。
当只有一个参数时,doAdd() 函数给参数加 5。如果有两个参数,则会把两个参数相加,返回它们的和。所以,doAdd(10) 输出的是 "15",而 doAdd(40, 20) 输出的是 "60"。虽然不如重载那么好,不过已足以避开 ECMAScript 的这种限制。
4.函数的重载怎样实现 (**)
从语言角度来说,javascript不支持函数重载,先定义的函数会被后定义的函数覆盖。不能够定义同样的函数然后通过编译器去根据不同的参数执行不同的函数。但是javascript却可以通过自身属性去模拟函数重载。如上题中介绍的arguments的第四个特性,就可以模拟函数重载。
参考 js函数重载
5.立即执行函数表达式是什么?有什么作用 (***)
在Javascript中,一对圆括号()
是一种运算符,跟在函数名之后,表示调用该函数。比如,foo()就表示调用foo函数。
所以我们也可以在函数声明后加上()
,表示立即执行函数。但是不能直接加括号,function(){}();
会引起语法错误。这是因为,JavaScript引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
立即执行函数可以写成下面的形式:
(function(){ /*code */ }());
或者
(function(){/*code*/})();
上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称IIFE。
它的目的有两个:
一是不必为函数命名,避免了污染全局变量;
二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
// 写法一
var tmp =newData;
processData(tmp);
storeData(tmp);
// 写法二
(function (){
var tmp = newData;
processData(tmp);
storeData(tmp);}());
上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。
6.什么是函数的作用域链 (****)
- 函数作用域就是函数的可访问范围,简单的说,一个function从
{
开始,到}
结束,是这个函数的作用域。不同的函数有不同的作用域。作用域中声明的变量无法被作用域外部所访问。但是作用域中可以访问上级作用域中的变量。 - 作用域链正是内部上下文所有变量对象(包括父变量对象)的列表,用来变量查询。在代码执行的过程中,所用到的变量会在当前作用域中进行寻找,如果找不到,就会往沿着作用域链向上一级进行寻找,一直到全局作用域为止,如果找到便会停止(而不理会上一级是否有同名的变量),如果找不到,就会报错。
1.参考JavaScript 开发进阶:理解 JavaScript 作用域和作用域链
2.参考深入理解JavaScript系列(14):作用域链(Scope Chain)
代码题
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, '男');
/*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(){
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>函数平方和</title>
</head>
<body>
<script>
function sumOfSquares(){
var s=0;
for(var i=0;i<arguments.length;i++){
s= arguments[i]*arguments[i]+s;
}
alert(s);
}
sumOfSquares(2,3,4);
sumOfSquares(1,3);
</script>
</body>
</html>
3.如下代码的输出?为什么 (难度*)
console.log(a);//undefined
var a = 1;
console.log(b);//not defined
结果:
因为变量提升,把对a的声明提到前面,但是并没有定义a,所以输出undefined。而b则未被声明,输出错误。
4.如下代码的输出?为什么 (难度*)
sayName('world');//hello world
sayAge(10); //not a function
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
结果:
函数声明提前到代码最前面,调用函数sayName。而
var sayAge = function(age){console.log(age);};
定义了函数表达式,sayAge是一个变量,调用sayAge(10)
会出错。
5.如下代码的输出?为什么 (难度**)
function fn(){}
var fn = 3;
console.log(fn);//3
结果:
当在同一个作用域内定义了名字相同的变量和方法的话,无论其顺序如何,变量的赋值会覆盖方法的赋值。
6.如下代码的输出?为什么 (难度***)
function fn(fn2){
var fn2
function fn2(){
console.log('fnnn2');
}
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
}
fn(10);
结果:
执行顺序:
function fn(fn2){
var fn2;
function fn2(){
console.log('fnnn2');
}
console.log(fn2);/*function fn2(){
console.log('fnnn2');
}*/
fn2 = 3;
console.log(fn2);//3
console.log(fn);
}
fn(10);
调用函数,才会执行函数中的内容。console.log(fn);
调用上级函数。
7.如下代码的输出?为什么 (难度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn)); //not a function
结果:
执行顺序:
var fn;
function fn(fn){
console.log(fn);
}
fn = 1;
console.log(fn(fn));
fn被赋值为1,是一个数字不是函数,出错。
8.如下代码的输出?为什么 (难度**)
//作用域
console.log(j);//undefined
console.log(i);//undefined
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);//10
console.log(j);//100
结果:
for循环把i增加到10,j的值被设置为100。这两个都是全局变量。
类似于:
console.log(j);
console.log(i);
var i=10;
var j=100;
console.log(i);
console.log(j);
输出结果如上图示。
9.如下代码的输出?为什么 (难度****)
fn();//undefined 100
var i = 10;
var fn = 20;
console.log(i);//10
function fn(){
console.log(i);//undefined
var i = 99;
fn2();
console.log(i);//100
function fn2(){
i = 100;
}
}
结果:
根据变量的声明提升和函数的声明提升,可以把上面的代码写成:
var i;
var fn;
function fn(){
var i;
function fn2(){
i = 100;
}//并没有被调用,没有运行。
console.log(i);//undefined
i = 99;
fn2();//i=100,i是全局变量
console.log(i);//100
}
fn();
i=10;//定义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 ));
console.log(say);//10 9 8 7 6 5 4 3 2 0
结果:
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
是立即执行函数,这个函数是对n从10依次递减,直到n<3,即n的值是2时,停止执行这个函数。得到数字10 9 8 7 6 5 4 3 2
。而say又被赋值为0,最后输出0。
本文版权归本人和饥人谷所有,转载请注明出处
网友评论