1.函数声明和函数表达式有什么区别
函数声明和函数表达式是声明函数的两种不同的方式,形式如下:
- 函数声明:即使用function关键字声明一个函数
//函数声明
function sayHello(){
console.log('hello')
}
//函数调用
sayHello()
声明不必放到调用的前面,函数调用可以发生在函数声明之前,例如下面这种情况下不会报错
printName();//输出console.log('1')
function printName(){
console.log('1');
}
//正常,因为提升了函数声明,函数调用可在函数声明之前
即在一个作用域下,var 声明的变量和function 声明的函数会前置
- 函数表达式
var sayHello = function(){
console.log('hello');
}
sayHello()
声明必须放到调用的前面,例如
printName();//浏览器提示Uncaught TypeError: printName is not a function(…)
var printName = function(){
console.log('1');
};
//报错,函数表达式和 var 一个变量没什么区别,变量printName还未保存对函数的引用,函数调用必须在函数表达式之后
浏览器提示Uncaught TypeError: printName is not a function(…).原因:类似变量提升,函数作为一个变量赋值给printName,等价于
var printName; //此时printName为undefined
printName();
printName = function(){
console.log('1');
};
- 因此,函数声明和函数表达式不同之处在于:
- Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,
- 函数声明后面可以加括号立即调用该函数,函数表达式不可以,只能以fnName()形式调用
2.什么是变量的声明前置?什么是函数的声明前置
- 变量声明前置
- JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到当前作用域的头部,这就叫做变量的声明前置,也叫变量提升(hoisting)。
- 函数声明前置
- JavaScript引擎将函数名视同变量名,所以采用function命令声明函数时,函数声明会像变量声明一样,被提升到代码头部。因此,函数的调用可以写在声明前面,函数可以被执行。(注意:如下函数表达式,变量fn还未保存对函数的引用,函数调用必须在函数表达式之后)。
console.log(fn); //undefined fn(); //浏览器提示Uncaught TypeError: fn is not a function(…) var fn = function(){} //报错,变量fn还未保存对函数的引用,函数调用必须在函数表达式之后
3.arguments 是什么
-
arguments是一个类数组对象,类似数组的方式,可以通过下标的方式去获取值,但它本身不是数组,没有数组的一些特性,所以叫类数组对象。在函数内部,可以使用arguments对象获取到该函数的所有传入参数。
- 例如如下函数
function printPersonInfo(name, age, sex){ console.log(name); console.log(age); console.log(sex); console.log(arguments); }
- 执行
printPersonInfo('liu', 21, 'boy')
后,输出:
name : liu age : 22 sex : boy ["liu", 22, "boy"]
- 执行
printPersonInfo('liu', 21,)
后,输出:
name : liu age : boy sex : undefined ["liu", "boy"]
4.函数的"重载"怎样实现
- 重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。
- JS并不像其他强类型语言一样可以声明重载函数,若在原先声明的函数后再声明一个不同参数数量的函数(JS是弱语言,声明的函数不需要指明参数类型),解析器会用后面声明的函数覆盖前面声明的函数。
JS种没有重载! 同名函数会覆盖。但可以在函数体针对不同的参数调用执行相应的逻辑,如下
function printPeopleInfo(name, age, sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');
5. 立即执行函数表达式是什么?有什么作用
(function(){ var a=1; })();
(function(){ var a=1; }());
-
作用:
- 作用:隔离作用域。
6.递归
- 递归实现一个函数,计算 n!
function factor(n) {
if(n===1){
return n
}
return n*factor(n-1)
}
几个代码小题目
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('饥人谷', 2, '男');
getInfo('小谷', 3);
getInfo('男');
-
getInfo('饥人谷', 2, '男');
输出:
name:饥人谷
age:2
sex:男
["饥人谷", 2, "男"]
name valley
-
getInfo('小谷', 3);
输出:
name:小谷
age:3
sex:undefined
["小谷", 3]
name valley
-
getInfo('男');
输出:
name:男
age:undefined
sex:undefined
["男"]
name valley
2.写一个函数,返回参数的平方和
function sumOfSquares(){
var sum = 0
for (var i = 0; i < arguments.length; i ++)
sum += arguments[i] * arguments[i]
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
3.如下代码的输出?为什么
console.log(a);//undefined
var a = 1;
console.log(b);//报错Uncaught ReferenceError: b is not defined(…)
a输出undefined是因为变量提升,b没有定义,类似如下代码
var a;
console.log(a); //undefined
a = 1;
console.log(b);
4.如下代码的输出?为什么
sayName('world'); //输出 'hello ', 'world' 使用函数声明做出的声明前置
sayAge(10); //报错 Uncaught TypeError: sayAge is not a function(…) sayAge此时还为被赋值为函数
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
作用域链
5.如下代码输出什么? 写出作用域链查找过程伪代码
// 输出10
var x = 10
bar() //输出10
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
作用域链查找过程伪代码
/*
1.
globalContext = {
AO:{
x: 10
foo:funcation(){}
bar:funcation(){}
}
Scope: null
}
foo.[[scope]] = globalContext.AO
bar.[[scope]] = globalContext.AO
2.
barContext = {
AO:{
x: 30
}
scope: bar.[[scope]] // globalContext.AO
}
3.
fooContext = {
AO:{}
scope: foo.[[scope]] // globalContext.AO
}
*/
6.如下代码输出什么? 写出作用域链查找过程伪代码
// 输出30
var x = 10;
bar() //输出30
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
作用域链查找过程伪代码
/*
1.
globalContext = {
AO:{
x: 10
bar:funcation(){}
}
Scope: null
}
bar.[[scope]] = globalContext.AO
2.
barContext = {
AO:{
x: 30
foo:funcation(){}
}
Scope: bar.[[scope]] // globalContext.AO
}
foo.[[scope]] = barContext.AO
3.
fooContext = {
AO:{}
Scope: foo.[[scope]] // barContext.AO
}
*/
7.以下代码输出什么? 写出作用域链的查找过程伪代码
输出30
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x) // 30
})()
}
/*
globalContext = {
AO:{
x: 10
bar:funcation(){}
}
Scope: null
}
bar.[[scope]] // globalContext.AO
2.
barContext = {
AO:{
x: 30
匿名函数A:funcation(){}
}
Scope: bar.[[scope]] = globalContext.AO
}
匿名函数A.[[scope]] // barContext.AO
3.
匿名函数AContext = {
AO:{}
Scope: 匿名函数A.[[scope]] // barContext.AO
}
*/
8.以下代码输出什么? 写出作用域链查找过程伪代码
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() //输出: undefined 5 1 6 20
console.log(a) //输出: 200
/*
1.
globalContext = {
AO:{
a: 1 -> 200
fn:funcation(){}
fn3:funcation(){}
}
Scope: null
}
fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO
2.
fnContext = {
AO:{
a: 5 -> 6 -> 20
fn2:funcation(){}
}
Scope: fn.[[scope]] // globalContext.AO
}
fn2.[[scope]] = fnContext.AO
3.
fn3Context = {
AO:{}
Scope: foo.[[scope]] // globalContext.AO
}
4.
fn2Context = {
A:{}
Scope: fn2.[[scope]] // fnContext.AO
}
*/
注意:在JavaScript里,每个函数,当被调用时,都会创建一个新的执行上下文。因为在函数里定义的变量和函数只能在函数内部被访问,外部无法获取;当调用函数时,函数提供的上下文就提供了一个非常简单的方法创建私有变量。