目录
- 调用位置
- 默认绑定
- 隐式绑定
- 隐式丢失
- 显式绑定
- call
- apply
- bind
- new绑定
- 装箱
- 绑定优先级
- this规则之外
- 忽略显式绑定
- 间接函数引用
- 箭头函数
调用位置
从字面意思上来理解,this
似乎是指向自己的
然而在JavaScript
中,this
并不是绑定到自身
的
可以看这一个例子
function foo(num) {console.log("num:" + num)this.count++}foo.count = 0for (var i = 0; i < 10; i++) {if (i > 5) {foo(i)}}console.log(foo.count)
结果
显然从字面意思来理解this
指向自身
是错误的认知
事实上this
并不是编写时绑定的,而是运行时绑定
的
我们判断this
会绑定到什么首先就要分析它的调用位置
function foo() {bar()//此时的调用栈为:全局--》foo--》barfunction bar() {baz()//此时的调用栈为:全局--》foo--》bar--》bazfunction 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.foobar()
可以看到this
绑定到了window
上,这就是隐式丢失
显式绑定
在上面的代码中我们可以看到隐式绑定
也会有可能出现隐式丢失
的现象,为了确保我们的this
能正确的绑定到我们想要的对象上可以使用显式绑定
显式绑定
可以通过传递参数
的形式来把this
绑定到参数
上
显示绑定有具体三种
方式
call
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
apply
方式与call
方法相似,但传递参数的方式不同
function foo(name, age) {console.log(this)console.log(name + " " + age)}var obj = {name: "obj",}foo.apply(obj, ["张三", 18])
结果
这里可以看到this
绑定到了obj
上了
注意:apply
方法也是立即执行的
bind
无论是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绑定
当我们使用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
结果
我们可以知道new绑定优先级高于显式绑定
this规则之外
在现实使用中,我们总有些语法
超出了规则之外
忽略显式绑定
当传入的参数是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()
结果