1.函数声明和函数表达式有什么区别
- 函数声明:function functionName(){};使用function关键字声明一个函数,再指定一个函数名,叫函数声明
函数表达式:var fn = function(){};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量fn,叫函数表达式,这是最常见的函数表达式语法形式
-** 区别:
1.Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式**。
2.函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。
alert(foo); // function foo() {} 原因:变量声明提升
alert(bar); // undefined:原因bar能提升,但是没赋值,默认为undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}
2.什么是变量的声明前置?什么是函数的声明前置
- 变量的声明前置:就是在一个作用域块中,所有的变量都被放在代码块的开始出处声明
- 函数的声明前置:和变量的声明会前置一样,函数声明同样会前置。
- 函数声明的前置为:声明和赋值均前置。
- 函数表达式的前置为:仅仅声明前置,值为undefined,直到读到表达式后才取得表达式的值。
3.arguments 是什么
arguments是类数组对象,包含着传入函数中的所有参数。arguments 变量不是一个数组(Array)。 尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype 继承,实际上它是一个对象(Object)。
因此,无法对 arguments 变量使用标准的数组方法,比如 push, pop 或者 slice。
eg: function printPersonInfo(name, age, sex){
console.log(arguments);
}
printPersonInfo("tiger",6,"boy")
4.函数的"重载"怎样实现
- "重载"的概念:,是Ada、C++、C#、D和Java等编程语言中具有的一项特性,这项特性允许创建数项名称相同但功能的输入输出类型不同的子程序,它可以简单地称为一个单独功能可以执行多项任务的能力。
其他语言的重载;
int sum(int num1, int num2){
return num1 + num2;
}
float sum(float num1, float num2){
return num1 + num2;
}
sum(1, 2);
sum(1.5, 2.4);
- 在 JS 中没有重载! 同名函数会覆盖。 但可以在函数体针对不同的参数调用执行相应的逻辑
function printPeopleInfo(name, age, sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
printPeopleInfo('Byron', 26); // 'Byron' 26
printPeopleInfo('Byron', 26, 'male');// 'Byron' 26 'male'
5.立即执行函数表达式是什么?有什么作用
- 英文:immediately-invoked function expression,缩写:IIFE,是一种利用JavaScript函数生成新作用域的编程方法。有时,这种编程方法也被叫做“自执行(匿名)函数”,但“立即调用函数表达式”是语义上最准确的术语。
-
作用
隔离作用域,保护私有变量,防止污染全局变量 -
用法
最常见的一种是将函数表达式字面量置于圆括号之内,然后使用圆括号调用函数。
(置于圆括号内是因为:无论在全局环境或者局部环境里遇到了这样的function关键字,默认的,它会将它当作是一个函数声明,而不是函数表达式,如果你不明确的告诉圆括号它是一个表达式,它会将其当作没有名字的函数声明并且抛出一个错误,因为函数声明需要一个名字)
(function() { // 这里的语句将获得新的作用域 })(); 若要将作用域外变量传递进函数,则按下述方式书写: (function(a, b) { // a == 'hello' // b == 'world' })('hello', 'world'); 在一些省略分号的程序中,可见将分号至于行首的做法。这样的分号被称为“防御性分号” a = b + c ;(function() { // 故意将分号放在这里 // 代码 })(); 如此书写,以防止语句被理解为对函数c的调用(c(...))。
6.求n!,用递归来实现
function factorial(n){
if(n<0){
return "无效输入";
}
else if(n<=1){
return 1;
}else{
return n*factorial(n-1) ;
}
}
alert(factorial(5))//120
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, '男');
> getInfo('小谷', 3);
> getInfo('男');
8. 写一个函数,返回参数的平方和?
function sumOfSquares(){
var sum = 0;
for(var i =arguments.length;i>0;i--){
sum+=Math.pow(arguments[i-1],2);
}
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
9. 如下代码的输出?为什么
console.log(a); //undefined ;变量a声明提前,但未赋值
var a = 1;
console.log(b);//b is not defined ;变量b没有声明和赋值
10. 如下代码的输出?为什么
sayName('world');// hello world;函数声明的前置为声明和赋值均前置
sayAge(10);//sayAge is not a function ;函数声明前置,但表达式值不能前置,sayAge为undefined,undefined 没有方法。
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
11. 如下代码输出什么? 写出作用域链查找过程伪代码
var x = 10
bar() //返回:10
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
globalContext={
Ao:{
x:10
foo:function
bar:function
} ,
Scope:null
}
//声明 foo 时 得到下面
foo.[[scope]] = globalContext.Ao
//声明 bar 时 得到下面
bar.[[scope]] = globalContext.Ao
//当调用 bar() 时, 进入 bar 的执行上下文
barContext ={
Ao:{
x:30
},
Scope:bar.[[scope]]// globalContext.Ao
}
当调用 foo() 时,先从 bar 执行上下文中的 AO里找,找不到再从 bar 的 [[scope]]里找找到后即调用
//当调用 foo() 时,进入 foo 的执行上下文
fooContext = {
AO: {},
Scope: foo.[[scope]] // globalContext.AO
}
所以 console.log(x)是 10
12. 如下代码输出什么? 写出作用域链查找过程伪代码
var x = 10;
bar() //返回30
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
作用域查找过程伪代码
globalContext={
Ao:{
x:10
bar:function
} ,
Scope:null
}
//声明 bar 时 得到下面
bar.[[scope]] = globalContext.Ao
//当调用 bar() 时, 进入 bar 的执行上下文
barContext ={
Ao:{
x:30,
foo:function
},
Scope:bar.[[scope]]// globalContext.Ao
}
当调用 foo() 时,先从 bar 执行上下文中的 AO里找,找到后即调用
//当调用 foo() 时,进入 foo 的执行上下文
fooContext = {
AO: {},
Scope: foo.[[scope]] // barContext.AO
}
所以 console.log(x)是 30
13. 以下代码输出什么? 写出作用域链的查找过程伪代码
var x = 10;
bar() // 30
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
作用域链的查找过程伪代码
globalContext = {
Ao:{
x:30
bar:function
},
Scope:null
}
// 声明bar时,得到下面
bar.[[scope]]=globalContext.Ao;
//当调用bar时,进入bar的执行上下文;先从 bar 执行上下文中的 AO里找,找到后即调用
barContext = {
Ao:{
x:30
IIFE:function
}
Scope: bar.[[scope]]//globalContext.Ao
}
所以 console.log(x)是 30
14. 以下代码输出什么? 写出作用域链查找过程伪代码
var a = 1;
function fn(){
console.log(a)//undefined
var a = 5
console.log(a)//5
a++
var a
fn3()
fn2()
console.log(a) //20
function fn2(){
console.log(a) //6
a = 20
}
}
function fn3(){
console.log(a) //1
a = 200
}
fn() //20
console.log(a) //200
//作用域链查找过程伪代码
globalContext = {
Ao:{
a:200
fn:function
fn3:function
},
Scope:null
}
// 声明fn时,得到下面
fn.[[scope]] = globalContext .Ao
// 声明fn3时,得到下面
fn3.[[scope]] = globalContext .Ao
fnContext={
Ao:{
a:20
fn2:function
}
Scope:fn.[[scope]]// globalContext .Ao
}
fn3Context={
Ao:{}
Scope:fn.[[scope]]// globalContext .Ao
}
// 声明fn2时,得到下面
fn2.[[scope]] = fnContext .Ao
fn2Context={
Ao:{}
Scope:fn.[[scope]]// fnContext .Ao
}
所以 fn()是20 console.log(a)是200