JavaScript中的this指向
1.函数在调用时,JavaScript会默认给this绑定一个值;
2.this的绑定和定义的位置(编写的位置)没有关系;
3.this的绑定和调用方式以及调用的位置有关系;
4.this是在运行时被绑定的;
this的绑定规则
规则一:默认绑定
在函数独立调用时使用默认绑定,可以理解为函数没有被绑定到某个对象上
常见的默认绑定
// 函数独立调用
function foo{
console.log(`this指向${this}`)
}
foo()//this指向的是window
// 函数在对象中但是是独立调用
const obj = {
fn:function(){
console.log(`this指向${this}`)
}
}
const bar = obj.fn
bar()//this指向window
// 函数的参数默认调用
function baz(fn){
fn()
}
baz(obj.fn)//this指向window
规则二:隐式绑定
◼另外一种比较常见的调用方式是通过某个对象进行调用的:
也就是它的调用位置中,是通过某个对象发起的函数调用。
◼ 隐式绑定有一个前提条件:
必须在调用的对象内部有一个对函数的引用(比如一个属性);const obj={fn:function(){console.log(this)}}
如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
正是通过这个引用,间接的将this绑定到了这个对象上;
function foo(){
console.log("this指向",this)
}
const obj = {
bar : foo
}
obj.bar()//this指向obj
规则三 new绑定
◼ JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
◼ 使用new关键字来调用函数是,会执行如下的操作:
1.创建一个全新的对象:const obj = {}
2.将这个空对象赋值给this:this = obj
3.将构造函数的显式原型赋值给这个对象的隐式原型:obj.__proto__ =Person.prototype
4.执行构造函数的代码
5.如果没有返回一个非空对象那么则返回这个对象:return obj
```JavaScript
function foo(name){
console.log("foo函数",this)
this.name = name
console.log(name)
}
const bar = new foo("bar")//name值为bar
const baz = new foo("baz")//name值为baz
```
规则四 显式绑定 apply()方法,call()方法,bind()方法
◼ 隐式绑定有一个前提条件:
必须在调用的对象内部有一个对函数的引用(比如一个属性);const obj={fn:function(){console.log(this)}}
如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
正是通过这个引用,间接的将this绑定到了这个对象上;
◼ 如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上这时候可以用到显式绑定
◼ JavaScript所有的函数都可以使用call和apply方法。
apply()和call方法
func.apply(thisArg,[argsArray])
func.call(thisArg,arg1,arg2,.....)
第一个参数是相同的,要求传入一个对象;
✓ 这个对象的作用是什么呢?就是给this准备的。
✓ 在调用这个函数时,会将this绑定到这个传入的对象上。
后面的参数,apply为数组,call为参数列表;
``` JavaScript
function foo(name,age,height){
console.log("foo函数被调用:",this)
console.log("打印参数:",name,age,height)
}
// ()调用
foo("hdc","18","1.88")
// apply
// 第一个参数绑定this
// 第二个参数传入额外的实参,以数组形式
foo.apply("apply",["kobe","30","1.98"])
// call
// 第一个参数绑定this
// 第二个参数传入额外的实参,以数组形式
foo.call("call","james","35","2.01")
```
bind()方法绑定this
◼ 如果我们希望一个函数总是显示的绑定到一个对象上可以使用bind()方法绑定this
使用bind方法,bind() 方法创建一个新的绑定函数(bound function,BF);
绑定函数是一个exotic function object(怪异函数对象,ECMAScript 2015 中的术语)
在bind() 被调用时,这个新函数的this 被指定为bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
```JavaScript
function foo(name,age,height,address){
console.log("foo:",this)
console.log("参数:",name,age,height,address)
}
var obj = {name:"hdc"}
// 调用foo总是绑定到obj对象身上,并且不希望obj对象身上存在foo函数
var bar = foo.bind(obj,"kobe",18,1.88)
bar("james")//this指向obj
//参数: kobe 18 1.88 james
```
this绑定的优先级
new > bind > apply/call > 隐式绑定 > 默认绑定
◼ 1.默认规则的优先级最低
毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
◼ 2.显示绑定优先级高于隐式绑定
obj.foo.apply("abc")
◼ 3.new绑定优先级高于隐式绑定
new obj.foo()
◼ 4.new绑定优先级高于bind
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
new绑定可以和bind一起使用,new绑定优先级更高
绑定之外的情况
this规则之外– 忽略显示绑定
如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则:
function foo(){
console.log("this指向",this)
}
foo.apply(undefined)//this指向window
this规则之外- 间接函数引用
情况二:创建一个函数的间接引用,这种情况使用默认绑定规则。
赋值(obj2.foo = obj1.foo)的结果是foo函数;
foo函数被直接调用,那么是默认绑定;
var obj1 = {
name :"obj1",
foo : function(){
console.log("foo:",this)
}
}
var obj2 = {
name:"obj2"
};
obj2.foo = obj1.foo
obj2.foo();//this指向obj2
(obj2.foo = obj1.foo)()//this指向window
this规则之外– ES6箭头函数
◼ 箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this。
◼ 我们来看一个模拟网络请求的案例:
这里我使用setTimeout来模拟网络请求,请求到数据后如何可以存放到data中呢?
我们需要拿到obj对象,设置names;
但是直接拿到的this是window,我们需要在外层定义:var _this = this
在setTimeout的回调函数中使用_this就代表了obj对象
function request(url,callbackFn){
var result = ["aaa","bbb","bbb"]
setTimeout(function(){
callback(result)
},2000)
}
const obj={
names:["aaa111"],
network:function(){
//不使用箭头函数
const _this = this
request("/hhh",function(res){
_this.names = _this.names.concat(res)
})
// 使用箭头函数
request("/xxx",res=>{this.names = this.names.concat(res)})
}
}
obj.network()
console.log(obj.names)//(4) ['aaa111', 'abc', 'cba', 'nba']