- 每个函数的this是在调用时被绑定的,完全取决于函数的调用位置
this绑定的规则
默认绑定,独立函数调用
函数默认绑定window
var a = 10
function foo () {
var a = 20
console.log(a) // 20
console.log(this.a) // 10
function boo () {
console.log(this.a) // 10
}
boo()
}
foo()
隐式绑定
函数调用的位置是否有上下文对象,或者说是否被某个对象拥有或者包含,见下代码
function foo () {
console.log(this.a)
}
var name = 'hui'
var obj = {
name: 'zhan'
foo: foo
}
obj.foo() // 打印'zhan',由于foo调用的位置,foo是被obj这个对象所调用,也可以是被obj所包含,所以this指向就指向obj这个调用上下文
var bar = obj.foo
bar() // 打印'hui', 此时foo函数的引用赋值给bar变量,就相当于window.bar(),所以此时this指向window
setTimeout(foo, 0) // 打印'hui', foo调用的环境是window
硬绑定
call、apply、bind
bind
给函数绑定了this之后,就无法改变bind返回的函数的this指向function foo (...ary) { this.a = ary.pop() // 取最后一个参数 } var bar = { a: 1 } var m = { a: 10 } var fn = foo.bind(bar, 2) // fn函数的this已经指向bar,无法在改了 m.fn = fn m.fn() // m中a依旧不变 fn.call(m, 20) // m中a依旧不变, fn内部的this无法改变
- API调用上下文,例如
forEach
,提供一个可选参数,指定回调函数this的指向
let obj = {
name: 'zh'
}
[1,2,3].forEach(function(p) {
console.log(this.name) // this指向obj
},obj)
new绑定
实际上并不存在所谓的“构造函数”,只是用
new
对函数的“构造调用”。 下面首先了解下用new调用一个函数的过程
- 创建一个对象
- 将该对象的__proto__指向函数的prototype
- 将函数的this指向该对象,并且执行该函数
- 如果函数的返回值是引用类型数据且非
null
,则new
调用的结果就是该引用类型数据,如果是非引用类型数据或者是null
,则new
调用的结果就是该对象
在上述的第三步中,改变了函数this的指向
上面4种this绑定的优先级如何呢
- 默认绑定优先级最低
- new绑定比隐式绑定优先级高, 见下面代码
function foo (a) {
this.a = a
}
var bar = {
a: 1,
foo: foo
}
var obj = new bar.foo(2)
console.log(obj.a) // 2
console.log(bar.a) // 1
- new绑定比硬绑定优先级高,但是new 无法和call、apply一起使用, 可以通过bind测试下
function foo (...ary) {
this.a = ary.pop() // 取最后一个参数
}
var bar = {
a: 1
}
var m = {
a: 10
}
var fn = foo.bind(bar, 2)
m.fn = fn
m.fn()
var obj = new fn(3)
console.log(obj.a) // 3
console.log(bar.a) // 1
之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化时就可以只传入其余的参数
- 硬绑定比隐式绑定高
绑定的例外
- 硬绑定
apply、call、bind
将this
指向null、undefined
,则函数内部的this指向就按照默认的方式指定
软绑定
上面讲了
bind
执行后,返回的函数的this就无法改变,有没有一种可以软绑定呢,使返回的函数也是可以被重新改变其this指向,下面见代码:
if(!Function.prototype.sofeBind) {
Function.prototype.sofeBind = function (obj, ...ary){
var fn = this
var bindFn = function (...params) {
var that = this === window ? obj : this // 如果返回函数this绑定到window上,则改变this指向obj,否则则不会修改this指向
console.log(that)
return fn.apply(that, [...ary, ...params])
}
return bindFn
}
}
// 见使用
var bar = {
a: 1
}
var mak = {
a: 2
}
function foo(a) {
this.a = a
}
var fn = foo.sofeBind(bar)
fn(3)
console.log(bar.a) // 3
mak.foo = fn
mak.foo(4) // **看这里,这里改变fn的this的指向,不像bind无法改变返回函数的this的指向**
console.log(mak.a) // 4