函数声明和函数表达式有什么区别
函数声明:
function 函数名称 (参数:可选){ 函数体 }1
2
3function sum(a){
console.log(a);
}函数表达式:
function 函数名称(可选)(参数:可选){ 函数体 }1
2
3var sum = function(a){
console.log(a);
}
- 函数声明必须带有标示符(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:
- 函数声明有声明前置,函数可以在函数声明之前调用,而函数表达式的函数只能在之后调用.
- 函数声明只能出现在程序或函数体内,它们 不能出现在Block(块)({ … })中,例如不能出现在 if、while 或 for 语句或者try/catch/finally中,而函数表达式可以出现在任何地方.
什么是变量的声明前置?什么是函数的声明前置 (**)
- 先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升,也就是变量的声明前置。
- 函数的声明前置,如果我们使用函数表达式那么规则和变量一样。如果使用函数声明的方式,即使函数写在最后也可以在前面调用
arguments 是什么 (*)
- arguments是默认存在与函数体内部的,它获取到该函数的所有传入参数,是一个数组。
函数的重载怎样实现 (**)
- 在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载。
也就是哪怕参数不同,函数名一样也可以被认为是不同的函数 - js本身不支持函数的重载,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的,但是可以利用js的arguments的特点模拟部分函数的重载。
立即执行函数表达式是什么?有什么作用 (*)
- 立即执行函数表达式是可以让你的函数在创建后立即执行,它的本质是一个表达式,并不属于声明和函数的调用。形式是:(函数定义表达式)函数调用表达式。
- 立即执行里函数是一个表达式,执行完内部变量便销毁,如果声明的函数只需要调用一次,建议使用。
- 独立模块。开发时,它能做到各模块的低耦合,减少对全局作用域的污染,防止与外部变量冲突,外部访问不到内部的变量。
什么是函数的作用域链 (**)
- 函数对象有一个内部属性是[Scope],该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
- 在函数创建时,它的作用域链在自身局部对象的基础上,会填入一个上一级直至全局的对象,该全局对象包含了所有全局变量,即Scope Chain。
- 执行此函数时,它的作用域链初始化为当前当前函数所有的局部和全局变量。
函数会首先从函数内部开始查找局部变量,然后逐级向上查找,直到全局变量,这样便形成了一个关于作用域的链条,是的函数执行。
代码
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, ‘男’);
getInfo(‘hunger’, 28);
getInfo(‘男’);
结果:
写一个函数,返回参数的平方和?如 (难度**)
function sumOfSquares(){
var sum = 0; for (var i = 0; i < arguments.length; i++) { sum = sum + arguments[i]*arguments[i] }; console.log(sum); }
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
结果
3.如下代码的输出?为什么 (难度*)
console.log(a);
var a = 1;
console.log(b);- undefined;var a声明前置,执行console.log(a)时a只是被声明没有初始化,所以a为undefined。
- b is not defined;b没有声明,是一个不存在的变量,所以报错。
4.如下代码的输出?为什么 (难度*)
sayName(‘world’);
sayAge(10);
function sayName(name){
}console.log('hello ', name);
var sayAge = function(age){
};console.log(age);
- hello word;函数声明前置,所以执行语句即使在声明前面也可以执行。
- Uncaught TypeError: sayAge is not a function;sayAge是一个函数表达式,没有声明前置的概念,调用只能在函数表达式的后面调用,否则会报错
5.如下代码的输出?为什么 (难度**)
function fn(){}
var fn = 3;
console.log(fn); - 3:变量声明和函数声明会前置,但是执行到fn=3时,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);
- 执行fn(10)时,函数fn内部初始化时会声明前置,function fn2(){console.log(‘fnnn2’);}覆盖传进来的参数10和变量fn2的声明,所以console.log(fn2);的结果是声明的函数fn2
- fn2=3赋值后fn2为3,所以console.log(fn2)输出为3.
- console.log(fn);作用域链,语句喊先查找函数内部的局部变量,没有fn则返回全局查找,全局中fn是一个函数,所以输出fn
7.如下代码的输出?为什么 (难度*)
var fn = 1;
function fn(fn){
}console.log(fn);
console.log(fn(fn)); - Uncaught TypeError: fn is not a function;首先函数和变量会声明前置,函数声明覆盖变量声明
- fn = 1执行,fn作为变量名被赋值为1.
- 执行fn(fn)时,fn已经被赋值一个变量1,不是一个函数,所以log会报错。
8.如下代码的输出?为什么 (难度**)
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
}var j = 100;
console.log(i);
console.log(j);
- js只在函数内部才有局部作用域,所以执行for语句i,j作为全局变量申明前置
- 第一次执行console.log(j);console.log(i);时i、j均没有初始化,所以输出undefined。
- 第二次执行console.log(j);console.log(i);时,for语句已经执行完,i、j分别被赋值10和100,所以输出10、100.
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; }
结果:Undefined,100,10 - 首先执行js之前,变量声明前置var i和var fn,函数fn()声明前置会覆盖var fn
- 执行fn()函数,fn()函数内作用域声明提前var i和fn2(),console.log(i)输出undefined。
- i=99,赋值操作,执行fn2(),由于fn2()内部作用域中的i没有声明,所以到上级作用域查找i。上级fn()中i已被声明,所以使用这个变量赋值100;
- console.log(i);i已被赋值100,所以输出100
- 之后执行i=10,fn=20操作,全局作用域的i被赋值10,所以console.log(i);输出10
10.如下代码的输出?为什么 (难度*)
var say = 0;
(function say(n){
}( 10 ));console.log(n); if(n<3) return; say(n-1);
console.log(say);
- 首先var say声明前置,之后执行say=0;再执行立即执行函数say();
- 立即执行函数执行完即被销毁,所有变量只在括号()内部有效,传入参数10,所以函数内部console.log(n);输出10,if语句判断,n<3,条件不满足,执行递归函数say(n-1);
- 参数变成n-1=9,输出9,if条件不满足,继续执行say(n-1),一次输出8,7,6,5,4,3,2。
- 执行console.log(say),全局作用域中say=0,输出0;