JS函数

作者: Marshall3572 | 来源:发表于2021-02-27 11:04 被阅读0次

    四种方式定义函数

    具名函数

    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函数

          本文链接:https://www.haomeiwen.com/subject/nvebfltx.html