黑马程序员技术交流社区

标题: 一篇文章搞定JavaScript中this指向问题 [打印本页]

作者: charcoal    时间: 2021-5-24 16:06
标题: 一篇文章搞定JavaScript中this指向问题
this取值是在函数执行是确定的:
const fn2= fn1.bind({name:1})
fn2()//bind会返回一个新的函数执行




第一部分:常见的this指向问题1. 全局上下文中的 this
在浏览器环境中,全局上下文中的 this,就是 window。


[JavaScript] 纯文本查看 复制代码
let a = 12;
console.log(this); // 控制台输出为 window


2. 块级上下文中的 this




[JavaScript] 纯文本查看 复制代码
let obj = {
  fn() {
    // fn函数中的this是obj
    {
      let a = 12;
      console.log(this); // 输出obj。继承上级上下文,即函数fn的私有上下文中的this
    }
  },
};
obj.fn();


3. 给事件绑定方法中的this

函数私有上下文中的 this 千变万化,可以总结为以下五种情况:

3.1 事件绑定
给元素的某个事件行为绑定方法,事件触发时方法执行,此时方法中的 this一般为当前元素本身


[JavaScript] 纯文本查看 复制代码
let body = document.body;
//DOM0中事件绑定
body.onclick = function () {
  console.log(this); // body
};


3.2 在 element 上绑定事件

[JavaScript] 纯文本查看 复制代码
<div class="container">
  <div id="aa"></div>
</div>;

function clicke() {
  console.log(this); // Window
}


3.3 js 绑定 onclick 事件
this 指向该元素
[JavaScript] 纯文本查看 复制代码
<div class="container">
  <div id="aa"></div>
</div>;

document.getElementById("aa").onclick = function () {
  console.log(this); //  <div id="aa"></div>
};

3.4 js 使用 addEventListener 绑定事件
此时的 this 指向 该元素, 注意: 在 IE 浏览器中,使用为 attachEvent(), this 指向全局变量

[JavaScript] 纯文本查看 复制代码
<div class="container">
  <div id="aa"></div>
</div>;

document.getElementById("aa").addEventListener("click", function () {
  console.log(this); //  <div id="aa"></div>
});

3.5 jquery 的 3 种绑定 click 事件
此时的 this 均指向该元素

[JavaScript] 纯文本查看 复制代码
$("#aa").bind("click", function () {
  console.log(this); //  <div id="aa"></div>
});

$("#aa").click(function () {
  console.log(this); //  <div id="aa"></div>
});

$("#aa").on("click", function () {
  console.log(this); //  <div id="aa"></div>
});

4. 普通函数执行中的this
这里的普通方法包含:普通函数、自执行函数、对象成员访问调取方法执行…等。只要看函数执行时,方法名前是否有“点”就是了。如果有“点”——xxx.方法()中 this 就是 xxx;如果没有“点”——this 即为 window(严格模式下为 undefined)。

[JavaScript] 纯文本查看 复制代码
// 自执行函数
(function(){
        console.log(this); // 非严格模式下输出window,严格模式下输出undefined
})();

let obj = {
        fn: (function(){
                console.log(this);    // 输出window
                return function(){};
        })();
}
/*
上面两个例子都是自执行函数执行,输出均为window。说明this的指向,与函数在哪
定义和执行的无关。自执行函数中的this一般都为window(因为函数执行时前面没有“点”)
*/


[JavaScript] 纯文本查看 复制代码
function func(){
        console.log(this);
}
let obj = {
        func:func
}

// 普通函数执行
func();            // this:window
// 成员访问方法执行
obj.func();        // this:obj


[].slice()方法中this->当前空数组。
Array.prototype.slice()方法中的this->Array.prototype。


涵盖以上两种情况的一道题: 点击body输出什么?

[JavaScript] 纯文本查看 复制代码
function func(){
        console.log(this);//Window
}
document.body.onclick = function(){
    console.log(this)//HTMLBodyElement
        func();
}
/*
body
window
*/

5 构造函数执行的this
构造函数体中的 this 指向的就是当前类的实例。因此,函数中出现的 this.xxx = xxx 这样的代码就是给当前实例设置私有属性;
换句话说,类中凡是不带 this 的代码统统都与它创建的实例无关,充其量也就是这个函数私有上下文中的私有变量而已。


[JavaScript] 纯文本查看 复制代码
function Func() {
  console.log(this);
  // 构造函数执行,构造函数体中的this指向当前类的实例——f,因此输出的this为f
}
Func.prototype.getNum = function getNum() {
  console.log(this);
};
let f = new Func();
//原型上方法中的this不一定是实例,要看执行时前面有没有点( 即当作普通函数看待 )
f.getNum(); //f
f.__proto__.getNum(); //f.__proto__
Func.prototype.getNum(); //Func.prototype


6. ES6 中的箭头函数
[JavaScript] 纯文本查看 复制代码
let obj = {
        func:function(){
                console.log(this);
        }
        sum:()=>{
                console.log(this);
        }
}
obj.func();       // this:obj 对象成员访问
obj.sum();        // this:window
obj.sum.call(obj);    // this:window 箭头函数没有this,改也没用


7. 回调函数中的 this
回调函数中的 this 一般指向 window,但也有例外;
[JavaScript] 纯文本查看 复制代码
      let obj = {
        i: 0,
        func() {
          console.log("func", this); //obj
          setTimeout(function () {
            console.log("定时器里面的", this); //window
          }, 1000);
        },
      };
      obj.func();


如何改写 this?
1.赋值that=this
[JavaScript] 纯文本查看 复制代码
let obj = {
        i:0,
    let that=this;
        func(){
                setTimeout(function(){
                        that.i++;
                        console.log(that);    // {i:1,func:f}
                },1000);
        }
}
obj.func();

2.用bind强行改 function () {}.bind(this)
[JavaScript] 纯文本查看 复制代码
let obj = {
  i: 0,
  func() {
    setTimeout(
      function () {
        this.i++;
        console.log(obj); // {i:1,func:f}
      }.bind(this),
      1000
    ); // 这里强行让内部这个回调函数中的this指向外层的this(即obj)
  },
};
obj.func();
3.用箭头函数
[JavaScript] 纯文本查看 复制代码
//第三种解决方案:回调函数用箭头函数,箭头函数中没有自己的this,这里用的this是继承自外层的,也就是func函数的this->obj!
let obj = {
  i: 0,
  func() {
    setTimeout(() => {
      this.i++;
      console.log(obj); // {i:1,func:f}
    }, 1000);
  },
};
obj.func();
第二部分:改变this指向
2.1 call

[JavaScript] 纯文本查看 复制代码
[function].call([context],params1,params2,…)

含义:执行 call 方法时,会将后面的参数传递给[function]执行,并把函数中的 this 修改为[context]:
@ [function]: 希望改变自身 this 的那个函数
@ [context]: 希望把 this 变为它!
@ params1, params2, … : 传递给函数的参数

第一个参数不传 / null / undefined时:
非严格模式——this:window
严格模式——传谁this就是谁,不传就是undefined

2.2 apply
与 call 方法相同,只不过传递函数实参以数组的方式。
[JavaScript] 纯文本查看 复制代码
[function].call([context],[params1,params2,…])


2.3 bind
bind 的语法与 call 相同,作用与以上两个方法均不同。 它是预先修改 this,预先存储参数,而函数不被立即执行; 也就是说,call / apply 都是把函数立即执行的,并且改变 this。
[JavaScript] 纯文本查看 复制代码
[function].call([context],params1,params2,…)

2.4案例分析
需求:把func函数绑定给body的click事件,触发body的click事件时,func函数执行要让func函数中的this指向obj,并给func函数传递参数10,20;
[JavaScript] 纯文本查看 复制代码
document.body.onclick = function anonymous() {
  func.call(obj, 10, 20);
};
/*
将一个匿名函数绑定给body的点击事件,也就是将这个匿名函数的地址绑定给了事件,
并没有立即执行。当点击事件触发时,将匿名函数执行,然后执行func.call。
事实上,在bind方法之前,大家就是这样处理的。已经和bind的原理非常类似了。
*/

[JavaScript] 纯文本查看 复制代码
document.body.onclick = func.bind(obj, 10, 20);
/*
bind函数不会立即执行,而是预先修改this,并预先存储需要传递的参数。后续需要时(事件触发)再执行函数。
*/

2.5 bind 原理
[JavaScript] 纯文本查看 复制代码
      function func(x, y) {
        console.log(this, x, y); //改了之后就是obj
      }

      Function.prototype.bind1 = function bind1(context = window, ...params) {
/*
1.bind方法执行的时候,预先修改this,预先存储参数,而函数不被立即执行,所以要
返回一个函数特定时(比如点击之后)候执行
*/
//2.bind执行的时候,因为是func调用的所以this指向func
        console.log("bind函数里面的this:", this); //func
//3.当点击事件时会执行bind()的返回结果,因为func.bind()执行后返回一个函数,被onclick绑定了
        let that = this;
        return function anonymous() {
          //匿名函数的this指向window的,要改变func的this,让that=this
          console.log("匿名函数的this", this); //window
          //改变this,并执行函数
          that.call(context, ...params);
        };
      };
      let obj = {};
      onclick = func.bind1(obj, 10, 20);
      /*
第一步:bind传参设置:如果不传参,this默认为改为window,后面的参数用展开运算符接受
第二步:特定条件下,bind才会改变this指向,比如点击了才改变this指向,而func.bind1(obj, 10, 20);时立即执行的,所以他的执行结果时返回一个函数,让点击事件绑定
第三步:bind执行的时候,因为是func调用的所以this指向func;而返回的供点击事件执行的匿名函数他的this是window;
第四步:bind函数里面让that=this,然后在匿名函数里面执行that.call()也就是func.call()
      */











欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2