您的位置:首页 > Web前端 > JavaScript

详解JavaScript中的 this 指向

2018-02-06 01:27 633 查看
this是在运行时进行绑定的,并不是在在编译时绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用位置。
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文),而this就是这个记录的一个属性。
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
调用位置:
既然this的指向完全取决于函数的调用位置,那么,怎么才能找到函数的调用位置呢?
function a(){
 //a的调用栈为:a
 b();   //b的调用位置
}

function b(){
  //b的调用栈为: a--->b
 c();   //c的调用位置
}

function c(){
  //c的调用栈为: a--->b---->c
 console.log("123");
}
a();   //a的调用位置

你可以把调用栈想象成一个函数调用链,当然你也可以直接用JavaScript调试器查看函数的调用栈。然后找到栈中的第二个元素,这就是真正的调用位置。
this的绑定规则:
上面也已经提到,this是在函数调用时发生的绑定,它指向什么完全取决于函数的调用位置。也就是说,this的绑定规则完全取决于调用位置。

1. 默认绑定
非严格模式下,this 指向 window,  严格模式下 this 指向 undefined。
独立函数调用,就使用的是默认绑定规则。(无法使用其他规则时就会使用默认规则)
 function foo(){
 console.log(this.a);
 }
var a = 2;
foo();     //2
2. 隐式绑定
当函数被调用时,它的前面加上了obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
function foo(){
 console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo
};

obj.foo();   //2

隐式丢失:
 function foo(){
 console.log(this.a);
 }
var obj = {
  a: 2,
  foo: foo
};
var bar = obj.foo;
var a = 3;
bar();   //3

注:虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,此时的bar其实是一个不带任何修饰符的函数调用,因此使用了默认规则。
    function foo(){
  console.log(this.a);
}
function doFoo(fn){
  //fn其实引用的的是foo
 fn(); 
}
var obj = {
  a: 2,
  foo: foo
};
var a = 3;
doFoo( obj.foo );      //3
注:这里需要注意,传参其实就是进行赋值操作。结果是一样的,没有区别。
    function foo(){
 console.log(this.a);
}
var obj = {
  a: 2,
  foo: f
4000
oo
};
var a = 3;
setTimeout( obj.foo, 100);      // 3 

3. 显示绑定:
    function foo(){
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
foo.call(obj);   //2

通过foo.call(),我们可以在调用foo时强制把它的this绑定到obj上。
当然,还可以通过appay和bind方法也可以显示的将this绑定到obj上,可以看我后面的文章。
4. new绑定
首先,我先重新定义一下JavaScript中的“构造函数”,在JavaScript中,构造函数只是一些使用new操作符时调用的函数。他们并不会属于某个类,也不会实例化一个类。它们只是被new操作符调用的普通函数而已。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行[[Prototype]]连接。
3. 这个新对象会绑定到函数调用的this.
4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);   //2

四种绑定规则的优先级:
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

判断this:
1.函数是否在new中调用(new绑定),如果是的话this绑定的是新创建的对象。
2. 函数是否通过call,apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对 象。
3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象

当然,凡事都有例外,上面所说的适用于绝大多数情况,但也有一些例外:
如果你把null或者undefined作为this的绑定对象传入call,apply,bind,这些值在调用时会被忽略,实际应用的是默认的绑定规则。
function foo(){
console.log(this.a);
}
var a = 2;
foo.call(null);   //2

箭头函数:
箭头函数并不是使用function关键字定义的,而是使用被称为“胖箭头”的操作符=>定义的,箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。也就是说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。
function foo(){
return (a) => {
console.log(this.a);
};
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = foo.call(obj1);
bar.call(obj2);    //2 , 不是3!!!

注:还有一点需要注意,箭头函数的绑定无法被修改。(new也不行)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: