在 JavaScript 中,函数实际上是一个对象。
🏌 声明
JavaScript 用 function 关键字来声明一个函数:
function fn () {
}
变体:函数表达式:
var fn = function () {
}
这类没有函数名的函数被称为匿名函数表达式。
🤾 return
函数能够有返回值
function fn () {
return true
}
位于 return 以后的任何代码都不会实行:
function fn () {
return true
console.log(false) // 永久不会实行
}
fn() // true
没有 return 或许只写 return,函数将返回 undefined:
function fn () {
}
fn() // undefined
// 或许
function fn () {
return
}
fn() // undefined
⛹ 参数
函数能够带有限个数或许不限个数的参数
// 参数有限
function fn (a, b) {
console.log(a, b)
}
// 参数不限
function fn (a, b, ..., argN) {
console.log(a, b, ..., argN)
}
没有传值的定名参数,会被自动设置为 undefined
// 参数有限
function fn (a, b) {
console.log(b) // undefined
}
fn(1)
🚣 arguments
函数能够经由历程内部属性 arguments 这个类数组的对象来接见参数,即使没有定名参数:
// 有定名参数
function fn (a, b) {
console.log(arguments.length) // 2
}
fn(1, 2)
// 无定名参数
function fn () {
console.log(arguments[0], arguments[1], arguments[2]) // 1, 2, 3
}
fn(1, 2, 3)
⛳️ 长度
arguments 的长度由传入的参数决议,并非定义函数时决议的。
function fn () {
console.log(arguments.length) // 3
}
fn(1, 2, 3)
假如按定义函数是决议个的,那末此时的 arguments.length 应当为 0 而不为 3。
🏓 同步
arguments 对象中的值会自动反应到对应的定名参数,能够明白为同步,不过并非由于它们读取了雷同的内存空间,而只是坚持值同步罢了。
function fn (a) {
console.log(arguments[0]) // 1
a = 2
console.log(arguments[0]) // 2
arguments[0] = 3
console.log(a) // 3
}
fn(1)
严厉形式下,重写 arguments 的值会致使毛病。
🏸 callee
经由历程 callee 这个指针接见具有这个 arguments 对象的函数
function fn () {
console.log(arguments.callee) // fn
}
fn()
🏒 类数组
长的跟数组一样,能够经由历程下标接见,如 arguments[0],却没法运用数组的内置要领,如 forEach 等:
function fn () {
console.log(arguments[0], arguments[1]) // 1, 2
console.log(arguments.forEach) // undefined
}
fn(1, 2)
经由历程对象那章晓得,能够用 call 或许 apply 借用函数,所以 arguments 能够借用数组的内置要领:
function fn () {
Array.prototype.forEach.call(arguments, function (item) {
console.log(item)
})
}
fn(1, 2)
// 1
// 2
关于云云诡异的 arguments,我以为照样少用为好。
🤺 this、 prototype
详细检察总结:
🏋 按值通报
援用《JavaScript 高等程序设计》4.1.3 的一句话:
ECMAScript 中一切函数的参数都是按值通报的,也就是说,把函数外部的值复制给函数内部的参数,就和把一个变量复制到另一个变量一样。
🎺 基础范例的参数通报
基础范例的通报很好明白,就是把变量复制给函数的参数,变量和参数是完整自力的两个个别:
var name = 'jon'
function fn (a) {
a = 'karon'
console.log('a: ', a) // a: karon
}
fn(name)
console.log('name: ', name) // name: jon
用表格模仿历程:
栈内存 | 堆内存 | |
name, a | jon |
将 a 复制为其他值后:
栈内存 | 堆内存 | |
name | jon | |
a | karon |
🎻 援用范例的参数通报
var obj = {
name: 'jon'
}
function fn (a) {
a.name = 'karon'
console.log('a: ', a) // a: { name: 'karon' }
}
fn(obj)
console.log(obj) // name: { name: 'karon' }
嗯?说好的按值通报呢?我们尝试把 a 赋值为其他值,看看会不会转变了 obj 的值:
var obj = {
name: 'jon'
}
function fn (a) {
a = 'karon'
console.log('a: ', a) // a: karon
}
fn(obj)
console.log(obj) // name: { name: 'jon' }
🎸 原形浮出水面
参数 a 只是复制了 obj 的援用,所以 a 能找到对象 obj,天然能对其举行操纵。一旦 a 赋值为其他属性了,obj 也不会转变什么。
用表格模仿历程:
栈内存 | 堆内存 | |
obj, a | 援用值 | { name: ‘jon’ } |
参数 a 只是 复制了 obj 的援用,所以 a 能找到存在堆内存中的对象,所以 a 能对堆内存中的对象举行修改后:
栈内存 | 堆内存 | |
obj, a | 援用值 | { name: ‘karon’ } |
将 a 复制为其他值后:
栈内存 | 堆内存 | |
obj | 援用值 | { name: ‘karon’ } |
a | ‘karon’ |
因而,基础范例和援用范例的参数通报也是按值通报的
🚴 作用域链
明白作用域链之前,我们须要明白实行环境 和 变量对象。
🍗 实行环境
实行环境定义了变量或许函数有权接见的别的数据,能够把实行环境明白为一个大管家。
实行环境分为全局实行环境和函数实行环境,全局实行环境被以为是 window 对象。而函数的实行环境则是由函数建立的。
每当一个函数被实行,就会被推入一个环境栈中,实行完就会被推出,环境栈最底下一直是全局实行环境,只要当封闭网页或许推出浏览器,全局实行环境才会被摧毁。
🍖 变量对象
每一个实行环境都有一个变量对象,存放着环境中定义的一切变量和函数,是作用域链构成的前置条件。但我们没法直接运用这个变量对象,该对象主如果给 JS 引擎运用的。详细能够检察《JS 总结之变量对象》。
🍤 作用域链的作用
而作用域链属于实行环境的一个变量,作用域链网络着一切有序的变量对象,函数实行环境中函数本身的变量对象(此时称为运动对象)安排在作用域链的最前端,如:
scope: [函数本身的变量对象,变量对象1,变量对象2,..., 全局实行环境的变量对象]
作用域链保证了对实行环境有权接见的一切变量和函数的有序接见。
var a = 1
function fn1 () {
var b = 2
console.log(a,b) // 1, 2
function fn2 () {
var c = 3
console.log(a, b, c) // 1, 2, 3
}
fn2()
}
fn1()
关于 fn2 来讲,作用域链为: fn2 实行环境、fn1 实行环境 和 全局实行环境 的变量对象(一切变量和函数)。
关于 fn1 来讲,作用域链为: fn1 实行环境 和 全局实行环境 的变量对象(一切变量和函数)。
总结为一句:函数内部能接见到函数外部的值,函数外部没法局限到函数内部的值。引出了闭包的观点,检察总结:《JS 总结之闭包》
🏇 箭头函数
ES6 新语法,运用 => 定义一个函数:
let fn = () => {}
当只要一个参数的时刻,能够省略括号:
let fn = a => {}
当只要一个返回值没有其他语句时,能够省略大括号:
let fn = a => a
// 等同于
let fn = function (a) {
return a
}
返回对象而且没有其他语句的时刻,大括号须要括号包裹起来,由于 js 引擎以为大括号是代码块:
let fn = a => ({ name: a })
// 等同于
let fn = function (a) {
return { name: a }
}
箭头函数的特性:
- 没有 this,函数体内的 this 是定义时外部的 this
- 不能被 new,由于没有 this
- 不能够运用 arguments,能够运用 rest 替代
- 不能够运用 yield 敕令,因而箭头函数不能用作 Generator 函数。
🚀 参考
- 《JavaScript 深切之参数按值通报》 by 冴羽
- 《ECMAScript 6 入门》函数的扩大 – 箭头函数 by 阮一峰
- 《JavaScript 高等程序设计》3 基础观点、4.1.3 通报参数、4.2 实行环境及作用域