Q1函数声明和函数表达式有什么区别
函数声明 VS 函数表达式
JavaScript 中需要创建函数的话,有两种方法:函数声明、函数表达式,各自写法如下:
// 方法一:函数声明
function foo() {}
// 方法二:函数表达式
var foo = function () {};
另外还有一种自执行函数表达式,主要用于创建一个新的作用域,在此作用域内声明的变量不会和其它作用域内的变量冲突或混淆,大多是以匿名函数方式存在,且立即自动执行:
(function () {
// var x = ...
})();
此种自执行函数表达式归类于以上两种方法的第二种,也算是函数表达式。
方法一和方法二都创建了一个函数,且命名为 foo
,但是二者还是有区别的。JavaScript 解释器中存在一种变量声明被提升(hoisting)的机制,也就是说变量(函数)的声明会被提升到作用域的最前面,即使写代码的时候是写在最后面,也还是会被提升至最前面。
例如以下代码段:
alert(foo); // function foo() {}
alert(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}
输出结果分别是function foo() {}、undefined、function foo() {}和function bar_fn() {}。
可以看到 foo的声明是写在 alert 之后,仍然可以被正确调用,因为 JavaScript 解释器会将其提升到 alert 前面,而以函数表达式创建的函数 bar则不享受此待遇。
那么bar究竟有没有被提升呢,其实用 var 声明的变量都会被提升,只不过是被先赋值为 undefined罢了,所以第二个 alert 弹出了 undefined。
所以,JavaScript 引擎执行以上代码的顺序可能是这样的:
1.创建变量 foo和 bar,并将它们都赋值为 undefined。
2.创建函数 foo的函数体,并将其赋值给变量 foo。
3.执行前面的两个 alert。
4.创建函数 bar_fn,并将其赋值给 bar。
5.执行后面的两个 alert。
注:
严格地说,再 JavaScript 中创建函数的话,还有另外一种方法,称为“函数构造法”:
var foo = Function('alert("hi!");');
var foo = new Function('alert("hi!");'); // 等同于上面一行
此方法以一个字符串作为参数形成函数体。但是用这种方法,执行效率方面会打折扣,且似乎无法传递参数,所以少用为妙。
翻译整理自:http://www.reddit.com/r/javascript/comments/v9uzg/the_different_ways_to_write_a_function/
Q2什么是变量的声明前置?什么是函数的声明前置
什么是变量的声明前置?
什么是函数的声明前置?
和变量的声明会前置一样,函数声明同样会前置,如果我们使用函数表达式那么规则和变量一样,如下图:
如果我们使用函数声明的方式,那么即使函数写在最后也可以在前面语句调用,前提是函数声明部分已经被下载到本地。
Q3arguments 是什么
是一个长的很像数组的对象,可以通过该对象获取到函数的所有传入参数。
Q4函数的"重载"怎样实现
本文介绍了在javascript中如何实现函数/方法的重载效果,主要是利用了JS函数的arguments对象来访问函数的所有参数,根据判断参数数量来进行不同的功能实现,从而模拟出函数重载的效果。
为什么要实现JS的函数重载?
在C#和JAVA等编程语言中函数重载是指在一个类中可以定义多个方法名相同但是方法参数和顺序不同的方法,以此来实现不同的功能和操作,这就是重载。JS中模拟重载也是一样的意思。
但是js本身没有重载,因为在JS中如果定义了多个相同的函数名称,那么最终只有最后一个定义的函数属于有效的函数,其他之前定义的函数都无效定义。造成此问题是由于javascript属于弱类型语言。比如下面的示例代码:
我们传入了参数100,最终计算结果和网页弹出框显示的是300。因此我们如果想要在JS中用上重载的效果,就必须自己模拟和实现出来。
JS如何实现函数/方法重载?
这里直接上代码:
在现实合计的方法showSum中,我们分别模拟重载3种计算方式,如果传入一个数字就加一并显示,传入两个和三个就将这些数值相加取和值并显示出来。
之所以可以使用arguments对象来实现重载,是因为js函数的参数并不是和其他语言那样必须固定声明,而是在函数内部以一个数组来表示传入的参数。也就是无论你传入多少的参数,什么类型的参数,最终所有参数在JS函数里面都是以一个arguments对象(参数数组)来表示的。所以在上面的代码中我们根据arguments对象的参数长度来判断最终要实现哪种计算方式,实现的效果和重载的效果是类似的。
而平时我们在JS中声明的函数显示命名,也是可以调用arguments对象来获取参数值,比如下面两个参数获取的值都是一样的:
这样就很好实现了重载效果,关键就是使用js中的arguments对象。
Q5立即执行函数表达式是什么?有什么作用
立即调用函数表达式(英文:immediately-invoked function expression,缩写:IIFE)[1]
,是一种利用JavaScript函数生成新作用域的编程方法。
表达式:(function(){ console.log("test");})(); // test
或者(function(){ console.log("test");}()); // test
IIFE的作用:
为什么要用立即执行函数表达式呢?有以下几个场景。
1.模拟块作用域 众所周知,JavaScript没有C或Java中的块作用域(block),只有函数作用域,在同时调用多个库的情况下,很容易造成对象或者变量的覆盖,比如:
liba.js
var num = 1;// code....libb.js
var num = 2;// code....
如果在页面中同时引用liba.js和liba.js两个库,必然导致num变量被覆盖,为了解决这个问题,可以通过IIFE来解决:
liba.js
(function(){ var num = 1; // code....})();libb.js
(function(){ var num = 2; // code....})();
经过改造之后,两个库的代码就完全独立,并不会互相影响。
2.解决闭包冲突
闭包(closure)是JavaScript的一个语言特性,简单来说就是在函数内部所定义的函数可以持有外层函数的执行环境,即使在外层函数已经执行完毕的情况下,在这里就不详细介绍了,感兴趣的可以自行Google。我们这里只举一个由闭包引起的最常见的问题:
var f1 = function() { var res = [];
var fun = null;
for(var i = 0; i < 10; i++) {
fun = function()
{ console.log(i);
};//产生闭包
res.push(fun);
}
return res;
}// 会输出10个10,而不是预期的0 1 2 3 4 5 6 7 8 9
var res = f1();
for(var i = 0;
i < res.length; i++) {
resi;
}
修改成:
var f1 = function() { var res = [];
for(var i = 0; i < 10; i++) {
// 添加一个IIFE
(function(index) {
fun = function() {console.log(index);};
res.push(fun);
})(i);
}
return res;
}
// 输出结果为0 1 2 3 4 5 6 7 8 9
var res = f1();
for(var i = 0; i < res.length; i++) {
resi;
}
Q6.求n!,用递归来实现
function factorial(n){
return n > 1 ? n * factorial(n-1) : 1;
}
factorial(5);//120
Q7.以下代码输出什么?
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('男');
输出:
Q8. 写一个函数,返回参数的平方和?
function sumOfSquares(){
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result) //10
Q9. 如下代码的输出?为什么
console.log(a);//undefined;变量声明提前,此时并未赋值
var a = 1;
console.log(b);//error:b is not defined;没声明b报错
Q10. 如下代码的输出?为什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
//hello world
sayAge is not a function(报错)
函数声明会在代码执行前首先读取,而函数表达式要在代码执行到那一句时,才会函数才被定义(函数声明提升)
Q11.如下代码输出什么? 写出作用域链查找过程伪代码
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
global Context={
AO:{
x:10
foo:function
bar:function
}
scope:null
foo.[[scope]]=globalContext.AO
bar.[[scope]]=globalContext.AO
barContext={
AO:{
x:30
}
scope:bar.[[scope]]//globalContext.AO
fooContext:{
AO:{}
scope:foo.[[scope]]//globalContext.AO
最后输出的是:10
Q12.如下代码输出什么? 写出作用域链查找过程伪代码
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
global Context={
AO:{
x:10
bar:function
}
scope:null
}
bar.[[scope]]=globalContext.AO
barContext={
AO:{
x:30
foo:function
}
scope:bar.[[scope]]// globalContext.AO
foo.[[scope]]=barContext.AO
fooContext={
AO:{}
scope:foo.[[scope]]//barContext.AO
最后输出的是:30
Q13. 以下代码输出什么? 写出作用域链的查找过程伪代码
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
global Context={
AO:{
x:10
bar:function
}
scope:null
}
bar.[[scope]]=globalContext.AO
bar Context={
AO:{
x:30
function
}
scope:bar.[[scope]]//globalContext.AO
}
function[[scope]]=barContext.AO
functionContext={
AO:{},
scope:function[[scope]]// barContext.AO
}
最后输出的是:30
Q14以下代码输出什么? 写出作用域链查找过程伪代码
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)
global Context:{
AO:{
a:1--200
fn:function
fn3:function
}
scope:null
}
fn.[[scope]]=globalContext.AO
fn3.[[scope]]=globalContext.AO
fn Context:{
AO:{
a:undefinted--5--6--20
fn3:function
fn2:function
}
scope:global Context.AO
}
fn2.[[scope]]=fnContext.AO
fn2 Context:{
AO:{
}
scope:fn Context.AO
}
fn3 Context:{
AO:{
}
scope:global Context.AO
}
输出:undefinted 5 1 6 20 200