论this的指向

578 阅读4分钟

this在js中是一个魔术变量,在不同的场景指向不同的对象,在使用的过程中也是千奇百怪,让人琢磨不透,令人头疼,在这里列举了this的指向问题

1.在这里普通的对象之中,this就是指向当前的对象
let obj = {
            name: '张三',
            info:'爱上你的美',
            say(){
                console.log(this);//{name: '张三', info: '爱上你的美', say: ƒ}
            }
        }

2.this在普通的函数之中就是指向window

function fn(){
            console.log(this);//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
        }
        fn()

3.在js事件动态绑定之中,this的指向就是绑定的当前节点对象

<body>
    <div>我爱写代码</div>
    <script>
        let div = document.querySelector('div');
        div.onclick = function(){
            console.log(this);//<div>我爱写代码</div>
        }
    </script>
</body>

4.在行内事件的绑定之中,this就是指向window

<body>
    <div onclick="fn()">我爱写代码</div>
    <script>
       function fn(){
            console.log(this);//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
        }
    </script>
</body>

5.箭头函数的this指向当前宿主对象也就是上级作用域,普通函数指向window

let obj = {
      name: '我爱写代码',
      say() {
        
        let fn1 = () => {
          console.log(this, this.name,'fn1');//{name: '我爱写代码', say: ƒ} '我爱写代码' 'fn1'

        function fn2() {
          console.log(this, 'fn2');//Window {window: Window, self: Window, document: document, name: '', location: Location, …} 'fn2'

        }
        fn1();
        fn2();
    }
    }
    obj.say()

6.this在构造函数中就是指向被new出来的实例化对象

<script>
     function Good(name, age) {
      this.name = name;
      this.age = age;
      this.say = function(){
        console.log(this);//Good {name: '刘亦菲', age: 99, say: ƒ}
      }
      
    }
    let g1 = new Good('刘亦菲', 99);//实例化对象
    g1.say()

小案例 this指向的改变

<body>
    <div>点击</div>
    <p>我出来了</p>
    <script>
    function Fn(){
        //获取div节点
        this.div = document.querySelector('div');
        //获取p节点的标签
        this.p = document.querySelector('p')
        // 给div添加点击事件
        this.Eve(this.div,'click',this.divhandler.bind(this))
    }
    // 给节点添加2级绑定的事件函数
    Fn.prototype.Eve = function(target,type,cb){
        target.addEventListener(type,cb)
    }
    //div点击事件的回调函数
    Fn.prototype.divhandler = function(){
        this.p.style.display = 'block'
    }
    //实例化对象
    new Fn();
    </script>
</body>

获取两个设置好样式的标签div和P,我们先让p标签隐藏,用构造函数的方式给div绑定点击事件,点击时让p标签显现。 在构造函数中的this指向实例化对象的,但是在div的事件绑定中的回调函数divhandler指向当前绑定的节点对象div,所以无法调动构造函数里的p标签使其显现,然后在事件绑定的函数的后面添加.bind(this)使其的指向改变为实例化对象,就能再点击时调动构造函数里的p标签使其显现。这就是this指向的改变

this指向改变的方法

可以通过callapplybind来修改this的指向

let garen = {
    name: "稻草人",
    age: 22,
    say: function () {
        console.log("我的武器是" + this.name);
    },
};

var weapon = {
    name: "大天使之杖",
};

garen.say(); // 输出:我的武器是稻草人

call

call方法可以接收多个参数

call(修改后的this指向, 参数1, 参数2, 参数3...)
var garen = {
    name: "稻草人",
    age: 22,
    say: function () {
        console.log("我的武器是" + this.name);
    },
};

var weapon = {
    name: "大天使之杖",
};

garen.say.call(weapon) // 输出:我的武器是大天使之杖

call()传入多个参数的使用

var garen = {
    name: "稻草人",
    age: 22,
    say: function (desc) {
        console.log("我的武器是" + this.name, desc);
    },
};

var weapon = {
    name: "大天使之杖",
};

garen.say.call(weapon, "就是个稻草人罢了", "我要三星") // 输出:我的武器是大天使之杖 就是个稻草人罢了

这里可以看到输出的内容少了第三个参数,那是因为desc只传递了第二个参数,第三个参数没有传递。那传递多个参数可以通过新增参数的方式,或者使用 ES6 的扩展运算符来完成。

var garen = {
    name: "稻草人",
    age: 22,
    say: function (...desc) {
        console.log("我的武器是" + this.name, ...desc);
    },
};

var weapon = {
    name: "大天使之杖",
};

garen.say.call(weapon, "就是个稻草人罢了", "我要三星") // 输出:我的武器是大天使之杖 就是个稻草人罢了 我要三星

apply

applycall的区别就在于接收的参数,call能接收多个参数,但apply只能接收2个参数。

第一个和call一样,都是this的指向。

第二个参数是一个数组,将call中的剩余参数都放到了这个数组中。

apply(修改后的this指向, [参数1, 参数2, 参数3...])

仅此而已,其他用法完全一样,只是接收参数的方式改变了。

var garen = {
    name: "盖伦",
    age: 22,
    say: function (...desc) {
        console.log("我的武器是" + this.name, ...desc);
    },
};

var weapon = {
    name: "暴风大剑",
};

// 只有这里修改了
garen.say.apply(weapon, ["我没有特色", "我很肉"]) // 输出:我的武器是暴风大剑 我没有特色 我很肉

bind

bindcall也很相似,不过返回结果不同,bind是返回一个新函数,callapply不会返回新函数,而是直接执行原函数,并返回函数执行的结果

bind(修改后的this指向, 参数1, 参数2, 参数3...)
var garen = {
    name: "盖伦",
    age: 22,
    say: function (...desc) {
        console.log("我的武器是" + this.name, ...desc);
    },
};

var weapon = {
    name: "暴风大剑",
};

// 只有这里修改了
garen.say.bind(weapon, "我没有特色", "我很肉")() // 输出:我的武器是暴风大剑 我没有特色 我很肉
复制代码

3. 总结

一般情况下this指向调用它的实例/window,但也可以手动修改this的指向,callapplybind都是用来修改this的指向的,区别在于:

  1. call接收多个参数,执行原函数,返回结果
  2. apply接收两个参数,第二个参数是数组,执行原函数,返回结果
  3. bind接收多个参数,不会执行函数,只会返回新函数,所以使用时要再调用一次