JS函数的this指向
- this在全局作用于下指向什么?
- 这个问题非常容易回答,在浏览器中测试就是指向window
- 但是,开发中很少直接在全局作用于下去使用this,通常都是在函数中使用。
- 所有的函数在被调用时,都会创建一个执行上下文:
- 这个上下文中记录着函数的调用栈、AO对象等;
- this也是其中的一条记录;
this绑定
函数在调用时,JavaScript会默认给this绑定一个值;
this的绑定和定义的位置(编写的位置)没有关系;
this的绑定和调用方式以及调用的位置有关系;
this是在运行时被绑定的;
一、四种绑定规则
1. 默认绑定
- 什么情况下使用默认绑定呢?独立函数调用。
- 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
- 我们通过几个案例来看一下,常见的默认绑定
- 案例一
function foo1(){
console.log(this);
}
function foo2(){
console.log(this);
foo1()
}
function foo3(){
console.log(this);
foo2()
}
foo3()
- 输出结果
- 案例二
var obj = {
name:'nishizhu',
foo:function(){
console.log(this);
}
}
var bar = obj.foo
bar()
-
输出结果
-
案例三
function foo(){
console.log(this)
}
var obj = {
name:'nishizhu',
foo:foo
}
var bar = obj.foo
bar()
- 输出结果
闭包的函数的this都指向window(x)
2.隐式绑定:object.fn()
object对象会被js引擎绑定到fn函数中的this里面
- 比较常见的调用方式是通过某个对象进行调用的:
- 也就是它的调用位置中,是通过某个对象发起的函数调用。
- 我们通过几个案例来看一下,常见的默认绑定
- 案例一
var obj1 = {
name:obj1,
foo1:function(){
console.log(this);
}
}
var obj2 = {
name:obj2,
bar:obj1.foo1
}
obj2.bar()
3.显式绑定
- 隐式绑定有一个前提条件:
- 必须在调用的对象内部有一个对函数的引用(比如一个属性);
- 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
- 正是通过这个引用,间接的将this绑定到了这个对象上;
- 如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
- JavaScript所有的函数都可以使用call和apply方法(这个和Prototype有关)。
- 它们两个的区别这里不再展开;
- 其实非常简单,第一个参数是相同的,后面的参数,apply为数组,call为参数列表;
- 这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给this准备的。
- 在调用这个函数时,会将this绑定到这个传入的对象上。
- JavaScript所有的函数都可以使用call和apply方法(这个和Prototype有关)。
- 因为上面的过程,我们明确的绑定了this指向的对象,所以称之为 显示绑定
call、apply、bind
- call/apply是可以指定this的绑定对象
function foo(){
console.log(this)
}
var obj3 = {
name:'我是obj3'
}
var obj4 = {
name:'我是obj4'
}
foo.call(obj3)
foo.apply(obj4)
输出结果
- call和apply的区别
call是直接传参(逗号分开),apply是数组传参
- 示例
function foo(num1,num2,num3){
console.log(num1+num2+num3,this)
}
foo.call("call",1,2,3)
foo.apply("apply",[1,2,3])
- 输出结果
- bind
如果要重复改变foo的this指向,比如说这样
function foo(){
console.log(this);
}
foo.call('aaa')
foo.call('aaa')
foo.call('aaa')
那么此时可以用bind来简化代码
function foo(){
console.log(this);
}
var newFoo = foo.bind('aaa')
newFoo()
newFoo()
newFoo()
输出结果
这里还涉及了一个优先级的问题,就是默认绑定和显示绑定冲突时,优先显示绑定。
4.new绑定
- JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
- 使用new关键字来调用函数是,会执行如下的操作:
- 创建一个全新的对象;
- 这个新对象会被执行prototype连接;
- 这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
- 如果函数没有返回其他对象,表达式会返回这个新对象
function Person(name,age){
this.name = name
this.age = age
}
var p1 = new Person('xsx',12)
console.log(p1.name,p1.age);
var p2 = new Person('yjf',99)
console.log(p2.name,p2.age);
输出结果
5.一些函数的this分析
-
setTimeout
- 示例
setTimeout(function(){ console.log(this) },1000)- 输出结果
-
数组的forEach/map/filter/find
-
默认是指向window的
- 示例
var arr = ['a','b','c'] arr.forEach(function(item){ console.log(item,this); })- 输出结果
-
也可以改变指向,比如说这里指向aaa
- 示例
var arr = ['a','b','c'] arr.forEach(function(item){ console.log(item,this); },'aaa')- 输出结果
-
-
div的点击
输出true
二、规则优先级
-
默认规则的优先级最低
- 毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
-
显示绑定优先级高于隐式绑定
-
代码测试
var obj = { name:'hello', foo:function(){ console.log(this); } } obj.foo.apply('abc') -
运行结果
-
-
new绑定优先级高于隐式绑定
-
代码测试
var obj = { name:'hello', foo:function(){ console.log(this); } } var f = new obj.foo() -
运行结果
-
-
new绑定优先级高于bind
-
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
-
new绑定可以和bind一起使用,new绑定优先级更高
-
代码测试
function foo (){ console.log(this); } var bar = foo.bind('abc') var f2 = new bar() -
运行结果
-
-
特殊绑定——忽略显示绑定
-
apply/call/bind:当传入null/undefined时,自动将this绑定成全局对象
-
代码测试
function foo (){ console.log(this); } foo.apply('abc') foo.apply(null) -
运行截图
-
-
this规则之外 - 间接函数引用
四、箭头函数的this
- 箭头函数不会绑定this、arguments属性;
- 箭头函数不能作为构造函数来使用(不能和new一起来使用,会抛出错误)
- 箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this