美文网首页
this 常见的面试题汇总

this 常见的面试题汇总

作者: 饥人谷1904_陈俊锋 | 来源:发表于2019-10-12 16:16 被阅读0次

    上一节我们了解到了 call 和 apply,通过他俩 我们来看看函数执行的本质是什么

    当我们执行一个函数,有以下几种调用方式等价

    函数直接调用情况下

    "use strict" 
    function fn(a, b) {
      console.log(this)
    }
    fn(1, 2)
    // 等价于
    fn.call( undefined, 1, 2 )
    fn.apply( undefined, [1, 2] )
    

    可以看到:

    • 在严格模式下,fn 里的 this 就是 call 的第一个参数,也就是 undefined
    • 在非严格模式下,(不加 "use strict") call 传递的第一个参数如果是 undefined 或者 null,那 this 会自动替换为 window 对象

    对象方法调用的情况下

    var obj = {
        fn: function(a, b){
            console.log(this)
        },
        child: {
            fn2: function(){
                console.log(this)
            }
        }
    }
    obj.fn(1, 2)
    //等价于
    obj.fn.call(obj, 1, 2)         // 所以 this 是 obj
    obj.fn.apply(obj, [1, 2])
    
    obj.child.fn2()
    //等价于
    obj.child.fn2.call(obj.chid)    // 所以 this 是 obj.child
    

    通过 call 和 apply 我们理解了原理之后来看看下面的测试题

    let name = '饥人谷'
    let people = {
        name: '若愚',
        sayName: function(){
            console.log(this.name)
        }
    }
    let sayAgain = people.sayName
    function sayName(){
        console.log(this.name)
    }
    
    sayName()
    /*
     解析:相当于 `sayName.call(undefined)` ,因为是非严格模式,所以 this 被替换成 Window,所以这里输出全局的 name 即 "饥人谷"
    */
    people.sayName()
    /*
    解析: 相当于 `people.sayName.call(people)` ,所以这里输出 `people.name` 即 "若愚"
    */
    
    sayAgain()
    /*
    解析: 相当于 `sayAgain.call(undefined)` ,,因为是非严格模式,所以 this 被替换成 Window,所以这里输出全局的 name 即 "饥人谷"
    */
    

    var arr = []
    for(var i=0; i<3; i++){
        arr[i] = function(){ console.log(this) }
    }
    var fn = arr[0]
    
    arr[0]()
    /*
    解析: 因为函数是个特殊的对象,所以 arr 相当于 { '0': function(){}, '1': function(){}, '2': function(){}, length:3}
    arr[0]相当于 `arr['0']` 相当于 `arr.0` (当然这种写法不符合规范),所以 arr[0]等价于 arr.0.call(arr), this就是 arr
    */
    
    fn()
    /*
    解析: 相当于 `fn.call(undefined)`, 所以 fn 里面的 this 是 Window
    */
    

    bind

    bind 的作用和 call 与 apply 类似,区别在使用上
    bind 的执行结果返回的是绑定了一个对象的新函数

    先看一个简单一点的例子

    var obj = {name: '饥人谷'}
    function sayName(){
        console.log(this.name)
    }
    var fn = sayName.bind(obj)  // 注意 这里 fn 还是一个函数,功能和 sayName 一模一样,区别只在于它里面的 this 是 obj
    fn() // 输出: '饥人谷'
    

    再看一个实际一点的例子

    var app = {
        container: document.querySelector('body'),
        bind: function(){
            this.container.addEventListener('click', this.sayHello)                  //点击的时候会执行 sayHello,sayHello 里面的 this 代表 body 对象
            this.container.addEventListener('click', this.sayHello.bind(this))  //点击的时候会执行 sayHello,sayHello 里面的 this 代表 app 对象
        },
        sayHello: function(){
           console.log(this)
        }
    }
    app.bind()
    

    箭头函数

    ES6 标准中,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

    同样先看一个简单点的例子

    let app = {
        fn1: function(a){
            console.log(this)  //app
        },
        fn2(a) {
            consoel.log(this)  //app
        },
        fn3: (a)=>{
            console.log(this)  //window
        }
    }
    

    粗略一看,fn1、fn2、fn3 貌似都一样,实际上 fn1和 fn2完全等价,但 fn3是有区别的
    以上代码等同于

    app.fn2.call(app)
    app.fn3.call( 它的上一级的 this )
    

    再看一个复杂点的例子就清楚了

    var app = {
        init() {
            var menu = {
                init: ()=>{
                    console.log(this)
                },
                bind() {
                    console.log(this)   
                }
            }
            menu.init() 
             /*相当于  menu.init.call(menu 所在的环境下的 this)  , 所以 init 里面的 this 也就是 app。
             (假设 app.init 也是箭头函数,想想 menu.init 里面的 this 是什么?)          
             (答案是:window)
             */
            menu.bind() 
            /*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu
            */
        }
    }
    app.init()
    

    理解了原理之后,我们再来看看下面的题目

    var app = {
        fn1() {
            setTimeout(function(){
                console.log(this)
            }, 10)
        },
        fn2() {
            setTimeout(()=>{
                console.log(this)
            },20)
        },
        fn3() {
            setTimeout((function(){
                console.log(this)
            }).bind(this), 30)        
        },
        fn4: ()=> {
            setTimeout(()=>{
                console.log(this)
            },40)        
        }
    }
    app.fn1()
    app.fn2()
    app.fn3()
    app.fn4()
    

    以上代码相当于

    var app = {
        fn1() {
            function fn(){
                console.log(this)
            }
            //过10ms 后执行
            //fn.call(undefined) ,所以输出 Window
        },
        fn2() {
            //过20ms 执行箭头函数
            //箭头函数里面没资格有 自己的 this,借用 setTimeout 外面的 this,也就是 app
        },
        fn3() {
            // 创建了一个新函数,这个新函数里面绑定了 外面的this,也就是 app
            // 20 ms 后执行新函数,输出 this,也就是刚刚绑定的 app    
        }
        fn4: ()=> {
            //过40ms 执行箭头函数
            //箭头函数里面没资格有 this,用 setTimeout 外面的 this
            //setTimeout 所在的 fn4也是箭头函数,没资格拥有自己的 this,借用外面的 this ,也就是 Window     
        }
    }
    

    转载自 饥人谷this 常见面试题汇总

    相关文章

      网友评论

          本文标题:this 常见的面试题汇总

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