js中关于this全面解析和this的指向问题

this指向原则
每个函数的this是在调用的时候被绑定的,完全取决于函数的调用位置。

1、 调用位置

调用位置就是函数在代码中被调用的位置(而不是声明的位置)。要找到调用位置就要分析调用栈(就是为了达到当前执行位置所调用的所有函数),我们要找的调用位置就在当前正在执行的函数的前一个调用中。如以下代码:

function baz() {
    // 当前的调用栈是baz
    // 当前的调用位置是全局作用域
    console.log(‘baz’);
    bar(); // bar的调用位置
}
function bar() {
    // 当前的调用栈 window  baz  bar
    // 因此,当前调用位置在 baz 中 
    foo(); // foo的调用位置
}
function foo () {
    // 当前的调用栈是 window –> baz  bar  foo
    // 所以foo函数的当前调用位置在bar中
    Console.log('foo');le.log(‘foo’);
}
baz(); // <-- baz 的调用位置 

2、绑定规则

(1)默认绑定
直接独立调用的函数this指向就使用的了默认绑定规则直接指向全局作用域如下代码:

function foo() {
    console.log( this.a ); 
} 
    var a = 2; 
foo(); // 2 

foo()函数是直接使用不带任何修饰的函数引用进行调用的,因此使用默认绑定规则。
(注意: 默认绑定规则在非严格模式下指向window,而在严格模式下的时候就会指向undefined)

(2)隐士绑定
隐士绑定规则要看调用位置是否有上下文对象,也就是说看函数是否被某个对象拥有或者引用。代码如下所示:

function foo() { 
    console.log( this.a ); 
} 
var obj2 = { 
    a: 42, 
    foo: foo
}; 
var obj1 = { 
    a: 2, 
    obj2: obj2 
}; 
obj1.obj2.foo(); // 42 

上述代码,虽然函数的声明位置在全局作用域中,但是而后被当做引用属性添加到了obj2中,(不管是直接在obj中定义还是先定义在添加为引用属性,这个函数严格意义来说都不属于obj2), 但是函数被调用的时候使用的是obj2的上下文引用,这个时候就触发了隐士绑定规则,当函数调用时,有引用的上下文对象时,隐士绑定规则就把
函数的this指向了这个上下文对象。
(注意:对象属性引用链中只有最顶层或者说最后一层会影响调用位置 )

(3)显示绑定
JavaScript 提供的绝大多数函数以及你自 己创建的所有函数都可以使用 call(..) 和 apply(..) ,bind()方法,使用这个方法会调用当前函数,并把this指向指定对象,这种绑定方式称为显示绑定。代码如下:

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

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

判断this的指向

根据规则判断函数在调用的时候this的指向:
1> 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。

  var bar = new foo()

2> 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是 指定的对象。

var bar = foo.call(obj2) 

3> 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。

var bar = obj1.foo() 

4> 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到 全局对象。

var bar = foo()

绑定例外

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

function foo() { 
    console.log( this.a ); 
}
var a = 2;
foo.call( null ); // 2 

箭头函数

箭头函数是es6中的新语法,它不使用this的四种绑定标准,而是根据外层作用域来决定this的指向。代码如下:

function foo() {
    // 返回一个箭头函数 
    return (a) => { 
    //this 继承自 foo() 
    console.log( this.a ); }; 
} 
var obj1 = {
    a:2 
}; 
var obj2 = { 
    a:3 
};
var bar = foo.call( obj1 ); 
bar.call( obj2 ); // 2, 不是 3 ! 

foo() 内部创建的箭头函数会捕获调用时 foo() 的 this。由于 foo() 的 this 绑定到 obj1, bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。(new 也不 行!)

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):