彻底理解this

1.什么是 this

  • JS中的this代表的是当前行为执行的主体
  • this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

2.this的五大类

(1) 函数执行时首先看函数名前面是否有".",有的话,"."前面是谁,this就是谁;没有的话this就是window

function fn(){
  console.log(this);
}
var obj={fn:fn};
fn();//this->window
obj.fn();//this->obj
function sum(){
     fn();//this->window
}
sum();
var oo={
 sum:function(){
 console.log(this);//this->oo
       fn();//this->window
  }
};
oo.sum();

上面也叫做隐式绑定的隐患

隐式绑定的隐患:被隐式绑定的函数,因为一些不小心的操作会丢失绑定对象,此时就会应用最开始讲的绑定规则中的默认绑定,看下面代码:

function child() {
  console.log(this.name);
}
let parent = {
  name: 'zhangsan',
  child,
}
let parent2 = parent.child;
var name = 'lisi';
parent2();   this指向window
//将 parent.child 函数本身赋给 parent2
//调用 parent2() 其实是一个不带任何修饰的函数调用,因此会应用默认绑定。

(2) 自执行函数中的this永远是window

  (function(){ //this->window })();
  ~function(){ //this->window }();

(3)给元素的某一个事件绑定方法,当事件触发的时候,执行对应的方法,方法中的this是当前的元素,除了IE6~8下使用attachEvent(IE一个著名的bug)

1.DOM零级事件绑定
  oDiv.onclick=function(){
     //this->oDiv
  };
  
  2.DOM二级事件绑定
    oDiv.addEventListener("click",function(){
     //this->oDiv
  },false);
  
  3.在IE6~8下使用attachEvent,默认的this就是指的window对象 (例外)
    oDiv.attachEvent("click",function(){
       //this->window
  });

(4) 使用 new 实例化对象,在构造函数中的this指向实例化对象。

var Show=function(){
    this.myName="Mr.Cao";   //这里的this指向的是obj对象
}
var obj=new Show();

注意: 类中某一个属性值(方法),方法中的this需要看方法执行的时候,前面是否有".",才能知道this是谁。

function Fn(){
this.x=100;//this->f1
this.getX=function(){
console.log(this.x);//this->需要看getX执行的时候才知道
   }
}
var f1=new Fn;
f1.getX();//->方法中的this是f1,所以f1.x=100
var ss=f1.getX;
ss();//->方法中的this是window ->undefined

(5) call、apply和bind

  • call:改变this指向,第一个参数为this指向,后面接受一系列参数。 严格模式和严格模式不同、
//在非严格模式下
var obj={name:"浪里行舟 "};
function fn(num1,num2){
console.log(num1+num2);
console.log(this);
}
fn.call(100,200);//this->100 num1=200 num2=undefined
fn.call(obj,100,200);//this->obj num1=100 num2=200
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window
//严格模式下 
fn.call();//在严格模式下this->undefined
fn.call(null);// 在严格模式 下this->null
fn.call(undefined);//在严格模式下this->undefined
  • apply:跟call一样改变this指向,第一个参数为this指向,第二个参数为一个数组 为接受参数
fn.call(obj,100,200);
fn.apply(obj,[100,200]);
  • bind:作用一样会改变this指向,后面项为参数。
fn.call(obj,1,2);//->改变this和执行fn函数是一起都完成了
fn.bind(obj,1,2);//->只是改变了fn中的this为obj,并且给fn传递了两个参数值1、2,
                     但是此时并没有把fn这个函数执行
var tempFn=fn.bind(obj,1,2);
tempFn(); //这样才把fn这个函数执行
不同点:体现预处理思想事先把fn的this改变为我们想要的结果,并且把对应的参数值也准备好,以后要用到了,直接的执行即可。
call和apply直接执行函数,而bind需要再一次调用。
  var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }
  var b = a.fn;
  b.bind(a,1,2)

必须要声明一点:遇到第五种情况(call apply和bind),前面四种全部让步。

(6)箭头函数的this: 为更简短的函数并且不绑定this。

  • 箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this。
  • 换句话说,箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。
  • 用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略:
    <button id="btn1">测试箭头函数this_1</button>
    <button id="btn2">测试箭头函数this_2</button>
    <script type="text/javascript">   
        let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                btn1.onclick = () => {
                    console.log(this);//obj
                };
            }
        };
        obj.getName();
    </script>
    
    
上例中,由于箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。其实可以简化为如下代码:
   let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                console.log(this)
            }
        };
   obj.getName();
   

那假如上一层并不存在函数,this指向又是谁?   
 <button id="btn1">测试箭头函数this_1</button>
    <button id="btn2">测试箭头函数this_2</button>
    <script type="text/javascript">   
        let btn2 = document.getElementById('btn2');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: () => {
                btn2.onclick = () => {
                    console.log(this);//window
                };//虽然存在两个箭头函数,其实this取决于最外层的箭头函数,由于obj是个对象而非函数,所以this指向为Window对象
            }https://segmentfault.com/a/1190000016680885
        };
        obj.getName();
    </script>

用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略:

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth仍是1990
        return fn.call({birth:2000}, year);
    }
};
obj.getAge(2018); // 28

优先级:new 绑定 -> 显示绑定 -> 隐式绑定 -> 默认绑定。

思考题:

function Fruit(name) {
  this.name = name;
}
Fruit.prototype.info = () => {
  console.log(this.name);
}
var name = 'Banana'
const f1 = new Fruit('Apple');
f1.info();  //打印什么?

答案: f1.info(); //Banana
//因为this指向window
如果使用构造函数和原型链模拟类,不能在原型链上定义箭头函数,因为箭头函数的里的 this 会继承外层函数调用的 this 绑定。

参考文章:
你还没搞懂this?
JavaScript 中 this 的错误认识和常见问题讲解

猜你喜欢

转载自blog.csdn.net/HZ___ZH/article/details/109788445