问答
函数声明和函数表达式有什么区别 (*)
函数声明形式:
function sayHello(){
console.log("Hello, World! ");
}
函数表达式形式:
var sayHello = function(){
console.log("Hello, World! ");
};
在声明一个变量的时候,javascript解释器会将变量声明的语句提前,函数声明形式会发生函数声明前置,所以代码如果放在使用之后也可以生效,而函数表达式形式只发生了变量声明前置,如果在定义之前使用,函数名目前还只是一个变量名,不能用()进行执行,会抛出错误。
什么是变量的声明前置?什么是函数的声明前置 (**)
var a = 1;
//实际上相当于:
var a; //这句声明会提前到所有语句之前执行
a=1;
所以,
console.log(b);
var b = 1;//console.log 并不会报错,而是返回变量声明之后的值undefined。
arguments 是什么 (*)
arguments 是一个可以接收所有函数传递参数的类似数组,在函数里可以直接取用。当函数的参数不确定数量的时候可以使用arguments对形参进行数组的方式操作,可以实现类似重载。
函数的重载怎样实现 (**)
function fn(){
var sum = 0;
for(x in arguments ){
sum=arguments[x]+sum;
};
console.log(sum);
}
//无论多少参数都可实现累加运算
立即执行函数表达式是什么?有什么作用 (***)
(function() {
c =1 ;
console.log(c)
})();
立即执行函数可以将写在函数体内的语句直接执行。区别于普通语句,立即执行函数内的变量不会干扰函数体外,形成一个类似区块的空间。
什么是函数的作用域链 (****)
函数对象其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。
--JavaScript 开发进阶:理解 JavaScript 作用域和作用域链]
简单来说,函数作用域链用于查找函数内部变量的位置。在执行过程中,按照作用域链表的顺序依次进行查找。在函数每执行一次,这个表进行动态创建。所以,在实际使用中应当尽量减少全局变量的使用,避免with语句使用,如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。
代码
- 以下代码输出什么? (难度**)
- 可以看出arguments 是参数的数组,可以对其进行修改,改变参数。
- 当参数缺省的时候,定义为 undefined。
- 参数是具有顺序的,第一个参数传递为第一个形参。
- 写一个函数,返回参数的平方和?如 (难度**)
function sumOfSquares(){
var sum = 0;
for(var x in arguments){
sum += arguments[x]*arguments[x];
}
console.log(sum);
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
- 如下代码的输出?为什么 (难度*)
console.log(a);//返回 undefined
var a = 1;
console.log(b);//报错 提示未定义
因为声明前置,实际上在这段代码执行前就已经执行了var a;
,所以在console.log(a)
的时候不发生错误,返回undefined。
4.如下代码的输出?为什么 (难度*)
- 由于函数声明前置,sayName可以正常执行
- sayAge用了函数表达式形式,在赋值之前都不可以使用。
5.如下代码的输出?为什么 (难度**)
function fn(){}
var fn = 3;
console.log(fn);//3
结果比较直观,实际上将上面的语句颠倒也会输出相同的结果:
var fn = 3;
function fn(){}
console.log(fn);//3
- 还是因为声明前置的原因,可以将声明都放到开头,再来理解:
var fn;
function fn(){}
fn = 3;
console.log(fn);//3
这就是上面那段代码实际执行的样子,这也就不难理解了。
需要注意的是,声明前置,变量的声明要比函数的声明要更早执行。
function fn(){}
var fn;
console.log(fn);
//function fn(){}
6.如下代码的输出?为什么 (难度***)
- 在函数内部定义的函数,也会发生声明前置。上图中即使传入了参数10,但与函数内部函数重名,被覆盖掉了。此时console.log(fn2),打印出的就是fn2函数本身。此后fn2被重新赋值2,打印出3。
7.如下代码的输出?为什么 (难度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
//Uncaught TypeError: fn is 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是属于运算符,不属于函数,所以在其内部定义的变量和当前属于同一个作用域。在JS中只有函数内部使用var定义,才具备另一个作用域性质。
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);//undefined
i = 99;
fn2();
console.log(i);//100
}
fn();
//undefined
//100
i = 10;
fn = 20;
console.log(i);//10
10.如下代码的输出?为什么 (难度*****)
- 立即执行函数的内部定义的函数名、变量不会影响到函数外面。所以在其内部执行的say(),并没有覆盖函数外的say = 0; 在最后打印出来还是0 。
- 函数内部有一个嵌套结构。使用嵌套时,函数名可以在function后定义。不需要的时候可以不加。
- return的执行,伴随函数的退出,不再继续往下执行。
本教程版权归属于 张宇 及 饥人谷 所有,转载请说明来源~