JS中的this指向
在常见的编程语言中,几乎都有this这个关键字,但是JavaScript中的this和常见的面向对象语言中的this不太一样:
常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。
也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。
但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义。
在全局作用域下this指向window,但是在真正的开发中我们很少在全局作用域下去使用this,通常都是在函数中使用。
this到底指向什么?
我们定义一个函数,采用三种不同的方法对他进行调用,他产生了三种不同的结果。
// 定义一个函数
function foo(){
console.log(this)
}
// 方式一:直接调用
foo(); //window
// 方式二:将foo放到一个对象里,在调用
var obj = {
name:"aaa",
foo:foo
}
obj.foo() //obj对象
// 方式三:通过call/apply调用
foo.call("abc") //String{"abc"}对象
由此可见this的绑定和定义的位置没关系,和调用的位置有关系,this是在运行时被绑定的。
this的绑定规则
规则一:默认绑定
// 默认绑定: 独立函数调用
// 1.案例一:
function foo() {
console.log(this);
}
foo(); // window
// 2.案例二:
function foo1() {
console.log(this) // window
}
function foo2() {
console.log(this) // window
foo1()
}
function foo3() {
console.log(this) // window
foo2()
}
foo3()
// 3.案例三:
var obj = {
name: "why",
foo: function() {
console.log(this)
}
}
var bar = obj.foo
bar() // window
// 4.案例四:
function foo() {
console.log(this)
}
var obj = {
name: "why",
foo: foo
}
var bar = obj.foo
bar() // window
// 5.案例五:
function foo() {
function bar() {
console.log(this)
}
return bar
}
var fn = foo()
fn() // window
var obj = {
name: "why",
eating: fn
}
obj.eating() // obj
规则二:隐式绑定
// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数的中this里面
// 1.案例一:
var obj = {
name: "why",
foo: foo
}
obj.foo() // obj对象
// 2.案例二:
var obj = {
name: "why",
eating: function () {
console.log(this.name + "在吃东西");
},
running: function () {
console.log(obj.name + "在跑步");
},
};
obj.eating() //obj
obj.running() //obj
var fn = obj.eating
fn() // window
// 3.案例三:
var obj1 = {
name: "obj1",
foo: function () {
console.log(this);
},
};
var obj2 = {
name: "obj2",
bar: obj1.foo,
};
obj2.bar(); // obj2
规则三:显式绑定
显示绑定-apply-call
// 1.foo直接调用和call/apply调用的不同在于this绑定的不同
function foo() {
console.log("函数被调用了", this);
}
var obj = {
name: "obj"
}
call/apply都可以指定this的绑定对象
foo.call(obj) //obj
foo.apply(obj) //obj
foo.apply("aaaa") //aaaa
// 2.call和apply有什么区别?
function sum(num1, num2, num3) {
console.log(num1 + num2 + num3, this);
}
sum.call("call", 20, 30, 40); //String{'call'}
sum.apply("apply", [20, 30, 40]);//String{'allpy'}
// 总结
// call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定
// 两者第一个参数是相同的,都是绑定到某一个对象上,区别在于后面的参数,apply为数组,call为参数列表;
显示绑定-bind
//如果我们希望一个函数总是显示的绑定到一个对象上,那么我们就是可以使用bind绑定
function foo() {
console.log(this)
}
var newFoo = foo.bind("aaa")
newFoo()
newFoo()
newFoo()
newFoo()
newFoo()
newFoo() // String{'aaa'}
var bar = foo
console.log(bar === foo) // true
console.log(newFoo === foo) //false
call、apply、bind 总结
相同点:
- 都可以改变函数内部的this指向。
区别点:
- call 和 apply 会调用函数,并且改变函数内部this指向。
- call 和 apply 传递的参数不一样,call 传递参数arg1,arg2...形式 apply 必须数组形式[arg]
- bind 不会调用函数,可以改变函数内部this指向。
主要应用场景:
- call 经常做继承。
- apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值。
- bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。
规则四:new绑定
// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定
function Person(name, age) {
this.name = name
this.age = age
}
var p1 = new Person("why", 18)
console.log(p1.name, p1.age)
var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age)
规则优先级:
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
new>bind(显示绑定)>隐式绑定>默认绑定
this规则之外
如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则:
function foo(){
console.log(this)
}
var obj = {
name:"why"
}
foo.call(obj); // obj
foo.call(null); // window
foo.call(undefined); // window
var bar = foo.bind(null)
bar(); //window
另外一种情况,创建一个函数的 间接引用,这种情况使用默认绑定规则。
function foo(){
console.log(this)
}
var obj1 = {
name:"obj1",
foo:foo
};
var obj2 = {
name:"obj2"
}
obj1.foo(); //obj1
(obj2.foo = obj1.foo)() //window
箭头函数
在箭头函数中,不是根据函数的调用位置来决定this的指向,而是根据外层作用域来决定this的指向。
var obj = {
data: [],
getData: function() {
// 发送网络请求, 将结果放到上面data属性中
// 在箭头函数之前的解决方案
// var _this = this
// setTimeout(function() {
// var result = ["abc", "cba", "nba"]
// _this.data = result
// }, 2000);
// 箭头函数之后
setTimeout(() => {
var result = ["abc", "cba", "nba"]
this.data = result
}, 2000);
}
}
obj.getData()
面试题:摘自coderwhy大神
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)