前端面试难题:this指向问题

概述

  1. this是一个JavaScript的一个很重要的关键字,他与var、let、function相比比较复杂,同时也比较灵活。
  2. this会在上下文中绑定一个对象,this绑定的对象有可能是window、Object对象、function方法等等。 根据某些规则进行绑定,那么这些规则又是什么呢?

理解this

1. 为什么使用this
  1. 常见的面向对象语言中,this通常只会出现在类的方法中
  2. 你需要有一个类,类中的方法,this代表当前调用对象
  3. JavaScript中的this出现的位置不同、代表的含义都会有不同的变化(灵活)。
2. this优化代码前后对比
var obj = {
    name: '小韩',
    running: function () {
        console.log(obj.name + '跑起来');
    }
}
obj.running() // 打印出:(obj.name = 小韩) 跑起来 

不使用this的时候,通过obj的引用获取内容
弊端: 定义的obj中如果有很多次obj引用,那么当改变obj为info的时候就要全部更换,很麻烦。

var obj = {
    name: '小韩',
    running: function () {
        console.log(this.name + '跑起来');
    }
}
obj.running() // 打印出:(this.name = 小韩) 跑起来 

此时这个时候,不管obj是否更换,obj对象的方法不用更换。 (!!!此时 this = obj)

3. this的使用可以让我们的更加便捷的引用对象,使代码可以更加简洁和易于复用;
4. this指向什么?
  1. 在浏览器中测试就是指向window
  2. 所以我们可以认为在全局的情况下, this = window。
  3. 注意:开发中很少在全局环境下使用this,一般都是在方法中使用。
5. 所有的函数在被调用的时候,都会创建一个执行上下文:
  1. 这个上下文中记录着函数的调用栈、函数的调用方式、传入的参数信息等;
  2. this也是其中的一个属性
6. 案例1: 定义一个函数,三种方式调用,产生三种结果:
        function han() {
            console.log(this)
        }
        han() // 1.直接调用   :  输出 window
        var obj = {
            name: 'why',
            han: han
        }
        // 2. 将han放到一个对象中,在调用
        obj.han() // obj 对象       : 输出obj这个对象
        han.call('abc')  //3.通过call/apply调用    :输出 String{"abc"}  字符串对象
启示
  1. 函数在调用时候,JavaScript会默认给this绑定一个值;
  2. this的绑定和定义的位置没有关系;
  3. this的绑定和调用方式以及调用的位置有关系
  4. this是在运行时被绑定的。

this的绑定规则

this就是一个在被调用时候被绑定的一个对象。

1. 默认绑定

  1. 情景: 独立函数调用 使用默认绑定;
  2. 定义: 函数没有被绑定到某个对象上,这叫做独立函数调用。
案例一:普通函数调用
  1. 直接被调用,没有进行对象关联
  2. 函数中的this指向window
function foo() {
  console.log(this); 
}
foo(); // window
案例二: 函数调用链(一个函数调用另外一个函数)
  1. 所有函数都没有被绑定到某个对象上
        function test2(){
            console.log(this)  // window
        }
        function test1() {
            console.log(this) // window
            test2()
        }
        test1()
案例三: 将函数作为参数传入到另外一个函数中
        function test2(func){
            func()
        }
        function test1() {
            console.log(this)  // window
        }
        test2(test1)

2. 隐式绑定

  1. 情景: 通过某个对象进行调用这个方法;
  2. 定义: 在它的调用位置中,是通过某个对象发起的函数调用
  3. 前提条件: 必须在调用的对象内部有一个对函数的引用;
  4. 没有这个引用会报错(调用时,找不到该函数) ,通过这个引用,间接的将this绑定到这个对象上。
案例一:通过对象调用函数
  1. han()的调用位置是obj.han()方式进行调用;
  2. 那么han()调用的时候this会隐式的绑定obj对象
        function han() {
            console.log(this)
        }
        var obj = {
            name: '小韩啊',
            han: han
        }
        obj.han()    // {name:'小韩啊',han: f}   输出 obj 对象
案例二:升级案例一
  1. 实际上还是obj1绑定了this。
        function han() {
            console.log(this)
        }
        var obj1 = {
            name: '小韩啊',
            han: han
        }
        var obj2 = {
            name:'2个小韩啊',
            han:obj1
        }
        obj2.han.han();   // 谁点函数 谁绑定  其他的都是引用
案例三:隐式丢失
  1. foo()最终是通过**bar()**进行的调用,bar()在进行调用的时候没有绑定任何的对象
  2. 因此,bar并没有形成隐式绑定
  3. 这就相当于是默认绑定
        function foo() {
            console.log(this);
        }

        var obj1 = {
            name: "obj1",
            foo: foo
        }

        // 讲obj1的foo赋值给bar
        var bar = obj1.foo;

        obj1.foo();  //  obj1对象
        bar();  // window

3. 显示绑定

  1. 情景: 不希望在对象内部包含对这个函数的引用,希望这个对象强制调用
  2. JavaScript所有的函数都可以使用call和apply方法 和Prototype有关;
  3. call 和 apply 两个函数 第一个参数相同,后面的参数:apply为数组,call为参数列表
  4. 这两个函数第一个参数都要求是一个对象, 这个对象的作用是给this准备的
  5. 在调用这个函数的时候,会将this绑定到这个传入的对象上
  6. 定义: 在上面的过程中,明确的绑定了this指向的对象,称为显示绑定
1. 通过call或者apply绑定this对象
        function han(){
            console.log(this)
        }
        han.call(window)   // window
        han.call({name : '韩'})  // {name:'韩'}
        han.call(123) // Number对象 存放123
2. bind函数

情景:希望一个函数总是显示的绑定到一个对象上面,如何做?

  1. 方案1: 手写bind辅助函数

这个bind辅助函数的目的是在执行han函数的时候,总是让它的this绑定到obj对象上。

        function han() {
            console.log(this)
        }
        var obj = {
            name: '韩'
        }
        function bind(func, obj) {
            return function () {
                return func.apply(obj, arguments)
            }
        }
        var bar = bind(han,obj);
        bar();    // {name:'韩'}
  1. 方案2: 使用Function.protoptype.bind
        function han(){
            console.log(this)
        }
        var obj = {
            name:'韩'
        }
        var bar = han.bind(obj)
        bar() // {name:'韩'}
3. 内置函数
  1. 这些内置函数包括JavaScript内置函数、第三方库中的内置函数
  2. 内置函数要求我们传入另一个函数中
案例一: setTimeout
        setTimeout(function(){
            console.log(this)   /// window
        },1000)    

setTimeOut内部是通过apply进行绑定的this对象,并且绑定的是全局对象

案例二: 数组的forEach
        var names = ["abc", "cba", "nba"];
        names.forEach(function(item){
            console.log(this);   // 三次window
        })

默认情况下传入的函数是自动调用函数(默认绑定)

改变forEach this指向

names.forEach(function(item) {
  console.log(this); // 三次obj对象
}, obj);
案例三:div的点击
        var box = document.getElementById('app')
        box.onclick = function () {
            console.log(this);   // box对象
        }
  1. 在发生点击事件的时候执行传入的回调函数被调用,会将box对象绑定到该函数中

4. new绑定

  1. JavaScript中的函数可以当做一个类的构造函数使用,也就是new关键字
  2. 使用new关键字调用函数:
  1. 创建一个全新的对象;
  2. 这个新对象会被执行Prototype连接
  3. 这个新对象会绑定到函数调用的this上(this绑定在这个时候完成);
  4. 如果函数没有返回其他对象,表达式会返回这个新对象;
        function Persion(name) {
            console.log(this);   Persion{ }  空对象
            this.name = name;
        }

        var p = new Persion("韩");
        console.log(p)   // Persion {  name:'韩' }

点击跳转到学习资料

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderHan_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值