js中this的指向

问题由来

function fn () {
    console.log(this.user);
}
var obj = {
	fn: fn,
	user: 'objZY'
};
var user = 'zy';
fn(); // zy
obj.fn(); // objZY

上面代码中,虽然obj.fn和fn指向同一个函数,但是执行结果不一样。这种差异的原因,就在于函数体内部使用了this关键字。

js中的this指的是函数运行时所在的环境(即调用的对象)。也就是说 this 既不是指向函数自身也不指向函数的作用域,this 实际上是在函数被调用时才发生绑定,它指向什么地方完全取决于函数在哪里被调用。

默认绑定

在 js中 ,最常用的函数调用类型就是独立函数调用。

function a(){
    console.log(this.user); //zy
    console.log(this); //Window
}
var user = "zy";
a();

如果在调用函数的时候,函数不带任何修饰,也就是“光秃秃”的调用,那就会应用默认绑定规则, 默认绑定的指向的是全局作用域。

隐式绑定

当函数在调用时,如果函数有所谓的“落脚点”,即有上下文对象(即调用时.前面有对象)时,隐式绑定规则会把函数中的 this 绑定到这个上下文对象。

var user = 'zy1';
var obj = {
    user:"zy",
    fn:function(){
        console.log(this.user);  //zy
    }
};
obj.fn();

在上面这段代码中,obj 就是所谓的 fn 函数的落脚点,专业一点的说法就是上下文对象,当给函数指定了这个上下文对象时,函数内部的this 自然指向了这个上下文对象。这也是很常见的一种函数调用模式。

隐式绑定时丢失上下文

var user = 'zy1';
var obj = {
    user:"zy",
    fn:function(){
        console.log(this.user);  //zy1
        console.log(this); //Window
    }
};
var f = obj.fn;
f();

可以看到这里输出的是 ”zy1“ ,和上例中不一样,只是给 obj.fn换了个名字而已?

这个可以结合函数的存储理解:obj.fn只是一个函数地址,不是函数本身。所以调用时,还是在当前全局环境下调用,即与obj这个对象无关了,这就是所谓的”丢失上下文“。

显示绑定

显式绑定,顾名思义,显示地将this绑定到一个上下文,js中提供了三种显式绑定的方法,apply、call、bind。

其中apply和call的用法基本相似,它们之间的区别是:

apply(obj,[arg1,arg2,arg3,...]) //被调用函数的参数以数组的形式给出
call(obj,arg1,arg2,arg3,...)  //被调用函数的参数依次给出
function fn() {
    console.log(this.a);
}

var obj1 = {
    a: 2
};
var obj2 = {
    a: 3
};

fn.call(obj1); // 2
fn.apply(obj1); // 2

fn.call(obj2); // 3
fn.apply(obj2); // 3

因此可以看出,apply, call 的作用就是给函数绑定一个执行上下文,且是显式绑定的。因此,函数内的this自然而然的绑定在了 call 或者 apply 所调用的对象上面。

而bind的用法,是在函数没有调用前就可以绑定一个执行上下文

function fn() {
    console.log(this.a);
}

var obj1 = {
    a: 2
};
var obj2 = {
    a: 3
};

 var fn1 = fn.bind(obj1);
 var fn2 = fn.bind(obj2);

fn1(); // 2
fn2(); // 3

new绑定

new 绑定,是指通过 new 操作符调用构造函数时发生的 this 绑定。

function Fn(name){
    this.user = name;
}
var a = new Fn('zy');
var b = new Fn('bzy');
console.log(a.user); //zy
console.log(b.user); //bzy

这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

四种this绑定的优先级

上面四种this绑定规则,每个规则分开应用可以理解。但是如果同时应用了这四种规则中的两种甚至更多,优先级顺序怎么处理?

首先,很容易理解,默认绑定的优先级是最低的。这是因为只有在无法应用其他this绑定规则的情况下,才会调用默认绑定。那隐式绑定和显式绑定呢?看下面代码:

function fn() {
    console.log(this.a);
}

var obj1 = {
    a: 2,
    fn: fn
};
var obj2 = {
    a: 3
};

obj1.fn(); // 2
obj1.fn.call(obj2); //3

在上面代码中,执行了obj1.fn(),fn函数内部的this指向了obj1,因此代码输出的当然就是obj1.a;但是当显式绑定了fn函数内的this到obj2上,输出结果就变成了obj2。所有从这个结果可以看出显式绑定的优先级是要高于隐式绑定的。

事实上我们可以这么理解obj1.fn.call(obj2)这行代码,obj1.fn只是间接获得了fn函数的引用,这就有点像前面所说的隐式绑定丢失了上下文。

现在知道了显式绑定的优先级要高于隐式绑定,那么接下来再来比较一下new 绑定和显式绑定。

function fn(name) {
	this.a = name;
}
var obj = {};
var o = fn.bind(obj);  // (1)
o(2);

var o2 = new o(3);  // (2)

console.log(obj.a); //2
console.log(o2.a); //3

我们可以看到,在(1)处,o函数内部的this原本指向的是obj,但是在(2)处,由于经过了new操作符调用,bar函数内部的this却重新指向了返回的实例,这就可以说明new 绑定的优先级是要高于显式绑定的。

至此,四种绑定规则的优先级排序就已经得出了,分别是:

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

箭头函数中的this绑定

箭头函数是ES6里一个重要的特性。
箭头函数的this是根据外层的(函数或者全局)作用域来决定的。函数体内的this对象指的是定义时所在的对象,而不是之前介绍的调用时绑定的对象。举一个例子

var a = 1;
var foo = () => {
    console.log(this.a) // 定义在全局对象中,因此this绑定在全局作用域
};

var obj = {
    a: 2
};
foo(); // 1 ,在全局对象中调用
foo.call(obj); // 1,显示绑定,由obj对象来调用,但根本不影响结果

从上面这个例子看出,箭头函数的 this 强制性的绑定在了箭头函数定义时所在的作用域,而且无法通过显示绑定,如apply,call方法来修改。

小结

以上就是javascript中所有this绑定的情况,在es6之前,前面所说的四种绑定规则可以涵盖任何的函数调用情况,es6标准实施以后,对于函数的扩展新增了箭头函数,与之前不同的是,箭头函数的作用域位于箭头函数定义时所在的作用域。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值