1.函数声明和函数表达式有什么区别
- 函数声明:function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。
function print(s) {
console.log(s);
}
上面的代码命名了一个print函数,以后使用print()这种形式,就可以调用相应的代码。这叫做函数的声明(Function Declaration)。
- 函数表达式:除了用function命令声明函数,还可以采用变量赋值的写法。
var print = function(s) {
console.log(s);
};
这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。
采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。
var print = function x(){
console.log(typeof x);
};
x
// ReferenceError: x is not defined
print()
// function
2.什么是变量的声明前置?什么是函数的声明前置
- 变量声明前置:JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。
console.log(a);
var a = 1;
上面代码首先使用console.log方法,在控制台(console)显示变量a的值。这时变量a还没有声明和赋值,所以这是一种错误的做法,但是实际上不会报错。因为存在变量提升,真正运行的是下面的代码。
var a;
console.log(a);
a = 1;
最后的结果是显示undefined,表示变量a已声明,但还未赋值。
请注意,变量提升只对var命令声明的变量有效,如果一个变量不是用var命令声明的,就不会发生变量提升。
- 函数声明前置:JavaScript引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。
f();
function f() {}
表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript就会报错。
f();
var f = function (){};
// TypeError: undefined is not a function
上面的代码等同于下面的形式。
var f;
f();
f = function () {};
上面代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。因此,如果同时采用function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义。
3.arguments 是什么
由于JavaScript允许函数有不定数目的参数,所以我们需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
var f = function(one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
f(1, 2, 3)
// 1
// 2
// 3
需要注意的是,虽然arguments很像数组,但它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用。
4.函数的"重载"怎样实现
函数重载是指具有不同参数列表的同名函数。
JS中如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
要实现重载,可以使用arguments参数
function overLoading() {
// 根据arguments.length,对不同的值进行不同的操作
switch(arguments.length) {
case 0:
/*操作1的代码写在这里*/
break;
case 1:
/*操作2的代码写在这里*/
break;
case 2:
/*操作3的代码写在这里*/
break;
}
}
5.立即执行函数表达式是什么?有什么作用
在Javascript中,一对圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。
有时,我们需要在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。
function(){ /* code */ }();
// SyntaxError: Unexpected token (
产生这个错误的原因是,function这个关键字即可以当作语句,也可以当作表达式。
// 语句
function f() {}
// 表达式
var f = function f() {}
为了避免解析上的歧义,JavaScript引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
(function () { /* code */ }());
// 或者
(function(){ /* code */ })();
上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即执行函数表达式”(Immediately-Invoked Function Expression),简称IIFE。
注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个IIFE,可能就会报错。
推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。
var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();
甚至像下面这样写,也是可以的。
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
6.求n!,用递归来实现
function factorial(n){
if(n===1||n===0){
return 1;
}else if(n<0){
return "负数无阶乘";
}
return n*factorial(n-1);
}
7.以下代码输出什么?
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('饥人谷', 2, '男');
//'name:饥人谷' 'age:2' 'sex:男'
getInfo('小谷', 3);
//'name:饥人谷' 'age:2' 'sex:undefined'
getInfo('男');
//'name:男' 'age:undefined' 'sex:undefined'
8. 写一个函数,返回参数的平方和?
function sumOfSquares() {
var a = 0;
for (i in arguments) {
a = a + arguments[i]*arguments[i];
}
return a
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
9. 如下代码的输出?为什么
console.log(a);
var a = 1;
console.log(b);
// undefined 在执行console.log(a)时,由于变量提升,a只声明,未定义
//Uncaught ReferenceError: b is not defined 代码中未声明变量b
10. 如下代码的输出?为什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
//hello world 由于函数名提升,函数定义可以放在执行代码后
//Uncaught TypeError: sayAge is not a function
//var sayAge = function(age) 只进行sayAge变量名提升,sayAge(10)前函数未定义
11. 如下代码输出什么? 写出作用域链查找过程伪代码
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
1.
globalContext = {
AO: {
x: 10
foo: function
bar: function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
foo.[[scope]] = globalContext.AO
2.
barContext = {
AO:{
x:10
}
scope:globalContext.AO
}
3.
fooContext = {
AO:{
}
scope:globalContext.AO
}
}
在 globalContext.AO找到x:10
输出:10
12. 如下代码输出什么? 写出作用域链查找过程伪代码
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
1.
globalContext = {
AO: {
x: 10
bar: function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
2.
batContext = {
AO: {
x: 30
foo: function
},
Scope:globalContext.AO
}
foo.[[scope]] = barContext.AO
3.
fooContext = {
AO: {
},
Scope:barContext.AO
}
从barContext.AO中得x:30,输出为30
13. 以下代码输出什么? 写出作用域链的查找过程伪代码
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
1.
globalContext = {
AO: {
x: 10
bar: function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
2.
barContext = {
AO: {
x: 30
},
scope:globalContext.AO
}
//(function (){
// console.log(x)
// })() 为立即执行函数表达式,会屏蔽全局变量的污染,调用当前bar的AO,此时x=30,因此输出30
14. 以下代码输出什么? 写出作用域链查找过程伪代码
var a = 1;
function fn(){
console.log(a)
var a = 5
console.log(a)
a++
var a
fn3()
fn2()
console.log(a)
function fn2(){
console.log(a)
a = 20
}
}
function fn3(){
console.log(a)
a = 200
}
fn()
console.log(a)
//undefined
//5
//1
//6
//20
//200
1.
globalContext = {
AO: {
a: 1
fn: function
fn3: function
},
Scope: null
}
fn[[scope]] = globalContext.AO
fn3[[scope]] = globalContext.AO
2.
fnContext = {
AO: {
a: undefined,然后为5,a++变6,fn2后变20
fn2: function
},
scope: globalContext.AO
}
fn2.[[scope]] = fnContext.AO
3.
fn2Context = {
AO: {
},
scope:fnContext.AO
}
4.
fn3Context = {
AO: {
},
scope: globalContext.AO
}