this指针/闭包及作用域(进阶)

65 篇文章 2 订阅
文章详细解释了JavaScript中的作用域链,通过示例展示了变量在不同作用域内的提升行为,强调了let不支持变量提升而var支持。同时,文章深入讨论了this的动态绑定特性,以及在不同调用方式下this的指向。还提到了bind、call和apply的区别,并给出了手动实现bind的方法。此外,文章还介绍了闭包的概念及其应用场景,包括函数作为返回值、作为参数以及嵌套函数的情况。
摘要由CSDN通过智能技术生成

一.作用域链

1.通过一个例子

        let a='global'
        console.log(a);//'global'

        function course(){
            let b='js'
            console.log(b);//'js'
            session()
            function session(){
                let c=this
                console.log(c);//Window
                teacher()//函数提升
                function teacher(){
                    let d='steven'
                    console.log(d);//'steven'

                    console.log('test1',b);//'js'
                }
            }
        }
        course()

2.改造一下这个例子

        let a='global'
        console.log(a);//'global'

        function course(){
            let b='js'
            console.log(b);//'js'
            session()
            teacher()//报错
            function session(){
                let c=this
                console.log(c);//Window
                // teacher()
                //函数提升--在作用域内提升(超出了当前作用域,所以报错)
                function teacher(){
                    let d='steven'
                    console.log(d);

                    console.log('test1',b);
                }
            }
        }
        course()

3.继续改造这个例子(let 和var能否提升变量)

        let a='global'
        console.log(a);//'global'

        function course(){
            let b='js'
            console.log(b);//'js'
            session()
            function session(){
                let c=this
                console.log(c);//Window
                teacher()
                //2.函数提升-作用域之内
                function teacher(){
                    //2.1 let不支持提升
                    //2.2 变量通过var支持提升,变量的声明可以提升
                    //相当于执行了这样一个操作: var e=underfined(提升)
                    console.log('e',e);//undefined
                    let d='steven'
                    console.log(d);
                    //e='tom'
                    var e='tom'
                    console.log('test1',b);//'js' //3.作用域向上查找,向下传递
                }
            }
        }
        course()

 4.提升优先级

        //提升优先级
        console.log('yunyin',yunyin);
        function yunyin(){
            this.course='js'
        }
        yunyin='course'
        //变量优先,函数需要变量,所以变量最终会覆盖函数

        //块级作用域
        if(true){
         let e=11
         var f=222
        }
        console.log(f);
     // console.log(e);
       //1.对于作用域链我们可以直接通过创建态来定位作用域链 --静态创建
       //2.手动取消全局

5.函数提升-作用域之内

        let a='global'
        console.log(a);//'global'

        function course(){
            let b='js'
            console.log(b);//'js'
            session()
            function session(){
                let c=this
                console.log(c);//Window
                teacher()
                //函数提升-作用域之内
                function teacher(){
                    console.log(d);//报错
                    let d='steven'
                    console.log(d);

                    console.log('test1',b);
                }
            }
        }
        course()

6.this/上下文context

例子:我家门有条河,门前的河上有座桥,门前的河里有群鸭.

我家门前有条河,这河上有座桥,这河里有群鸭.

这指的就是我家门前那条河,也就是上下文context.

结论:this是在执行时动态读取上下文决定的,而不是创建时.

7.考察重点--各使用态的指针指向

(1)函数直接调用中,this指向的是window

==>全局上执行的环境=>函数表达式/匿名函数/嵌套函数

为什么呢?因为它是通过调用它的调用方的执行环境来决定的.

function foo(){
console.log(this)
}
foo() //window.foo()

(2)隐式绑定--this指带的是调用堆栈的上一级=>对象/数组等引用关系逻辑

        function fn(){
            console.log('隐式绑定',this.a);//1
        }
        let obj={
            a:1,
            fn
        }
        obj.fn=fn
        obj.fn()

面试题:

       const foo={
            bar:10,
            fn:function(){
                console.log(this.bar);//undefined
                console.log(this);//Window
            }
        }
        //取出
        let fn1=foo.fn
        //独立执行
        fn1()

追问1:如何改变this指向?

       const o1={
            text:'o1',
            fn:function(){
                //直接使用上下文--传统派活
                console.log('o1fn',this);
                return this.text
            }
        }

        const o2={
            text:'o2',
            fn:function(){
                //呼叫领导执行,部门协作
                return o1.fn()//o1的执行态
            }
        }

        const o3={
            text:'o3',
            fn:function(){
                //直接内部构造,公共人
                let fn=o1.fn
                return fn()//挂载在全局公共的一个方法
            }
        }
        console.log('o1fn',o1.fn());
        console.log('o2fn',o2.fn());
        console.log('o3fn',o3.fn());

追问2:现在我要将concole.log('o2fn',o2,fn())的结果是o2.

(1)人为干涉,改变this--bind/apply/call

o2.fn().call(o2)

(2)不需人为改变

 const o1={
            text:'o1',
            fn:function(){
                //直接使用上下文--传统派活
                console.log('o1fn',this);
                return this.text
            }
        }

        const o2={
            text:'o2',
            fn:o1.fn
        }
 console.log('o2fn',o2.fn());

(3)显式绑定(bind | apply | call)

        function foo() {
            console.log('函数内部', this);
        }
        foo()
        foo.call({
            a: 1
        })
        foo.apply({
            a: 1
        })
        const bindFoo = foo.bind({
            a: 1
        })
        bindFoo()

面试题:call/apply/bind的区别

1.call vs apply 传参不同 依次传入/数组传入

2.bind直接返回不同,需要再调用一次

###bind的原理/手写一个bind

        //1.需求:手写bind=>bind挂载位置(挂载在哪里)=>Function.prototype
        Function.prototype.newBind = function () {
            //2.bind是什么?
            //改变this
            const _this = this
            //接收参数args,第一项参数是新的this,第二项到最后一项是函数传参
            const args = Array.prototype.slice.call(arguments)
            console.log('args', args);
            const newThis = args.shift()
            console.log('newThis', newThis);
            //3.返回值
            return function () {
                return _this.newApply(newThis, args)
            }
        }

        Function.prototype.newApply = function (context) {
            context = context || window
            //挂载执行函数
            context.fn=this
            let result=arguments[1]
            ? context.fn(...arguments)
            :context.fn()
            delete context.fn
            return result

        }

###闭包:一个函数和它周围状态的引用捆绑在一起的组合

1.函数作为返回值的场景

    //函数作为返回值的场景
    function mail(){
        let content='信'
        return function(){
            console.log(content);//信
        }
    }
    const envelop=mail()
    envelop()

2.函数作为参数的时候

        let content=0
        function envelop(fn) {
            content = 1
            fn()
        }
        
        function mail(){
            console.log(content);//1
        }
        envelop(mail)//把mail函数嵌入到了envelop函数中

3.函数的嵌套

        let counter = 0
        function outerFn() {
            function innerFn() {
                counter++
                console.log(counter);//1
            }
            return innerFn
        }
        outerFn()()

延伸:1.立即执行函数=>js模块化的基石

        let count=0
        (function immediate(args){
            if(count===0){
                let count=1
                console.log(count);
            }
        })(args) 

2.实现私有变量

       function createStack() {
            return {
                items: [],
                push(item) {
                    this.item.push(item)
                }
            }
        }

        const stack={
             items:[],
             push:function(){}
        }

        function createStack(){
            const items=[]
            return {
                push(item){
                    items.push(item)
                }
            }
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值