从字面意思上来理解,this
似乎是指向自己的
然而在JavaScript
中,this
并不是绑定到自身
的
可以看这一个例子
function foo(num) {
console.log("num:" + num)
this.count++
}
foo.count = 0
for (var i = 0; i < 10; i++) {
if (i > 5) {
foo(i)
}
}
console.log(foo.count)
显然从字面意思来理解this
指向自身
是错误的认知
事实上this
并不是编写时绑定的,而是运行时绑定
的
我们判断this
会绑定到什么首先就要分析它的调用位置
function foo() {
bar()//此时的调用栈为:全局--》foo--》bar
function bar() {
baz()//此时的调用栈为:全局--》foo--》bar--》baz
function baz() {
}
}
}
foo()//此时的调用栈为:全局--》foo
在寻找到它的调用位置
之后我们就需要判断这里的this
适用于以下四条规则
中的哪一条
当独立调用函数
时适用此条,也可以看成当其他三条规则都不适用时适用这条规则
独立函数调用
可以看成函数没有被绑定到某个对象上进行调用
例如下面这个例子
function foo() {
console.log(this)
}
foo()
结果
此时的this
绑定了全局对象window
上
值得注意的是,在严格模式
下,这里的this
会绑定到undefined
上
另一种比较常见的绑定方式为通过某个对象调用
function foo() {
console.log(this)
}
var obj = {
name: "obj",
foo: foo
}
obj.foo()
结果
此时的this
绑定到了obj
对象上了
被隐式绑定的this
有时也会应用默认绑定
规则
例如下面这段代码
function foo() {
console.log(this)
}
var obj = {
name: "obj",
foo: foo
}
var bar = obj.foo
bar()
可以看到this
绑定到了window
上,这就是隐式丢失
在上面的代码中我们可以看到隐式绑定
也会有可能出现隐式丢失
的现象,为了确保我们的this
能正确的绑定到我们想要的对象上可以使用显式绑定
显式绑定
可以通过传递参数
的形式来把this
绑定到参数
上
显示绑定有具体三种
方式
call
方法需要传入一个参数来作为this
的绑定对象,如果函数需要参数则在后面用逗号
隔开
function foo(name, age) {
console.log(this)
console.log(name + " " + age)
}
var obj = {
name: "obj",
}
foo.call(obj, "张三", 18)
结果
这里可以看到this
绑定到了obj
上了
注意:call
方法是立即执行
的
apply
方式与call
方法相似,但传递参数的方式不同
function foo(name, age) {
console.log(this)
console.log(name + " " + age)
}
var obj = {
name: "obj",
}
foo.apply(obj, ["张三", 18])
结果
这里可以看到this
绑定到了obj
上了
注意:apply
方法也是立即执行的
无论是apply
绑定或者是call
绑定,都不能完全解决this
的绑定丢失
问题
当将函数作为参数传递给其他函数之后,很难知道其他函数会怎么调用这个函数
函数的this指向此时也不受你控制
为了解决这个问题,我们可以使用bind
方法
function foo() {
console.log(this)
}
function bar(fn) {
fn.call(obj)
}
var obj = {
name: "obj",
}
bar(foo.bind(window))
结果
bind
方法不会立即执行函数,而是会返回
一个新函数
新函数的this
将会指向你所指定的对象
以后this
的指向也始终会是预期
的
当我们使用new
关键字时,会执行以下几件事情
空对象
this
绑定到这个空对象
function foo() {
console.log(this)
this.a = 2
}
var obj = {
name: "obj",
}
var bar = new foo()
console.log(bar.a)
可以看到此时的this
已经绑定到了foo
上
无论是call
还是apply
还是bind
,当我们传入一个原始值(如数字
,字符串
)时会发生什么呢
function foo() {
console.log(this)
this.a = 2
}
var obj = {
name: "obj",
}
foo.call(123)
foo.apply("123")
结果
得到的结论就是如果你传入了一个原始值
来当作this
的绑定对象,这个原始值
会被转换成它的对象形式
如new Number()
、new String()
等等
这通常被称为装箱
当一个函数
涉及了多条规则
,规则与规则之间则会按照自己的优先级
生效
毋庸置疑的是默认绑定
优先级最低
,是其他规则都不适用情况下的兜底条款
显示绑定
的优先级高于隐式绑定
function foo() {
console.log(this.name)
this.a = 2
}
var obj = {
name: "obj",
foo: foo
}
var obj2 = {
name: "obj2",
foo: foo
}
obj.foo.call(obj2)
结果
new
绑定比隐式绑定优先级高
function foo() {
this.a = 2
}
var obj = {
name: "obj",
a: 4,
foo: foo
}
var obj2 = new obj.foo()
console.log(obj2.a)
结果
new
绑定无法与call
/apply
方法一起使用,但我们可以通过与bind
方法比较来得到结果
function foo() {
console.log(this)
}
var obj = {
name: "obj",
}
var obj2 = foo.bind(obj)
new obj2
在现实使用中,我们总有些语法
超出了规则之外
当传入的参数是null
或者为undefined
时,这个显式绑定
会忽略
,使用默认绑定
function foo() {
console.log(this)
}
foo.call(null)
foo.call(undefined)
创建一个函数的间接引用
,这种情况适用默认规则
function foo() {
console.log(this)
}
var obj = {
foo: foo
}
var obj2 = {
name: "obj2"
};
(obj2.foo = obj.foo)()
在ES6
中提出了一种新函数,他的名字叫箭头函数
在箭头函数
中,其实并没有this
所以并不适用
上面提到的四条规则
箭头函数
中的this是由其外部作用域来决定的
当箭头函数中遇到this
时,箭头函数便会去它的外层作用域寻找
var obj = {
bar: function () {
var foo = () => {
console.log(this)
}
return foo
}
}
var baz = obj.bar()
baz()
结果