js中的this指向理解

1:为什么要使用this?

 this提供了一种更优雅的方法来隐式'传递'一个对象的引用,因此可以将API设计得更加简洁并且易于复用。

   需要注意大家一般会有的两个误解:
(1)this指向函数自身
(2)this指向函数的作用域

作用域无法通过JavaScript代码访问,它存在于JavaScript引擎内部。每当把this和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的!

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用位置(也就是函数的调用方式)!

2:this的四种绑定规则

  1)默认绑定:

     当在全局作用域中独立调用函数的时候,如 fun(); ,此时this指代的是全局对象,即window。 

 

function foo() {
  console.log(this.a); // this指向全局对象
}
var a = 2;
foo(); // 2


function foo() {
  "use strict"; // 在严格模式下,全局对象将无法使用默认绑定,因此this会绑定到undefined。
  console.log(this.a); 
}
foo(); // TypeError:a undefined

      (对于默认绑定来说,决定this绑定对象的是函数体是否处于严格模式,严格指向undefined,非严格指向全局对象)


    2)隐式绑定:

      在函数调用时,其存在上下文对象,也就是被某个对象拥有,如:var obj = { fun: fun(); }; ,此时this绑定到这个上下文对象上。 

function foo() {
      console.log(this.a);
    }
    var a = "1";
    let obj1 = {
      a: 2,
      foo: foo
    };
    let obj2 = {
      a: 22,
      obj2: obj2
    };
    obj1.foo(); // 2 this指向调用函数的对象
    obj2.obj1.foo(); // 2 this指向最后一层调用函数的对象

  隐式丢失

    常见的this绑定问题就是“隐式绑定”的函数会丢失绑定对象,也就是“默认绑定”,从而把this绑定到全局对象(严格模式下为undefined)。

var a = "foo";  
function foo(){  
    console.log(this.a);  
}  
var obj = {  
    a : 2,  
    foo : foo  
}  
var bar = obj.foo;  
bar();  //"foo"  

   很好的一个例子:

   var a = "foo";  
function foo(){  
    console.log(this.a);  
}  
function test(fn){     
    fn();  
    // 相当于 let = obj.foo  绑定丢失  this指向window
}  
var obj = {  
    a : 2,  
    foo : foo  
}  
test(obj.foo); //"foo" 


    3)显式绑定:

调用call(...)或apply(...)进行显式绑定,将函数中的this绑定到指定的对象上,如 fun.call(obj); 或 fun.apply(obj); 。 

function foo() {
    console.log(this.a);
}
let obj = {
    a: 2
};
foo.call(obj); // 2

当传入的不是对象:

如果你传入了一个原始值(字符串,布尔类型,数字类型),来当做this的绑定对象,这个原始值转换成它的对象形式。

如果你把null或者undefined作为this的绑定对象传入call/apply/bind,这些值会在调用时被忽略,实际应用的是默认绑定规则

    4)new绑定:

  new调用函数会自动执行下面操作:
(1)创建(或者说构造)一个全新的对象;
(2)这个新对象会被执行[[原型]]连接;
(3)这个新对象会绑定到函数调用的this;
(4)如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

 

调用函数是在函数前使用new关键字,会将函数绑定到此时赋值的对象上,如 var obj = new fun(); 。
在上述几种规则中,优先级顺序如下:

                              显式绑定 > new绑定 > 隐式绑定 > 默认绑定

3:绑定this注意点

1) 忽略this

把null或undefined作为this的绑定对象传入call、apply、bind,调用时会被忽略,实际应用的是默认绑定规则!

function foo(){  
    console.log(this.a);  
}  
var a = 1;  
foo.call(null, 2);          //1  
foo.apply(undefined, [3]);  //1  

 

2) 间接引用

function foo(){  
    console.log(this.a);  
}  
var a = 2;  
var o = { a : 3,foo : foo};  
var p = { a : 4};  
o.foo();            //3  
(p.foo = o.foo)();  //2 间接引用  
var pfoo = o.foo;  
pfoo();         //2 隐式丢失  

注意:同上述“隐式丢失”结果一样,但是过程不太一样,区分!!

3) ES6箭头函数

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
箭头函数的绑定无法被修改。常用于回调函数中,如事件处理器或定时器。和ES6之前代码中的this = self机制一样。

function foo(){  
    setTimeout(()=>{  
        console.log(this.a);  
    },100);  
}  
var obj = { a : 2};  
foo.call(obj);  

等价于:

function foo(){  
    var self = this;  
    setTimeout(function(){  
        console.log(self.a);  
    },100);  
}  
var obj = { a : 2};  
foo.call(obj); 

4:详解如何确定this

1)(隐式绑定)如果某个对象中某个成员是个function,当从这个对象上调用这个方法时this指向当前对象。

var FenFei = {  
    firstname:"li",  
    lastname:"gang",  
    timeTravel:function(year){  
        console.log(this.firstname + " " + this.lastname + " is time traveling to " + year);  
    }  
}  
  
FenFei.timeTravel(2014);    //li gang is time traveling to 2014(父/拥有者对象:FenFei)

2)(隐私绑定)可以通过创建一个新的对象,来引用FenFei对象上的timeTravel方法。

var Camile = {  
    firstname:"li",  
    lastname:"yunxia"  
}  
  
Camile.timeTravel = FenFei.timeTravel;  
Camile.timeTravel(2014);    //li yunxia is time traveling to 2014(父/拥有者对象:Camile)  

注意:此示例同上述“隐式丢失”、“间接引用”区分!!!

3)(隐式丢失)使用变量保存FenFei.timeTravel方法的引用

var getTimeTravel = FenFei.timeTravel;  
getTimeTravel(2014);    //undefined undefined is time traveling to 2014(父/拥有者对象:Window;window对象里并没有firstName和lastName属性)  

PS:谨记方法中的this将指向调用它的那个父/拥有者对象!无论何时,当一个函数被调用,我们必须看方括号或者是圆括号左边紧邻的位置,如果我们看到一个引用(reference),那么传到function里面的this值就是指向这个方法所属于的那个对象,如若不然,那它就是指向全局对象的。

4)异步调用的方法内部的this

<button id="async">点击我</button>  
var btnDom = document.getElementById("async");  
btnDom.addEventListener('click',FenFei.timeTravel);//undefined undefined is time traveling to [object MouseEvent](父/拥有者对象:button)  
  
btnDom.addEventListener('click',function(e){  
    FenFei.timeTravel(2014);//li gang is time traveling to 2014(父/拥有者对象:FenFei)  
});  

5)(new绑定)构造函数里的this

当使用构造函数创建一个对象的实例时,构造函数里的this就是新建的实例。

var TimeTravel = function(fName, lName){  
    this.firstname = fName;  
    this.lastname = lName;  
}  
  
var FenFei = new TimeTravel("li", "gang");  
console.log(FenFei.firstname + " " + FenFei.lastname);      //li gang  

6)(显示绑定)call和apply方法设定this值

var Camile = {  
    firstname:"li",  
    lastname:"yunxia"  
}  
  
FenFei.timeTravel.call(Camile,2014);        //li yunxia is time traveling to 2014(指定this对象为Camile)  
FenFei.timeTravel.apply(Camile,[2014]); //li yunxia is time traveling to 2014(指定this对象为Camile)

PS:注意和上述“2”处做对比

7)(显示绑定)bind将函数绑定至某个对象(ES5)

function f(y){  
    return this.x + y;  
}  
var o = {x:1};  
/* f.bind(o)返回一个新函数,调用g(2)会把原始的函数f()当作o的方法来调用 */  
var g = f.bind(o);    
g(2);   // 3  

补充:ES5之前方法模拟bind()方法

function myBind(f, o){  
    if(f.bind){  
        return f.bind(o);  
    }else{  
        return function(){  
            return f.apply(o, arguments);  
        }  
    }  
}  

bind的作用和apply,call类似都是改变函数的execute context,也就是runtime时this关键字的指向。但是使用方法略有不同。一个函数进行bind后可稍后执行。 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值