具名函数
function 函数名 (形参1, 形参2){语句return 返回值}
匿名函数
等号右边叫函数表达式let a = function (x, y) {语句;return 返回值;};
特殊情况
let a = function fn(x, y) {return x + y;};fn(1, 2);//会报错,=右边的fn只在=右边有效,全局调用没有fn,只能用a
箭头函数
let f1 = (x) => x * y;let f2 = (x, y) => {console.log("hi");return x * y;};let f3 = (x) => ({ name: "x" });
构造函数
- 基本没人用,但是能让你知道函数是谁构造的
- 所有函数都是 Function 构造出来的
- 包括 Object、Array、Function
let f1 = new Function("x", "y", console.log('hi'); "return x + y");
函数与函数调用
- fn 与 fn()
-
二、函数的要素
每个函数都有这些东西
let 在 for 外面
let i;for (i = 0; i < 6; i++) {setTimeout(() => {console.log(i);}, 0);}//打印6个6
let 在 for 里面
for (let i = 0; i < 6; i++) {setTimeout(() => {console.log(i);}, 0);}//打印 0,1,2,3,4,5
因为JS在for 和let一起用的时候会加东西
- 每次循环会多创建一个i,记录本次i的值2. 作用域
function fn() {let a = 1;}console.log(a);//报错 a 不存在,a为局部变量,只作用在fn里面
全局变量 局部变量
- 全局变量
- 在顶级作用域声明的变量
- window 的属性是全局变量
- 其他的都是局部变量
- 全局变量
函数可嵌套
function f1() {let a = 1;function f2() {let a = 2;console.log(a);}console.log(a);a = 3;f2();}f1();
- 作用域规则
- 如果多个作用域有同名变量 a
- 那么查找 a 的声明时,就向上取最近的作用域
- 简称[就近原则]
- 查找 a 的过程与函数执行无关
- 但 a 的值与函数执行有关
-
3. 闭包
如果一个函数用到它作用域外的变量,那么这个函数加这个变量,就叫做闭包
下面 f2 里的 a 和 f3 组成了闭包
function fi() {let a = 1;function f2() {let a = 2;function f3() {console.log(a);}a = 22;f3();}console.log(a);a = 100;f2();}f1();
4. 形式参数
形式参数的意思就是非实际参数
- 调用时传入的参数是实际参数(传入的是复制的地址)
形参的本质是变量声明
function add() {var x = arguments[0];var y = arguments[1];return x + y;}
-
5. 返回值
每个函数都有返回值
- 注意:console.log(‘hi’) 的值是 undefined
-
6. 调用栈
什么是调用栈
- JS 引擎在调用一个函数前
- 需要把函数所在的环境 push 到一个数组里
- 这个数组叫做调用栈
- 等函数执行完了,就会把环境弹出来(pop)
- 然后 return 到之前的环境,继续执行后续代码
- 举例

图片来自杭州饥人谷教程 递归函数(如阶乘)
function fn(n) {return n === 1 ? 1 : n * fn(n - 1);}
递归的调用栈
- 递进 - 压栈 - 一只递进一直压栈,递一次压一次栈,一直累积
- 回归 - 弹栈 - 开始回归才开始弹栈
递归压栈的贞超过最大值就会爆栈,报错
调用栈最大值测试代码:function computeMaxCallStackSize() {try {return 1 + computeMaxCallStackSize();} catch (e) {//报错说明 stack overflow 了return 1;}}
Chrome 12578 、Firefox 26773 、Node 12536 (取决于浏览器 JS 引擎)
7. 函数提升
函数提升
- function fn() {}
- 不管你把具名函数声明在哪里,他都会跑到第一行
不是函数提升
每个函数都有,除了箭头函数
argument 包含所有参数的伪数组,传参的同时会复制到 argument 里
9. this
每个函数都有,除了箭头函数
- 如果不给任何条件,this 默认指向 window
- 目前只能通过 call 来指定 this
如果传的 this 不是对象,JS 会自动帮你封装成对象
禁用此功能:在声明函数时加上字符串’use strict’function fn() {"use strict";console.log(this);}
但是,没有人会在定义函数的时候加上这句话。。
- call fn.call(xxx,2,3,4)
xxx(第一个),是 this
1,2,3(后面几个) 是 argument 假设没有 this
- 我们可以用直接保存了对象地址的变量获取’name’
- 我们把这种方法简称为引用
代码示例
let person = {name: "frank",sayHi() {console.log("你好,我叫" + person.name);},};
但是:
- 如果 person 改名了,sayHi 函数就挂了
- sayHi 函数甚至有可能在另一个文件里面
- 所以我们不希望 sayHi 函数出现 person 引用
用 this 获取那个对象(person)
let person = {name: "frank",sayHi() {//sayHi(隐藏了this)console.log(`你好,我叫` + this.name);},};
- this 两种调用法
- 小白调用法
person.sayHi() 默认传 person - 大师调用法person.sayHi.call(person) 可以传别的 this以后所有函数调用都用这种写法
- 如果函数里没有 this,call(里面要多传第一个参数 undefined,用来占位)
- 有 this,第一个参数传函数作用的东西(也就是 this 对应的),后面再传其他参数
- 小白调用法
- .call fn.call(xxx,2,3,4)
xxx(第一个),是 this
1,2,3(后面几个) 是 argument - .apply fn.apply(xxx,[2,3,4])
除了 this 后面参数加中括号[],其他的和 call 一样 .bind
- 让 this 不被改变
‘frank’})function f1(p1, p2){console.log(this, p1, p2)}let f2 = f2.bind({name:
- 让 this 不被改变
f2 就是 f1 绑定了 this 之后的新函数
f2() 等价于 f1.call({name: ‘frank’})- .bind 还可以绑定其他参数
let f3 = f1.bind({ name: "frank" }, "hi");
- .bind 还可以绑定其他参数
f3() 等价于 f1.call({name: ‘frank’}, ‘hi’)
10. 箭头函数
没有 argument 和 this
箭头函数里的 this 就是一个普通变量,相当于一个 a、b 之类的普通变量
console.log(this); //打印windowlet fn = () => console.log(this);fn(); //还是打印window
就算你加上 call 都没有用
fn.call({ name: "frank" }); //仍然打印window
11.立即执行函数
- 只有 JS 才有的变态玩意,现在很少用,一般用来声明局部变量
!(function () {var a = 2;console.log(a);})();
函数前面加+-!~都行,不加报错,最好用!,其他可能出 bug
可以不加,也尽量不要加最外面的括号(),如果加(),上一句必须用;
vscode 格式化了自动加了()
- 新版 JS,有新的方法声明局部变量
{let a = 2;console.log(a);}



