JS函数

181 阅读4分钟

定义

函数是一种对象

  • 具名函数
function 函数名 (形式参数1,形式参数2){
   语句
   return 返回值
}
  • 匿名函数 上面的具名函数去掉函数名,也叫函数表达式
let a = function(x,y){return x+y}

如果函数的声明是在等于号右边的,那他的作用域只在等于号右边成立,除了这里,其他地方在调用这个函数只能用声明的变量,若没有等于号则是全局使用

let a = function fn(x,y){return x+y}
fn(1,2) //错误❌
  • 箭头函数 如果要返回对象,在对象{ }外面加()
let f1 = x => x*x
let f2 = (x,y) => x+y //圆括号不能省略
let f3 = (x,y) => {return x+y; console.log('你好')} //如果有两句必须自己家return
let f4 = (x,y) => ({name: x,age: y}) //直接返回对象会出错,需要加()
  • 构造函数


fn和fn()

let fn = () => console.log('hi')
fn            //不会有任何结果,因为fn没有执行
fn()          //打印出hi,有()圆括号才是调用
let fn = () => console.log('hi')
let fn2 = fn
fn2()

fn保存了匿名函数的地址,这个地址被复制给了fn2,fn2()调用了匿名函数,fn和fn2都是匿名函数的引用而已,真正的函数既不是fn也不是fn2



函数的要素

每个函数都有这些东西

  • 调用时机
  • 作用域
  • 闭包
  • 形式参数
  • 返回值
  • 调用栈
  • 函数提升
  • arguments(除了箭头函数)
  • this(除了箭头函数)

调用时机

时机不同结果不同

let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
  console.log(i)
  },0)
}

打印出6个6,循环先执行完i=6,在打印调用的6次,把当前的事情干完之后再去干

for( let i = 0; i<6; i++){
  setTimeout(()=>{
  console.log(i)
  },0)
}

打印出0、1、2、3、4、5,因为JS在for和let一起用时会加东西,每次循环会多创建一个i,循环执行后会创建6个不同的i

作用域

每个函数都会创建一个作用域,let的作用域,找到包住他最近的花括号

function fn(){
  let a = 1
}
console.log(a)  //a不存在
  • 局部变量:只在这一块代码中可以生效
  • 全局变量:在顶级作用域声明的变量或者把你的变量写在window上,如window.c = 1 如果多个作用域有同名变量a,那么查找a的声明时,就向上取最近的作用域,查找a的过程与函数执行无关,即作用域与函数执行无关,那么这种成为静态作用域,但a的值与函数执行有关

闭包

如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包

形式参数

形式参数的意思就是非实际参数,也可认为是变量声明而且形参声明可多可少;如add(x,y),x,y就是;但是add(1,2)1,2就是实际参数

返回值

每个函数都有返回值,返回值实在执行之后才有的,就算不写return返回值会默认是undefined

只有函数有返回值

调用栈

JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组里,这个数组就是调用栈,等函数执行完,就会把环境弹(pop)出来,然后return到之前的环境,继续执行后续代码

console.log(1)
console.log('1+2的结果为'+add(1,2))
console.log(2)

递归函数的调用栈很长

函数提升

function fn(){}不管你把具名函数写在哪里,它都会跑到第一行,可以在声明之前调用

let add = 1
function add(){}

let add = function fn(){} 会报错,这是赋值,右边的匿名函数声明不会提升

arguments

arguments是包含所有参数的伪数组,没有数组的共有属性

  • 如何传arguments 调用fn即可传,fn(1,2,3)就是[1,2,3]伪数组

this

如果不给任何条件,this默认指向window,传一个undefined会变成全局变量Window

  • 如何传this 目前可以用fn.call(xxx,1,2,3)传this和arguments,而且xxx会被自动转化成对象,即你传的不是对象,JS会自动帮你封装成对象,第一个xxx是this,后面的所有的是arguments,this是隐藏参数,arguments是普通参数
function fn(){
 'use strict'
 console.log(this)
}
fn.call(1)  //打印出1,不是对象

需要一种办法拿到还没出现的对象,实现函数对那个对象的引用

  • 用this获取那个对象
let person ={
  name = 'frank'
  sayHi(this){
   console.log(`你好,我叫` + this.name)
  }
}
person.sayHi()    //正确
person.sayHi(person)  //错误
person.sayHi.call({name:1})    //正确{}内传什么,this就是什么,

person.sayHi()相当于person.sayHi(person)然后person被传给了this(person是个地址) 每个函数都能用this获取一个未知对象的引用了

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)
fn.apply(undefined,[1,2]) //后面的其他参数上面要加[]

绑定this

  • 使用.bind可以让this不被改变
  • .bind还可以绑定其他参数

立即执行函数

优先使用!

function (){
  var a = 2 
  console.log(a)
} ()

只要一个局部变量的方法,新版只需要

{
 let a = 2
 console.log(a)
}