函数是第一型对象
就像是 JavaScript 中的其他任意对象一样。和其他任意对象类型一样,它们有如下特点:
- 通过字面量进行创建
function fn(x) { console.log(x) }
- 赋值给变量或属性
var fn = function (x) { console.log(x) }
var obj = {}
obj.fn = function (x) { console.log(x) }
- 作为参数进行传递
var fn1 = function () { console.log('fn1') }
var fn2 = function (fn) { fn() }
fn2(fn1) // "fn1"
- 作为函数结果进行返回
var createFn = function () {
return function () {
console.log('a created function')
}
}
var fn = createFn()
fn() // "a created function"
- 拥有属性和方法
function fn() {}
fn.name // "fn"
fn.toString() // "function fn() {}"
函数作用域
函数内部的作用域与大多数其他语言的作用域不同。具体来说,有如下几种。
- 变量的作用域开始于声明处,结束于函数尾部,其会跨域边界块(如大括号)。
- 内部函数在当前函数的任何地方都可用(提升),即便是提前引用。
函数的形参列表和实际参数列表的长度可以是不同的
- 未赋值的参数被设置为
undefined
。 - 多出的参数是不会绑定到参数名称的。
function log(a, b) {
console.log(a, b)
}
log(1) // 1 undefined
log(1, 2) // 1 2
log(1, 2, 3) // 1 2
每个函数调用都会传入两个隐式参数
-
arguments
,实际传入的参数集合。
function log() {
for (let i of arguments) {
console.log(i)
}
}
log(1, 2, 3) // 1 2 3
-
this
,作为函数上下文的对象引用。
function log() {
console.log(this === window) // 作为普通函数进行调用时`this`指向`window`
}
log() // true
可以用不同的方法进行函数调用,不同的调用机制决定了函数上下文的不同
- 作为普通函数进行调用时,其上下文是全局对象(在浏览器中是
window
,在 Nodejs 中是global
)。 - 作为方法进行调用时,其上下文是拥有该方法的对象。
var obj = {
username: '老王',
hello: function () {
console.log(this.username)
}
}
obj.hello() // 老王
- 作为构造器进行调用时,其上下文是新分配的一个对象。
function Person(name) {
this.name = name
this.hello = function () {
console.log(`Hello ${this.name}`)
}
}
var laowang = new Person('老王') // laowang是新分配的对象,this指向laowang
laowang.hello() // Hello 老王
构造器调用时,会发生如下特殊行为。
a. 创建一个新的空对象。
b. 传递给构造器的对象是this
参数,从而成为构造器的函数上下文。
c. 如果没有显式的返回值,新创建的对象则作为构造器的返回值进行返回。
- 通过函数的
apply()
或call()
方法进行调用时,上下文可以设置成任意值。