一、函数声明和函数表达式有什么区别
首先先来看一下什么叫函数声明和函数表达式
console.log(fn1())
console.log(fn2())
//函数声明
function fn1 (){
return "This is fn1."
}
//函数表达式
var fn2 = function (){
return "This is fn2."
}
上面得到的结果是 fn1 运行成功, fn2 是undefined
,那么,我们现在来分析一下为什么。JS 引擎在解释 JS 语句的时候,会先把变量声明和函数声明前置。变量声明前置是先定义变量,而函数声明前置是把整个函数提升到最前,而函数表达式和变量声明没区别。所以上面的例子可以同等替换成一下:
//函数声明前置 ==> 此时 fn1 函数已经完整定义了
function fn1 (){
return "This is fn1."
}
//函数表达式前置 ===> 只是简单声明变量,值还是 undefined
var fn2
console.log(fn1())
console.log(fn2())
//函数表达式
fn2 = function (){
return "This is fn2."
}
二、什么是变量的声明前置?什么是函数的声明前置
JS 引擎在解释 JS 语句的时候,会先把变量声明和函数声明前置。变量声明前置是先定义变量,而函数声明前置是把整个函数提升到最前,而函数表达式和变量声明没区别。举个例子:
console.log(value)
console.log(fn())
var value = 1
function fn(){
return "OK"
}
实际上是:
var value
function fn(){
return "OK"
}
console.log(value) //undefined
console.log(fn()) //"OK"
value = 1
三、arguments 是什么
arguments
是 JS 中的一个内置对象,是所有函数中可用的局部变量,里面存放着所有传进函数的参数,第一个参数键值是 "0" ,第二个是 "1" · · · 同时该对象还有length
键值,所以这是一个类数组对象。直接上例子:
function test(){
return arguments
}
console.log(test(1,2,3,4))
输出结果如下:
所有传进来的参数均可从这里访问,这样可以让函数处理指定一些不固定参数数量的情况,或者实现重载。
四、函数的"重载"怎样实现
重载就是:函数的方法名一样,而参数表不同的函数。打个比方,在 Java 中,方法名称相同,但参数顺序、名字、类型不同,编辑器就会认为这是两个不同的方法,可以同时存在。而 JS 中,相同名称的函数会被覆盖,因为 JS 函数虽然叫函数但本质还是对象,同一个对象不会共存。那在 JS 中如何实现?
既然重载是根据参数的不同,有不同的方法,那么刚好,JS 里面就有一个内置对象叫arguments
,我们可以用这个来判断一下当前所有参数的长度、类型等,做出不同的效果,举个简单的例子:
function overLoading() {
// 根据arguments.length,对不同的值进行不同的操作
switch(arguments.length) {
case 0:
/*操作1的代码写在这里*/
break;
case 1:
/*操作2的代码写在这里*/
break;
case 2:
/*操作3的代码写在这里*/
//后面还有很多的case......
}
}
更深层次的例子在参考链接里面。
参考地址:浅谈 Javascript 函数重载 | 渔歌 - 博客园
五、立即执行函数表达式是什么?有什么作用
1. 什么是立即执行函数?
立即执行函数(IIFE - Immediately Invoked Function Expressions)就是声明一个匿名函数,然后马上执行,通常写法是:
(function(){/* code */})()
(function(){/* code */}())
还有更多其他写法,但都差不多,可以自行查找
2. 有什么作用?
开辟独立的作用域,避免变量污染。
六、求 n!,用递归来实现
function factorial(n){
if(typeof n === 'number' || !isNaN(Number(n))){
if(1 === n || 0 === n){
return 1
}else {
return n * factorial(n - 1)
}
} else {
return new Error("Please enter the correct parameters!")
}
}
console.log(factorial(5))
七、以下代码输出什么?
题目
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('男');
结果
//1.
/*
name: 饥人谷
age: 2
sex: 男
{
0: 饥人谷,
1: 2,
2: 男,
length: 3,
callee: f(),
}
name: valley
*/
//2.
/*
name: 小谷
age: 3
sex: undefined
{
0: 小谷,
1: 3,
length: 2,
callee: f(),
}
name: valley
*/
//3.
/*
name: 男
age: undefined
sex: undefined
{
0: 男,
length: 1,
callee: f(),
}
name: valley
*/
八、写一个函数,返回参数的平方和?
function sumOfSquares(){
//这里下面开始写代码
var sum = 0
for(var n = 0; n < arguments.length; n++){
sum += arguments[n] * arguments[n]
}
return sum
//这里上面开始写代码
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
九、如下代码的输出?为什么
题目
console.log(a);
var a = 1;
console.log(b);
结果
// js 解析器会对 js 的变量、函数声明进行前置,先定义后使用,所以a 是定义了的,就是还没赋值
undefined
// b 并没有定义
error: b is not define
十、如下代码的输出?为什么
题目
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
结果
// 函数声明的前置,是直接把函数提到最前,这是一个完整的函数,是可以访问的
hello world
//第二种实际上是变量声明的前置,因为函数赋值给了变量,而变量声明前置只是先声明,还没有赋值,所以提前使用就出错
error: sayName is not a function
十一、如下代码输出什么? 写出作用域链查找过程伪代码
题目
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
结果
//输出
10
//作用域链
//1. 全局上下文
globalContext = {
AO: {
x: 10,
foo: function,
bar: function,
},
scope: null
}
fooContext[[scope]] = globalContext.AO
barContext[[scope]] = globalContext.AO
//2. bar 上下文
barContext = {
AO: {
x: 30
},
scope: globalContext.AO
}
//3. foo 上下文
fooContext = {
AO: {},
scope: globalContext.AO
}
//在 bar 函数里面运行 foo, foo 函数没有AO x ,只能向 scope 查找 x ,最后找到 global 的 x,就是 10
十二、如下代码输出什么? 写出作用域链查找过程伪代码
题目
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
结果
//输出
30
//1. 全局上下文
globalContext = {
AO: {
x: 10,
bar: function
},
scope: null
}
barContext[[scope]] = globalContext.AO
//2. bar 的上下文
barContext = {
AO: {
x: 30,
foo: function
},
scope: globalContext.AO
}
fooContext[[scope]] = barContext.AO
//3. foo 的上下文
fooContext = {
AO: {},
scope: barContext.AO
}
//在 bar 内运行 foo,由于 foo 是在 bar 内定义的,所以 foo 的 scope 就是 bar 的 AO。当 foo 内找不到 x ,就去 bar 里面找;再找不到,再去 bar 的 scope 里面找,直到 null。
十三、以下代码输出什么? 写出作用域链的查找过程伪代码
题目
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
结果
//输出
30
//1. global 上下文
globalContext = {
AO: {
x: 10,
bar: function
},
scope: null
}
barContext[[scope]] = globalContext.AO
//2. bar 上下文
barContext = {
AO: {
x: 30,
anonymousFunction: function
},
scope: globalContext.AO
}
anonymousFunction[[scope]] = barContext.AO
//3. 匿名函数上下文
anonymousFunctionContext = {
AO: {},
scope: barContext.AO
}
//这是个立即执行的匿名函数,其实和上一题没有多大区别,这个函数同样也是在 bar 里面定义的。
十四、以下代码输出什么? 写出作用域链查找过程伪代码
题目
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. global 上下文
globalContext = {
AO: {
a: 1,
fn: function,
fn3: function
},
scope: null
}
//2. fn 上下文
fnContext = {
AO: {
a: 5,
fn2: function
},
scope: globalContext.AO
}
//3. fn2 上下文
fn2Context = {
AO: {},
scope: fnContext.AO
}
//4. fn3 上下文
fn3Context = {
AO: {},
scope: globalContext.AO
}
//只要理解在当前 AO 中没有的值,就会去找 scope ,然后就是 scope 的 scope,知道找到对应值为止。