函数与作用域
2017-12-17 本文已影响0人
akena
函数声明和函数表达式
- 函数声明
function fn() {}
- 必须有函数名
- 函数可以在任意地方调用
- 函数表达式
var fn = function fn() {}
-
function
后函数名可以省略 -
function
后函数名只能在函数内部使用 - 函数表达式只能在声明之后才能调用
-
作用域和声明提升
-
作用域
- 变量作用域
- 全局变量拥有全局作用域,局部变量只在函数体内有定义
- 函数体内,局部变量优先级高于同名的全局变量
- 函数作用域
- 与
块级作用域
概念类似的,JavaScript 使用函数作用域(function scope
),即在函数体内声明的所有变量在函数体内可以使用及复用
- 与
- 变量作用域
-
声明提升
在代码开始执行之前,解析器会在初始化进程中创建全局变量对象
VO(variable object)
,VO
中包含全局对象原有属性,全局定义的变量和函数-
VO
的变化与上下文代码的两个执行阶段有关- 变量初始化,进入执行上下文
- 执行代码
- 在变量初始化阶段,
VO
会被以下属性按优先级顺序填充- 函数参数(未传入实参时,初始值为
undefined
) - 函数声明(存在相同名称的属性时,会完全替换该属性)
- 变量声明(初始值为
undefined
,存在相同名称的属性时会忽略该声明)
- 函数参数(未传入实参时,初始值为
- 在执行代码阶段,依据以上流程,可得知 JavaScript
声明提升(declaration hoisting)
特性的结果,即:- 将 JavaScript 函数声明的所有变量添加到执行环境中,并将它们按优先级顺序放到源代码树的顶部,故函数声明提升在变量声明提升之前
var haha = function() { console.log('赋值给变量haha') } function haha() { console.log('具名函数haha') } haha(); //赋值给变量haha
-
arguments
对象
-
arguments
对象是所有函数中都可用的局部变量,此对象包含传递给函数的每个参数的条目,示例:arguments[0] //对应第一个参数的条目 arguments[1] //类推,第二个 arguments[2] arguments[1] = 'new Value'; //参数可被设置
-
arguments
对象类似Array
,但不是Array
,除了length
属性来确定传递参数个数外没有任何Array
属性
函数的重载
- Java 和 JavaScript 的对比
- Java 中,通过方法签名来唯一确定一个方法。方法签名的要素包括:方法名、参数类型、参数顺序和参数个数。若两个方法名称相同,但其他要素不同,编译器便将其视为同名的不同方法,从而导致了
重载
现象 - JavaScript 中,函数没有签名,其参数由包含零或多个值的数组来表示,完全靠函数名称唯一确定。当存在相同名称的函数时,则后者会覆盖前者,因而不存在
重载
- Java 中,通过方法签名来唯一确定一个方法。方法签名的要素包括:方法名、参数类型、参数顺序和参数个数。若两个方法名称相同,但其他要素不同,编译器便将其视为同名的不同方法,从而导致了
- JavaScript 模仿函数的
重载
//通过检查传入函数中参数的数量,间接达到重载的目的 function add(num1, num2) { if(arguments.length == 1) { console.log(arguments[0] + "input 2nd parameter") } if(arguments.length == 2) { console.log(arguments[0] + arguments[1]) } }
立即执行函数表达式 IIFE
IIFE
即声明一个匿名函数并即时调用
- 两种模式
- 当圆括号包裹函数时,它会默认将函数作为表达式去解析,而不是函数声明
(function() { /* code */}());
- 当圆括号出现在匿名函数的末尾想要调用函数时,它会默认将函数当成是函数声明
(function() { /* code */})();
- 当圆括号包裹函数时,它会默认将函数作为表达式去解析,而不是函数声明
- 用途
- 创建一个独立的作用域,避免全局污染
// 这是一个自执行函数,函数内部执行的是自己,递归调用
function foo() { foo(); }
// 这是一个自执行匿名函数,因为它没有函数名
// 所以如果要递归调用自己的话必须用arguments.callee
var foo = function() { arguments.callee(); };
// 这可能也算是个自执行匿名函数,但仅仅是foo标志引用它自身
// 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数
var foo = function() { foo(); };
// 有些人叫它自执行匿名函数,尽管它没有执行自己,只是立即执行而已
(function(){ /* code */ }());
// 给函数表达式添加了标志名称,可以方便debug
// 但是一旦添加了标志名称,这个函数就不再是匿名的了
(function foo(){ /* code */ }());
// 立即执行函数也可以自执行,不过不常用罢了
(function(){ arguments.callee(); }());
(function foo(){ foo(); }());
递归求n!
function fn(i) {
if(i < 2) {
return 1
}else {
return i*fn(i-1)
}
}
//简写
function fn(i) {
return i<2?1:i*fn(i-1)
}
refer to 变量对象 | cnblogs,立即执行函数表达式(IIFE) | segmentfault,立即执行函数 | 每日一题