关于this

 

一、this到底是什么

this是在运行时候进行绑定的,并不是编写时绑定的。this的绑定和函数声明的位置没有关系,只取决于函数的调用方式。

真正了解this的绑定就要分析调用位置,调用位置就在当前正在执行的函数的前一个调用中。

function baz() {
// 当前调用栈是:baz
// 因此,当前调用位置是全局作用域
console.log( "baz" );
bar(); // <-- bar 的调用位置
}
function bar() {
this全面解析 | 83
// 当前调用栈是 baz -> bar
// 因此,当前调用位置在 baz 中
console.log( "bar" );
foo(); // <-- foo 的调用位置
}
function foo() {
// 当前调用栈是 baz -> bar -> foo
// 因此,当前调用位置在 bar 中
console.log( "foo" );
}
baz(); // <-- baz 的调用位置

  可以在工具中给 foo() 函数的 第一行代码设置一个断点,或者直接在第一行代码之前插入一条 debugger; 语句。运行代码时,调试器会在那个位置暂停,同时会展示当前位置的函数 调用列表,这就是你的调用栈。因此,如果你想要分析 this 的绑定,使用开 发者工具得到调用栈,然后找到栈中第二个元素,这就是真正的调用位置。

二、this的绑定规则

1、默认绑定,即独立函数调用。

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

  函数调用时应用了 this 的默认绑定,因此 this 指向全局对象。如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定 到 undefined;

2、隐式绑定,考虑调用位置是否有上下文或者被某个对象拥有或者包含。

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

  调用位置会使用 obj 上下文来引用函数,因此你可以说函数被调用时 obj 对象“拥 有”或者“包含”它。当函数引 用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。

***隐式丢失

function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
//虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的
bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

3、显式绑定(call、apply、bind)

function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
通过 foo.call(..),我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。

 硬绑定:

function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的 bar 不可能再修改它的 this
bar.call( window ); // 2


function foo(something) {
console.log( this.a, something );
return this.a + something;
}
90 | 第 2 章
var obj = {
a:2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
bind(..) 会返回一个硬编码的新函数,它会把参数设置为 this 的上下文并调用原始函数。

  我们创建了函数 bar(),并在它的内部手动调用 了 foo.call(obj),因此强制把 foo 的 this 绑定到了 obj。无论之后如何调用函数 bar,它 总会手动在 obj 上调用 foo。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。

4、new绑定

 在 JavaScript 中,构造函数只是一些 使用 new 操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上, 它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

1. 创建(或者说构造)一个全新的对象。

2. 这个新对象会被执行 [[ 原型 ]] 连接。

3. 这个新对象会绑定到函数调用的 this。

4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
//使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this
上。

如果想“更安全”地忽略 this 绑 定,你可以使用一个 DMZ 对象,比如 ø = Object.create(null),以保护全局对象。

三、优先级以及判断方法

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()

 

!!!!ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这 其实和 ES6 之前代码中的 self = this 机制一样。

posted @ 2019-03-11 14:26  准备养老的少女  阅读(95)  评论(0编辑  收藏  举报