一、函数声明和函数表达式有什么区别?
-
函数声明和函数表达式的区别:
- 函数声明可以提升到其他代码之前(即函数声明前置),但函数表达式不能提升到其他代码之前
- 函数声明必须给定函数名称,函数表达式则可以忽略函数名称
- 函数表达式后面有分号,函数声明没有
-
函数声明:
getsum(1, 2); //3 function getsum(num1, num2){ return num1+num2; }
等价于
function getsum(num1, num2){
return num1+num2;
}
getsum(1, 2); //3
-
函数表达式:
getsum(1, 2); //Uncaught TypeError: getsum is not a function var getsum = function(num1, num2){ return num1+num2; };
等价于
var getsum;
getsum(1, 2); ////Uncaught TypeError: getsum is not a function
getsum = function(num1, num2){
return num1+num2;
};
二、什么是变量声明前置?什么是函数声明前置?
-
变量声明前置就是把变量的声明提升到当前作用域的最前面。如下例:
console.log(a); //undefined var a = 1;
等价于
var a; console.log(a); //undefined a = 1;
下面这个例子也是输出undefined
,因为输出的是main()
内的局部变量
var a = 1;
function main(){
console.log(a);
var a = 2;
}
main(); //undefined
等价于
var a;
function main(){
var a;
console.log(a);
a = 2;
}
a = 1;
main(); //undefined
-
函数声明前置就是把函数声明提升到当前的最前面(位于变量声明前置后面)
var num1 = 1, num2 = 2; getsum(num1, num2); //3 function getsum(num1, num2){ return num1 + num2; }
等价于
var num1, num2;
function getsum(num1, num2){
return num1 + num2;
}
num1 = 1;
num2 = 2;
getsum(num1, num2); //3
三、arguments是什么?
arguments
是一个用于存放函数所有传入参数的类数组对象。
function main(a, b, c){
return arguments;
}
main(1, 2, 3); //[1, 2, 3]
arguments
的值由函数的传入参数决定,与函数定义的参数无关
四、函数的重载怎样实现?
函数的重载是指为一个函数编写多个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。也就是定义几个名字相同的函数但参数的类型和数量不同,当函数接受到不同的参数时就按照不同的定义执行。
-
ECMAScript函数没有签名,所以ECMAScript函数不能重载,如果同时定义两个名称相同的函数,后定义的那个函数会覆盖先定义的函数
function addnum(num1, num2){ return num1+num2; } function addnum(num1, num2, num3){ return num1+num2+num3; } console.log(addnum(1, 2)) ; //NaN, 后定义的函数覆盖了先定义的函数 console.log(addnum(1, 2, 3)) ; //6
-
可以通过
arguments
来实现函数重载的功能。如下:function addnum(num){ var sum = 0; for(var i = 0; i < arguments.length; i++){ sum += arguments[i]; } return sum; } console.log(addnum(1)); //1 console.log(addnum(1, 2)); //3
五、立即执行函数表达式是什么?有什么作用
-
立即执行函数表达式(IIFE——Immediately Invoked Function Expression),是将函数的定义放在一个圆括号里,让JavaScript引擎将其理解为一个表达式,再在函数的定义后面加一个
()
,以达到定义函数后立即调用该函数的效果。下面两种形式在功能上是一致的:(function(){ /* code */ }()); //立即执行函数表达式 (function(){ /* code */ })(); //立即执行函数表达式
-
立即执行函数表达式的作用
可以创建匿名函数,避免污染全局变量
-
形成一个单独的作用域,可以封装一些外部无法读取的私有变量
// 写法一 var tmp = newData; processData(tmp); storeData(tmp); // 写法二 (function (){ var tmp = newData; processData(tmp); storeData(tmp); }());
上面代码中,写法二比写法一更好,因为完全避免了污染全局变量
六、什么是函数的作用域链
-
作用域
作用域就是变量和函数的可访问范围,控制着变量与函数的可见性和生命周期。在Javascript中变量的作用域有全局作用域和局部作用域。- 全局作用域
变量没有在函数内声明或者声明的时候没有带var
就是全局变量,拥有全局作用域,window
对象的所有属性拥有全局作用域,在代码任何地方都可以访问。 - 局部作用域
函数内部声明并且以var
修饰的变量就是局部变量,只能在函数体内使用,函数的参数虽然没有使用var
但仍然是局部变量。
- 全局作用域
执行环境(运行上下文)
执行环境(execution context)定义了变量或函数有权访问的其他数据,决定了他们的各自行为。每个执行环境都有一个与之关联的变量对象(variable object,VO),执行环境中定义的所有变量和函数都会保存在这个对象中,解析器在处理数据的时候就会访问这个内部对象。
全局执行环境是最外层的一个执行环境,在web浏览器中全局执行环境是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。每个函数都有自己的执行环境,当执行流进入一个函数的时候,函数的环境会被推入一个函数栈中,而在函数执行完毕后执行环境出栈并销毁,保存在其中的所有变量和函数定义随之销毁,控制权返回到之前的执行环境中,全局的执行环境在应用程序退出(浏览器关闭)才会被销毁。-
作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments
对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域的前端开始,然后逐级地向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)
代码
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);
sumOfSquares(1,3);
function sumOfSquares(num1, num2){
var sum = 0;
for(var i = 0; i < arguments.length; i++){
var a = arguments[i];
sum += a*a;
}
return sum;
}
console.log(sumOfSquares(2,3,4)); // 29
console.log(sumOfSquares(1,3)); // 10
3.如下代码的输出?为什么
console.log(a); // undefined,变量声明提升
var a = 1;
console.log(b); // 报错,因为变量b没有声明
等价于
var a;
console.log(a); // undefined
a = 1;
console.log(b); // 报错,因为变量b没有声明
4.如下代码的输出?为什么
sayName('world'); // hello world
sayAge(10); // 报错,因为函数表达式不会前置,执行到这里时sayAge没有指向任何函数
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没有指向任何函数
sayAge = function(age){
console.log(age);
}
5.如下代码的输出?为什么
function fn(){}
var fn = 3;
console.log(fn); // 3
等价于
var fn;
function fn(){}
fn = 3;
console.log(fn); // 3
6.如下代码的输出?为什么
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
fn(10);
// function
// 3
// function
等价于
function fn(fn2){
var fn2;
function fn2(){
console.log('fnnn2');
}
console.log(fn2);
fn2 = 3;
console.log(fn2);
console.log(fn);
}
fn(10);
// function
// 3
// function
7.如下代码输出什么?为什么
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn)); // 报错,这里变量fn存放的是一个数值,没有指向函数
等价于
var fn
function fn(fn){
console.log(fn);
}
fn = 1;
console.log(fn(fn)); // 报错,这里变量fn存放的是一个数值,没有指向函数
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
等价于
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
变量i,j都是全局变量,只有在函数内才会存在局部变量
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();
/**
* console.log(i); // undefined
* fn2(); // 没有输出
* console.log(i); // 100
*/
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));
/**
*立即执行函数表达式,传入参数为10,即n=10
*console.log(n); // 10
*if(n<3) return; //n<3时跳出循环
*say(n-1); // 9 8 7 6 5 4 3 2
*/
console.log(say); // 0, 这里的say是全局变量say,立即执行函数表达式中的say是局部变量
本文版权属吴天乔所有,转载务必注明出处。