this作用域

每个函数的this是在调用的时候被绑定的,完全取决于函数的调用位置. 调用位置就是函数在代码中被调用的位置,而不是声明的位置.
通常来说,寻找调用位置就是寻找”函数被调用的位置”,但是做起来并没有这么简单,因为某些编程模式可能会隐藏真正的调用位置.
最重要的是分析调用栈(就是为了到达当前执行位置所调用的所有函数)。我们关心的调用位置就在当前执行的函数的前一个调用中.
如果调用站中只有当前执行的函数,则当前调用位置是全局作用域.

先找到调用位置,然后判断需要应用this绑定四条规则中的哪一条

this绑定四条规则:
1.默认绑定
2.隐式绑定
3.显示绑定
4.new绑定


默认绑定——无法应用其他规则时的默认规则
最常用的函数调用类型:独立函数调用
思考以下代码

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

为什么this.a被解析成了全局变量a?因为在本例中,函数调用时应用了this的默认绑定,因此this指向全局对象.
我们怎么知道这里应用了默认绑定呢?通过分析调用位置来看,foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则.
如果使用严格模式(strict mode),则不能将全局对象用于默认绑定,因此this会绑定到undefined

function foo() {
    "use strict";
    console.log(this.a);
}
var a = 2;
foo(); //TypeError:this is undefined

隐式绑定
调用位置是否有上下文?或者说是否被某个对象拥有或者包含?调用栈是否存在‘到达当前执行位置所调用的上一函数’?
思考下列代码

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 obj2 = {
    a: 42,
    foo: foo,
};
var obj1 = {
    a: 3,
    obj2: obj2,
};
obj1.obj2.foo(); // 42 

隐式丢失
一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也是就说他会应用默认绑定(没有了上下文对象,就不会应用隐式绑定规则)

function foo() {
    console.log(this.a);
}
function doFoo(fn) {
    // fn其实引用的是foo
    fn(); 
}
var obj = {
    a: 2,
    foo: foo,
};
var a = "oops, global";  // a是全局对象的属性
doFoo(obj.foo); // "oops, global"

参数传递就是一种隐式赋值,foo()的调用位置是 fn(),fn()的调用位置是doFun() ,fn只是一个foo引用,所以foo没有上下文对象,会应用默认绑定.

回调函数丢失this绑定也是非常常见的。调用回调函数的函数可能会修改this,无论哪种情况,this的改变都是意想不到的,实际上你无法控制回调函数的指向方式,因此就没有办法控制调用位置以得到期望的绑定.


显式绑定
如果我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,该怎么做呢?
JavaScript所有函数都有call和apply方法.这两个方法能够给在调用函数时指定this绑定的对象.
因为这种方式可以直接指定this的绑定对象,因此我们称之为显式绑定.
思考下列代码

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

在调用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  
// 我觉得这是当然啦,毕竟是将bar中的this绑定到window上,而不是将foo中的this绑定到window上

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

Q: 如果不用call的话也可以调用foo()啊,使用call的原因是为了绑定this吗?如果在this不重要时,就可以不使用这种方式来调用函数?


new绑定
在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来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this上。


这四条规则的优先级?
new绑定>显式绑定>隐式绑定>默认绑定

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

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

在JavaScript中创建一个空对象最简单的方法都是Object.create(null). Object.create(null)和{}很像,但是并不会创建Object.prototype这个委托,所以它比{}更空

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值