JS 函数

196 阅读4分钟

函数是对象

在JS中,函数只是一种对象,尽管它看起来和对象很不一样


如何定义一个函数

  1. 具名函数
function 函数名(形式参数){
    语句
    return 返回值
}
  1. 匿名函数

匿名函数与具名函数的区别就在于是否有函数名

let a = function (形式参数){
    语句
    return 返回值
}

这里函数一定要将其地址赋予a,否则,该函数由于没有函数名,也未被引用而消失!

  1. 箭头函数(没有this与arguments)
let 函数名 = (形式参数) => (
    语句
    return 返回值
)
  1. 总结

其实,我们也可以通过一种不常用的方法构造函数

let fn = new Function ('形式参数1','形式参数2''语句' 'return 返回值')

通过以上语句,我们可以看出 函数 是由 Function 构造出来的


函数的调用

  1. fn 与 fn() 的区别
let fn = function(){
    console.log('hi')
}

//fn
//fn()
  • fn 指向的是 函数(对象)在内存中的 地址
  • fn() 则表示调用函数

函数的要素 —— 调用时机

一个函数的调用时机不同,结果就不同


函数的要素 —— 作用域与闭包

作用域遵循 ‘就近原则’ 如图中f3 中的 变量a 就是 let a = 2 的a,

a的值为22

f3 与 let a = 2 中的 a 组成了一个 闭包


函数的要素 —— 形式参数

形式参数就相当于数学中的设 x y ,他并没有实际意义

function add(x,y){
    return x+y
}

上面的 x y 就是形式参数,他没有意义,只有当我们调用它时,形式参数才会变成 实际参数

add.call(undefined,1,2)

其中,1,2,分别是 x,y 的实际参数


我们可以将形式参数 认为是变量声明,它等同于以下代码
function add(){
    var x = arguments[0]
    var y = arguments[1]
    return x+y
}

函数的要素 —— 返回值

每个函数都有返回值,若未写return ,则默认返回 undefined

function hi(){
    return console.log('hi')
}

// 返回 console.log('hi') 的值,即undefined

函数的要素 —— 调用栈

  1. 什么是调用栈

JS 引擎在调用一个函数前,需要把函数所在的环境push到一个数组(压栈),这个数组叫做调用栈,等函数执行完了,九八环境pop出来(弹栈),return 到之前的环境,继续执行后面的代码

  1. 爆栈

以递归函数为例

function f(n){
    return n !== 1 ? n*f(n-1) : 1
}

此函数 压了n次栈 , 弹了n次栈, 当n大于浏览器的最大数量时,程序就会报错


函数的要素 —— 函数提升

  1. 什么是函数提升

具名函数不管写在哪里,他都会自动跑到第一行

  1. 什么不是函数提升

当函数赋给一个变量时,不会提升


函数的要素 —— arguments 与 this

每个函数都有arguments 与 this , 除了箭头函数

对于箭头函数来说,this永远是window

  1. arguemnts

arguments 是一个伪数组 ,它没有 数组 的原型,只有对象的原型

  1. this

this === window

  1. this 的 原理

我们先来看一段代码

let person = {
name: 'hht',
sayHi(){
     console.log(this.name)
  }
}

//person.sayHi()
//hht

如果我们不用this,那么此段代码就应该写成

let person = {
name: 'hht',
sayHi(p){
     console.log(p.name)
  }
}

//person.sayHi(person)
//hht

this 的原理就是在帮你省略了一个形式参数p,在调用函数时,将sayHi前的person 复制一份到()中

  1. 自定义this

如果我们让JS自动帮我们调用this,有时会发生歧义,所以我们也可以自定义this

let person = {
name: 'hht',
sayHi(){
     console.log(this.name)
  }
}

//person.sayHi.call({name:'jack'})
//jack
function add(x,y){
  return x+y
}

// add.call(undefined,1,2)
// add.apply(undefined,[1,2])
  1. this 可以解决的问题

this 可以帮我们解决 一些问题 ,例如 我们在使用构造函数时,我们并不知道构造出来的函数的名字,此时,我们就可以使用this

  1. 绑定this 及 其他参数
let fn = function (x,y){
    console.log(this.name),
    console.log(x,y)
}

let fn2 = fn.bind({name:'hht'},1)

  1. this 的自动封装问题

我们可以看到,当我们传一个 非对象 时,this会自动将其封装

解决办法: 加一行 'use strict'

function fn(){
'use strict'
console.log(this)
}

立即执行函数

在ES5 时代,还没有let, var声明的变量会自动变为全局变量,所以我们必须要使用以下方法来解决这个问题, 让 var 声明的变量变成 局部变量

! function(){
var a = 2
}()

// ! ~ () + - 这些运算符号放在匿名函数前都可以
// 但是推荐使用 !

在ES6 时代,我们用let就可以解决这问题

{
    let a = 2
}