函数是对象
定义函数的4种方式
具名函数
// 函数名为fnfunction fn (x, y) {return x + y}fn() // 调用函数
匿名函数
// 将函数的地址复制给a, 右边的也叫函数表达式let a = function (x, y) {return x + y}a() // 调用函数
箭头函数
let f1 = () => console.log("hello") // 无参数f1()let f2 = x => x * x // 一个参数, 省略了returnf2(10)let f3 = (x, y) => {return x + y} // 两个参数就要加(), 有return或2个以上语句就要加{}f3(2, 3)let f4 = x => ({name: x}) // {}优先解释为代码块,返回对象要加()
构造函数
let fn = new Function('x', 'y', 'return x + y')// 所有函数都由Function构造出来
调用时机
let a = 1function fn(){setTimeout(()=>{console.log(a)},0)}fn()a = 2// 打印出2
let i = 0for(i = 0; i<6; i++){setTimeout(()=>{console.log(i)},0)}// 打印出6个6
for(let i = 0; i<6; i++){setTimeout(()=>{console.log(i)},0)}// 打印出0,1,2,3,4,5// JS在for和let一起用时会加东西,每次循环都保留那个i,再新创建一个i
作用域
let 的作用域只在{}里
function fn(){let a = 1}fn()console.log(a) // 访问不到a
在顶级作用域声明的变量为全局变量
window的属性为全局变量
其他为局部变量
给window加属性,可以在任何位置
let b = 1function f2() {window.c = 2 // 给window添加属性}f2() // 必须执行,才能声明cfunction f1() {console.log(c)}f1()
函数嵌套
function f1(){let a = 1function f2(){let a = 2console.log(a) // // step2: a = 2}console.log(a) // step1: a = 1a = 3f2()}f1()// 输出结果为 1, 2
function f1(){let a = 1function f2(){let a = 2function f3(){console.log(a)}a = 22f3() // step2: a = 22}console.log(a) // step1: a = 1a = 100f2()}f1()// 输出结果为: 1, 22
作用域规则
- 如果多个作用域有同名变量a, 查找a的声明时,向上取最近的作用域
- 简称“就近原则”
- 查找a的过程与函数执行无关
- 但a的值与函数执行有关
- 与函数执行无关的作用域叫静态作用域(词法作用域)
闭包 closure**
如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包
形参和返回值
形参 parameter 实参 argument
形参只是声明
function add(x) {return x + arguments[1]}add(1, 2)
返回值
- 只有函数才有返回值
- 且每个函数都有返回值,没写return则返回undefined
function xxx() {return console.log("hi") // 返回值是undefined}
调用栈
JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组里,这个数组叫做调用栈。
等函数执行完毕,就会把环境pop出来,回到之前的环境,继续执行后续代码
递归:先递进后回归 递进是压栈,回归是弹栈
函数提升
function fn(){}
不管把具名函数声明在哪里,都会跑到第一行
let fn = function(){}
用匿名函数赋值变量不会函数提升
let不允许重复声明
arguments 和 this
JS三座大山
- 闭包
- this
- AJAX
翻过三座大山才算入门
arguments是包含所有参数的伪数组
调用fn就传入arguments
不给条件,this默认指向window
用call传入this,如果传入的不是对象,会被自动转为对象
‘use strict’ 阻止转换
调用对象里的函数,this默认传的是对象本身
可以使用call显示的传入this
call第一个参数是this, 之后的参数都是arguments

总结:
- 在 new fn() 调用中,fn 里的 this 指向新生成的对象,这是 new 决定的
- 在 fn() 调用中, this 默认指向 window,这是浏览器决定的
- 在 obj.fn() 调用中, this 默认指向 obj,这是 JS 的隐式传 this
- 在 fn.call(xxx) 调用中,this 就是 xxx,这是开发者通过 call 显式指定的 this
- 在 arrow() 调用中,arrow 里面的 this 就是 arrow 外面的 this,因为箭头函数里面没有自己的 this
- 在 arrow.call(xxx) 调用中,arrow 里面的 this 还是 arrow 外面的 this,因为箭头函数里面没有自己的 this
以后都使用call调用函数
fn.call(undefined, 1, 2, 3)
fn.apply(undefined, [1, 2, 3])
绑定this
使用.bind让this不被改变
function f1(p1, p2){console.log(this, p1, p2)}let f2 = f1.bind({name:'xxx'})f2() // 等价于 f1.call({name:'xxx'})// 也可以绑定参数let f3 = f1.bind({name:'xxx'}, 1, 2}
箭头函数没有arguments和this
立即执行函数
立即执行函数用来造局部变量
用let可以轻松制造局部变量

