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 也不 行!)
发表评论 (审核通过后显示评论):