js-作用域、闭包、this

81 阅读3分钟

作用域

作用域链

子级可以拿到父级作用域的变量或者方法
现实应用场景:嵌套函数内部读写各外层上下文中的变量

变量提升、函数提升

变量提升优先级高于函数提升
块级作用域 --let/const  值作用于当前作用域
	var无块级作用域,污染全局变量

js-编码解析

静态创建
	作用域链确认:当前变量和所有父级变量
	变量申明
动态执行(区别于其他语言js的this是动态的)
	this/context/指针 执行上下文

this指向问题

函数表达式、匿名函数、嵌套函数

    this为其执行上下文-window
    function a(){
            console.log(this) // this指向其执行环境-window
            b() // this指向其执行环境-func-window
    }
    function b(){
            c() // this指向其执行环境-func-window
    }
    function c(){
            console.log(this)
    }
    a() 

隐式绑定

    function fn(){
            console.log('print-this: ', this)
    }
    const obj = {
            a: 1,
            fn
    }
    obj.fn()   // 函数作为对象方法,this为该对象实例

    let fn1 = obj.fn
    fn1() // 取出函数,this为上下文window
    

class中的this

    // 指向new之后生成的实例
    class Course{
            constructor(name){
                    this.name = name
                    console.log('构造函数中的this:',  this)
            }
            test(){
                    console.log('类方法中的this:', this)
            }
            asyncTest(){
                    console.log('异步方法外this:', this) // 类实例的this
                    setTimeout(function(){
                            console.log('异步方法外this:', this) // this指向window
                    }, 2000)
            }
    }
	

tips: 类异步方法上下文变为window

解决方法:
记录this并传入;箭头函数;

问题:this指向案例

    const obj2 = {
            name:  'obj2',
            fn: function(){
                    console.log('obj2 this: ', this, this.name)
                    return this.name
            }
    }
    const obj3 = {
            name: 'obj3',
            fn: function(){
                    return obj2.fn()
            }
    }
    const obj4 = {
            name: 'obj3',
            fn: function(){
                    let fn1 = obj2.fn
                    return fn1()
            }
    }
    console.log('obj2.fn: ', obj2.fn()) // this为o1,name为obj2
    console.log('obj3.fn: ', obj3.fn()) // this为o1,name为obj2
    console.log('obj4.fn: ', obj4.fn()) // this为window,name为undefined
	

改变this指向

    // 1.直接拿过上下文
    const obj3 = {
            fn: o1.fn
    }
    // 2.callapplybind
    // errorconsole.log('obj3.fn: ', obj3.fn.call(obj3, null)) -- 操作不到实际调用者
			
    const obj3 = {
        name: 'obj3',
        fn: function(){
                return obj2.fn.call(this)
        }
    }
			

call、apply、bind

三者区别: call、apply 传参不同:依次传入、数组传入
bind:返回方法

手写bind

分析:改变this指向,返回一个方法-未执行

    Function.prototype.newBind = function(){
            const _this = this
            const args =  Array.prototype.slice.call(arguments)
            const newThis = args.shift()

            return function(){
                    return _this.apply(newThis, args)
            }
    }

手写apply

分析:改变this指向,返回一个方法-未执行

    Function.prototype.newApply = function(context ){
            // 边缘检测,参数检测
            if(typeof this !== 'function') throw new Error('type new error')
            // 参数检测
            context = context  || window
            context.fn = this

            let result  = arguments[1]?  context.fn(…arguments[1]): context.fn()

            delete context.fn
            return result
    }
				

闭包

与作用域\上下文相关,函数和周围状态-上下文产生关联捆绑

    // 最简单闭包
    function  bibbao(){
            let context =  'bibbao'
            return function(){
                    console.log(context)
            }
    }
    
    // 实现私有变量,利于项目模块封装功能
    fucntion bibao(){
            const items =  []
            return {
                    getItem(){

                    },push(val){
                            items.push(val)
                    },setItem(){

                    }
            }
    }

优点:函数外部可以获取到内部作用域中的变量
缺点:导致内部局部变量不能被回收

闭包场景

    //  函数作为参数传递 抽离,单一职责原则
    let content;
    function envelop(fn){
            content = 1;
            fn();
    }
    function mail(){
            console.log(content)
    }
    envelop(mail)

    //  函数嵌套
    包裹函数模块-高内聚
    let  counter =  0
    function outerFn(){
            function innerFn(){
                    counter ++
                    console.log(counter)
            }
            return innerFn
    }
    outerFn()()
    
    // 事件处理(异步)的闭包
    // 上下文产生一定的捆绑
    let lis = document.getElementById('myli')
    for(var i = 0; i < lis.length; i++)
    {
            (function(i){
                    lis[i].onclick= function(){
                            console.log(i)
                    }
            })(i)
    }

    立即执行函数  --模块化的开端
            让内部内容生成块级作用域
            (function immediateFn(a){
                    return (function immediateFn2(b){
                            console.log(b)
                    })(200)
            })(100)