We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在 JavaScript 中,函数实际上是一个对象。
JavaScript 用 function 关键字来声明一个函数:
function fn () { }
变体:函数表达式:
var fn = function () { }
这种没有函数名的函数被称为匿名函数表达式。
函数可以有返回值
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 这个类数组的对象来访问参数,即便没有命名参数:
// 有命名参数 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 这个指针访问拥有这个 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,我觉得还是少用为好。
具体查看总结:
引用《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
用表格模拟过程:
将 a 复制为其他值后:
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 也不会改变什么。
参数 a 只是 复制了 obj 的引用,所以 a 能找到存在堆内存中的对象,所以 a 能对堆内存中的对象进行修改后:
因此,基本类型和引用类型的参数传递也是按值传递的
理解作用域链之前,我们需要理解执行环境 和 变量对象。
执行环境定义了变量或者函数有权访问的其它数据,可以把执行环境理解为一个大管家。
执行环境分为全局执行环境和函数执行环境,全局执行环境被认为是 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 } }
箭头函数的特点:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在 JavaScript 中,函数实际上是一个对象。
🏌 声明
JavaScript 用 function 关键字来声明一个函数:
变体:函数表达式:
这种没有函数名的函数被称为匿名函数表达式。
🤾 return
函数可以有返回值
位于 return 之后的任何代码都不会执行:
没有 return 或者只写 return,函数将返回 undefined:
⛹ 参数
函数可以带有限个数或者不限个数的参数
没有传值的命名参数,会被自动设置为 undefined
🚣 arguments
函数可以通过内部属性 arguments 这个类数组的对象来访问参数,即便没有命名参数:
⛳️ 长度
arguments 的长度由传入的参数决定,并不是定义函数时决定的。
如果按定义函数是决定个的,那么此时的 arguments.length 应该为 0 而不为 3。
🏓 同步
arguments 对象中的值会自动反应到对应的命名参数,可以理解为同步,不过并不是因为它们读取了相同的内存空间,而只是保持值同步而已。
严格模式下,重写 arguments 的值会导致错误。
🏸 callee
通过 callee 这个指针访问拥有这个 arguments 对象的函数
🏒 类数组
长的跟数组一样,可以通过下标访问,如 arguments[0],却无法使用数组的内置方法,如 forEach 等:
通过对象那章知道,可以用 call 或者 apply 借用函数,所以 arguments 可以借用数组的内置方法:
对于如此诡异的 arguments,我觉得还是少用为好。
🤺 this、 prototype
具体查看总结:
🏋 按值传递
引用《JavaScript 高级程序设计》4.1.3 的一句话:
🎺 基本类型的参数传递
基本类型的传递很好理解,就是把变量复制给函数的参数,变量和参数是完全独立的两个个体:
用表格模拟过程:
将 a 复制为其他值后:
🎻 引用类型的参数传递
嗯?说好的按值传递呢?我们尝试把 a 赋值为其他值,看看会不会改变了 obj 的值:
🎸 真相浮出水面
参数 a 只是复制了 obj 的引用,所以 a 能找到对象 obj,自然能对其进行操作。一旦 a 赋值为其他属性了,obj 也不会改变什么。
用表格模拟过程:
参数 a 只是 复制了 obj 的引用,所以 a 能找到存在堆内存中的对象,所以 a 能对堆内存中的对象进行修改后:
将 a 复制为其他值后:
因此,基本类型和引用类型的参数传递也是按值传递的
🚴 作用域链
理解作用域链之前,我们需要理解执行环境 和 变量对象。
🍗 执行环境
执行环境定义了变量或者函数有权访问的其它数据,可以把执行环境理解为一个大管家。
执行环境分为全局执行环境和函数执行环境,全局执行环境被认为是 window 对象。而函数的执行环境则是由函数创建的。
每当一个函数被执行,就会被推入一个环境栈中,执行完就会被推出,环境栈最底下一直是全局执行环境,只有当关闭网页或者推出浏览器,全局执行环境才会被摧毁。
🍖 变量对象
每个执行环境都有一个变量对象,存放着环境中定义的所有变量和函数,是作用域链形成的前置条件。但我们无法直接使用这个变量对象,该对象主要是给 JS 引擎使用的。具体可以查看《JS 总结之变量对象》。
🍤 作用域链的作用
而作用域链属于执行环境的一个变量,作用域链收集着所有有序的变量对象,函数执行环境中函数自身的变量对象(此时称为活动对象)放置在作用域链的最前端,如:
作用域链保证了对执行环境有权访问的所有变量和函数的有序访问。
对于 fn2 来说,作用域链为: fn2 执行环境、fn1 执行环境 和 全局执行环境 的变量对象(所有变量和函数)。
对于 fn1 来说,作用域链为: fn1 执行环境 和 全局执行环境 的变量对象(所有变量和函数)。
总结为一句:函数内部能访问到函数外部的值,函数外部无法范围到函数内部的值。引出了闭包的概念,查看总结:《JS 总结之闭包》
🏇 箭头函数
ES6 新语法,使用 => 定义一个函数:
当只有一个参数的时候,可以省略括号:
当只有一个返回值没有其他语句时,可以省略大括号:
返回对象并且没有其他语句的时候,大括号需要括号包裹起来,因为 js 引擎认为大括号是代码块:
箭头函数的特点:
🚀 参考
The text was updated successfully, but these errors were encountered: