JS函数

四种方式定义函数

具名函数

function 函数名(){
    return xxx;
}

注意:let a = function fn(x,y){return x+y}这种情况函数的作用域仅在等号右边。
匿名函数

let a = function(x,y){return x+y}

箭头函数

注意:直接返回一个对象会出错,记得加个圆括号。let f4 = (x,y) => ({name:x,age:y})

用构造函数
let f = new Function('x','y','return x+y')
基本没人用,但是通过这个函数可以看出,所有函数都是由function构造出来的。

函数自身和函数调用
fn 和 fn()

let fn = () => console.log('hi')
fn

结果:不会有任何结果,因为fn没有执行。

let fn = () => console.log('hi')
fn()

结果:打印出hi,有圆括号才是调用。

let fn = () => console.log('hi')
let fn2 = fn
fn2()

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

调用时机

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

输出6个6,setTimeout会等 整个代码运行完了之后再运行,i的最终值为6,最后连续打印
6次i。

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

会输出0,1,2,3,4,5
因为JS在 for和let 一起使用的时候会加东西,每次循环内存中都会生成一个新的i。
除了let和for结合,还有什么办法打印出0,1,2,3,4,5吗
答案就是利用闭包

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

解析:

  1. 这里利用立即执行函数在每一次的循环中都执行一次这个函数,而这个函数的参数为i,记录着每一次循环中i的值。
  2. 一个立即执行函数会生成一个块级作用域,这作用域里面有两个东西,一个是i的值,一个是匿名函数(箭头函数)。
  3. 由于我们循环了六次,也就是说生成了六个块级作用域,且这六个块级作用域中的i都是不同的(原因见1)。

作用域:就近原则&闭包

在顶级作用域声明的变量是全局变量
window的属性是全局变量
其他都是局部变量

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

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

参数和返回值

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

x 和 y是形式参数
add(1,2)中 1和2 是实际参数。
形参的本质就是变量声明。

返回值
function hi(){
console.log('hi')
}
没写return,所以返回值是undefined
函数执行完了后才会返回,只有函数有返回值,1+2的值为3不能叫返回值。

递归,调用栈与爆栈

调用栈类似于玩游戏存档
JS在调用一个函数前,需要把函数所在环境压(push)到一个数组里,这个数组叫调用栈,等函数执行完了再把函数弹(pop)出来,然后return到之前的环境继续执行代码。

this

每次调用函数时,都会对应产生一个 arguments
我们应该尽量不对 arguments 内的元素进行修改,修改 arguments 会让代码变得令人疑惑
arguments(伪数组,没有数组的共有属性push等) 和 this 每个函数都有,除了箭头函数。
用Array.from()把任何不是数组的变成数组。
如果不给任何条件,默认的this指向window(通常不用默认的this)。
如何传arguments
调用fn,fn(1,2,3)那么arguments就是[1,2,3]伪数组
如何传this
在 fn() 调用中, this 默认指向 window,这是浏览器决定的

fn.call(xxx,1,2,3)传this和arguments
而且xxx会被自动转化为对象。

两种调用

  • 小白调用法
    person.sayHi()
    会自动把person传到函数里,作为this。
  • 大师调用法(强推)
    person.sayHi.call(person)
    需要自己手动person传到函数里,作为this

call指定this

如果没有用到this
add.call(undefined,1,2)
为什么要多写个undefined
因为第一个参数要作为this,但是代码中没有用this,所以要用undefined占位,别的也可(比如null)

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])

箭头函数

没有arguments 和 this
新版js没有this,直接用箭头函数

  • 在 arrow() 调用中,arrow 里面的 this 就是 arrow 外面的 this,因为箭头函数里面没有自己的 this
  • 在 arrow.call(xxx) 调用中,arrow 里面的 this 还是 arrow 外面的 this,因为箭头函数里面没有自己的 this

立即执行函数

ES5时代,为了得到局部变量必须引入一个函数,这个函数必须为匿名函数,申明匿名函数然后加个()去执行它。
最终发现,只要在匿名函数前加个运算符(!、~、()、+、-)即可,推荐!。

你可能感兴趣的:(JS函数)