一、函数定义
1.1function命令
function 函数名(形式参数1,形式参数2){
语句
return 返回值
}
复制代码
1.2匿名函数
匿名函数也叫函数表达式
let a = function(x,y){
return x+y
}
复制代码
-
采用函数表达式声明函数时,
function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。let a = function fn(x,y){ return x+y } fn(1,2)//会报错 复制代码
1.3箭头函数
let f1 = x =>x*x
f1(9) //81
let f2 = (x,y) => x*y
f2(8,9) //72
let f3 = (x,y) => {
console.log('hi')
return x*y
}
f3(8,9) // hi 72
let f4 = x => ({name:x}) //如果需要直接返回对象,需要加上圆括号
f4('frank') // name:'frank'
复制代码
1.4构造函数
所有的函数都是 Function 构造出来的
var add = new Function(
'x',
'y',
'return x + y'
);
// 等同于
function add(x, y) {
return x + y;
}
复制代码
二、函数自身和函数调用
2.1fn
let fn = () => console.log('hi')
fn
复制代码
- 不会有任何结果,因为fn没有执行
2.2fn()
let fn = () => console.log('hi')
let fn2 = fn
fn2()
复制代码
fn保存了匿名函数的地址- 这个地址被复制给了
fn2 fn2()调用了匿名函数fn和fn2都是匿名函数的引用- 真正的函数不是
fn也不是fn2
三、函数的要素
3.1调用的时机
函数的调用时机决定了变量的值
let a = 1
function fn(){
setTimeout(()=>{
console.log(a)
},0)
}
fn()
a = 2
//2 设置了定时器,先把a=2执行完,在调用fn
复制代码
let i = 0
for(i = 0;i<6;i++){
setTimeout(()=>{
console.log(i)
},0)
}
//6个6 先执行完循环,再执行定时器
复制代码
- setTimeout 为异步任务(放在任务队列中等待同步任务执行完才执行), for 循环被认定为同步任务(主线程上排队等待执行的任务)
- 只有同步任务for循环完全结束,JS才会去任务队列中找到未执行的6个 setTimeout 任务,并顺序执行
for(let i = 0;i<6;i++){
setTimeout(()=>{
console.log(i)
},0)
//0、1、2、3、4、5
复制代码
3.2作用域
function fn(){
let a = 1
}
fn()
console.log(a) //a不存在
复制代码
3.2.1全局变量与局部变量
-
在顶级作用域声明的变量是全局变量
-
window的属性是全局变量 -
其他都是局部变量
3.2.2作用域嵌套
function f1(){
let a = 1
function f2(){
let a = 2
console.log(a)
}
console.log(a)
a = 3
f2()
}
f1()
//1 2
复制代码
- 如果多个作用域有同名变量
a,那么查找a的声明,就向上取最近的作用域 - 查找a的过程与函数执行无关,但
a的值与函数执行有关
3.3闭包
如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包
function f1(){
let a = 1
function f2(){
let a = 2
function f3(){
console.log(a) //左边的 a 和 f3 组成了闭包
}
a = 22
f3()
}
console.log(a)
a = 100
f2()
}
f1()
复制代码
3.4形参
简单理解为变量的声明,形参可多可少
3.5返回值
每个函数都有返回值,只有函数执行完了才会有返回值
3.6调用栈
3.6.1定义
- JS 引擎在调用一个函数前,需要把函数所在的环境 push 到一个数组里,这个数组叫做调用栈
- 等函数执行完了,就会把环境弹(pop)出来
- 然后 return 到之前的环境,继续执行后续代码
简单说明:
- JS每次进入一个函数,都要记下来等会要回到哪里,所以要把回到的地址写到栈里面,如果进入一个函数要回到另一个函数,要把地址再放到栈里面,等函数执行完就弹栈,知道回到哪里
3.6.2递归函数
function fn(n){
return n !== 1 ? n*f(n-1) : 1
}
复制代码
- 如果调用栈中压入的帧过多,程序会崩溃
3.7函数提升
-
function fn(){}不管你把具名函数声明在哪里,它都会跑到第一行 -
let fn = function(){}这是赋值,右边的匿名函数声明不会提升
3.8arguments和this
每个函数都有 this 和 arguments,除了箭头函数
function fn(){
console.log(arguments)
console.log(this)
}
复制代码
-
调用 fn 即可传 arguments,fn(1,2,3) 那么 arguments 就是 [1,2,3] 伪数组
-
如果没有设置其他条件,this默认指向 Window
-
可以用 fn.call(xxx, 1,2,3) 传 this 和 arguments ,this 是第一个参数,arguments 是剩下的参数
-
xxx 会被自动转化成对象(JS 的糟粕),这时要在函数声明时加上’use strict’
function fn(){
'use strict'
console.log(arguments)
console.log(this)
}
复制代码
- this 是隐藏参数,arguments 是普通参数
3.8.1形式参数、this获取对象引用
let person = {
name: 'frank',
sayHi(){
console.log(`你好,我叫` + person.name)
}
}
//可以用直接保存了对象地址的变量获取 'name',这种办法简称为引用
复制代码
-
//问题1: let sayHi = function(){ console.log(`你好,我叫` + person.name) } let person = { name: 'frank', 'sayHi': sayHi } /* person 如果改名,sayHi 函数就挂了 sayHi 函数甚至有可能在另一个文件里面 所以我们不希望 sayHi 函数里出现 person 引用 */ 复制代码 -
//问题2: class Person{ constructor(name){ this.name = name // 这里的 this 是 new 强制指定的 } sayHi(){ console.log(???) } } /* 这里只有类,还没创建对象,故不可能获取对象的引用 那么如何拿到对象的 name ? */ 复制代码
在不知道对象的引用下,如何获取对象的 name 属性
-
//方法1:形式参数(python采用) //对象 let person = { name: 'frank', sayHi(p){ console.log(`你好,我叫` + p.name) } } person.sayHi(person) //类 class Person{ constructor(name){ this.name = name } sayHi(p){ console.log(`你好,我叫` + p.name) } } 复制代码 -
//方法2:用 this 获取那个对象(JS采用) let person = { name: 'frank', sayHi(this){ console.log(`你好,我叫` + this.name) } } /* person.sayHi()相当于person.sayHi(person) 然后 person 被传给 this 了(person 是个地址) 这样,每个函数都能用 this 获取一个未知对象的引用了 */ person.sayHi.call(person) //需要自己手动把 person 传到函数里,作为 this(建议使用这种方法调用) person.sayHi() //会自动把 person 传到函数里,作为 this 复制代码
3.8.2 call 指定 this
Array.prototype.forEach2 = function(fn){
for(let i=0;i<this.length;i++){
fn(this[i], i, this)
}
}
//调用
array.forEach2.call(array,(item)=>console.log(item))
复制代码
3.8.3 this 的两种使用方法
//隐式传递
fn(1,2) // 等价于 fn.call(undefined, 1, 2)
obj.child.fn(1) // 等价于 obj.child.fn.call(obj.child, 1)
//显示传递
fn.call(undefined, 1,2)//undefined就是this
fn.apply(undefined, [1,2])//undefined就是this
复制代码
3.8.4绑定 this
//使用 .bind 可以让 this 不被改变
function f1(p1, p2){
console.log(this, p1, p2)
}
let f2 = f1.bind({name:'frank'})// 那么 f2 就是 f1 绑定了 this 之后的新函数
f2() // 等价于 f1.call({name:'frank'})
//.bind 还可以绑定其他参数
let f3 = f1.bind({name:'frank'}, 'hi') //'hi'相当于p1
f3() // 等价于 f1.call({name:'frank'}, hi)
复制代码
3.9箭头函数
箭头函数没有 arguments 和 this
fn.call({name:'frank'}) // window
let fn = () => console.log(this)
fn() // window
复制代码
3.10立即执行函数
ES 5 时代,为了得到局部变量,必须引入一个函数,但是这个函数如果有名字,就得不偿失
function fn(){
var a = 1
console.log(a)
}
复制代码
于是这个函数必须是匿名函数声明匿名函数,然后立即加个 () 执行它
function (){
var a = 1
console.log(a)
}()
复制代码
但是 JS 标准认为这种语法不合法,所以 JS 程序员寻求各种办法最终发现,只要在匿名函数前面加个运算符即可
!、~、()、+、- 都可以
! function (){
var a = 1
console.log(a)
}()
//一般使用!
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END





















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)