ArkTS要点笔记之this
序言
在其他OOP语言中,方法function func
通常以归属于某一个对象的形式存在,语言往往提供this self
之类的关键字用来在某一个方法代码中,用于指向当前方法所属的对象、类、结构体,如Objective-C中[self func];
Swift中的self.func()
。ArkTS(TypeScript扩展)中也不例外,但TS中的this
确有一些特殊,我认为从其他语言转来学习TS的人都应该把这个点当做重点对待。不然容易掉坑,或对于某一些特定使用规则不明就里。
基于以上,将用这篇文章分别从JS、TS、ArkTS场景举例说明this
的细节。
ArkTS、TS、JS关系
套娃,如图
TS和JS具体区别
ArkTS和TS扩展部分后续补充
关于this的要点(严格模式)
TS中this并不能像常规OOP语言中那样在编译期确定指向,需要动态绑定,对比常规语言比较反直觉
普通函数,谁调用函数(动态绑定),谁就是那个this,a.f(),在f中this===a
箭头函数,var obj = {}中直接定义箭头函数作为对象属性,this===undefined。
箭头函数,TS class类中定义箭头函数,通过对象调用,和赋值后调用this===函数所属obj。
箭头函数在ArtTS struct中,this指向不同于class(上一条)
箭头函数,嵌套在其他函数内部情况中,this===外层function或箭头函数的this。
见下方例子。
本文描述中,将obj.func()
称为obj.调用
,将var func = obj.func();func()
称为赋值后直接调用
。
举例说明
案例一(严格模式,JS对象)
var obj = {
myName:'myObj',
func: function(type) {
console.log(type + "func:")
console.log(this)
},
func2:function(type){
var nestArrow = ()=>{
console.log(type + "arrow2:")
console.log(this)
}
nestArrow()
},
arrow:(type)=>{
console.log(type + "arrow:")
console.log(this)
}
}
obj.func("obj.调用")// this === obj
var func = obj.func
func("赋值后调用")// this === undefined
obj.func2("obj.调用")// this === obj
var func2 = obj.func2
func2("赋值后调用")// this === undefined
obj.arrow("obj.调用")// this === undefined
var funcArrow = obj.arrow
funcArrow("赋值后调用")// this === undefined
分析
1、func
和func2
在obj
中用普通函数定义,当通过obj.func() obj.func2()
的形式调用时,它们的直接调用者是obj
,这种调用下,function
内部的this===obj
。而当使用var func = obj.func
进行重新赋值并调用func()
后,func
并没有显示调用者,所以内部的this
绑定为undefined
(严格模式下,非严格模式为window
)。
2、func2
内部的nestArrow
是箭头函数定义,箭头函数的this
是指向外层最近的函数的this
,因此,当通过obj.func2()
调用时,nestArrow
的this
指向外层func2
的this === obj
。而当使用var func2 = obj.func2
赋值后,func2
没有显示调用者(同1),this
绑定为undefined
(严格模式)
3、直接在对象obj
中定义箭头函数arrow
作为其属性,由于该函数并没有外层function
,根据箭头函数this
的绑定规则,无论是否通过obj.
进行调用,this===undefined
。
小结
普通函数
普通函数的this
会绑定为函数的直接调用者,如a.b.c.func()
中,func
的this===c
,而var f = a.b.c.func;f()
中,f()
没有没有直接调用者,严格模式下this===undefined
。
箭头函数
箭头函数内部的this
运行时绑定为其外层最近的函数的this
,当箭头函数直接作为obj属性时,this===undefined
。
案例二(严格模式,TS类)
class MyObj{
constructor(){}
type = "MyObj"
func(type:string){
console.log(type + "func:")
console.log(this)
}
func2(type:string){
var nestArrow = ()=>{
console.log(type + "func2:")
console.log(this)
}
nestArrow()
}
arrow = (type:string)=>{
console.log(type + "arrow:")
console.log(this)
}
arrow2 = (type:string)=>{
var nestArrow = ()=>{
console.log(type + "arrow2:")
console.log(this)
}
nestArrow()
}
arrow3 = (type:string)=>{
var nestFunc = function(){
console.log(type + "arrow3:")
console.log(this)
}
nestFunc()
}
}
var obj = new MyObj()
obj.func("obj.调用")// this === obj
var func = obj.func
func("赋值后调用")// this === undefined
obj.func2("obj.调用")// this === obj
var func2 = obj.func2
func2("赋值后调用")// this === undefined
obj.arrow("obj.调用")// this === obj
var arrow = obj.arrow
arrow("赋值后调用")// this === obj
obj.arrow2("obj.调用")// this === obj
var arrow2 = obj.arrow2
arrow2("赋值后调用")// this === obj
obj.arrow3("obj.调用")// this === undefined
var arrow3 = obj.arrow3
arrow3("赋值后调用")// this === undefined
小结
普通函数
普通函数的this
在类中表现和案例一对象中使用一致,需要根据调用者进行动态绑定。
箭头函数(作为类成员时和案例一不同)
箭头函数作为成员(非嵌套)的this
在类中表现不同于对象中使用,在对象中的箭头函数成员,无论哪种调用this===undefined
,而在类中时,无论通过obj调用还是赋值后直接调用,箭头函数的this
都指向所属对象obj。
嵌套函数
无论嵌套何种函数,依据其自身(箭头或普通)的this
绑定规则,嵌套的箭头函数this===上层函数this
,普通函数,严格模式下没有直接调用者,一般是undefined
。
案例三(ArkTS)
@Entry
@Component
struct EventCase {
@State
username: string = ""
@State
password: string = ""
// 登录方法
login1(){
console.log(this.username) // .onClick(this.login1)方式绑定,this===undefined
}
login2 = () => {
console.log(this.username) //两种.onClick绑定,this都绑定当前结构体
}
build() {
Row() {
Column({ space: 20 }) {
TextInput({ placeholder: '请输入用户名', text: $$this.username })
.height(40)
TextInput({ placeholder: '请输入密码', text: $$this.password })
.height(40)
.type(InputType.Password)
Button("登录")
.width('100%')
//.onClick(this.login1) 不能拿到this
//.onClick(() => {
//this.login1() 能拿到this===struct
//})
//.onClick(this.login2)
//.onClick(() => {
//this.login2()
//}) 以上两种onClick绑定,login2中都可以正确拿到this===当前struct
}
.padding({
left: 20,
right: 20
})
.width('100%')
}
.height('100%')
}
}
小结
普通函数
login1
方式定义的普通成员函数,依然需要考虑this动态绑定
问题。
.onClick(this.login1)
相当于赋值后传入进onClick
调用,没有直接调用者,this===undefined
。
.onClick(()=>{this.login1()})
情况下,由于外层包裹了箭头函数
,箭头函数中的this
会指向外层,最终this===struct
。
箭头函数
login2
方式定义的属性箭头函数,类似于案例二类中定义的方式,onClick
不管通过箭头函数包装后显式调用this.login2()
还是直接赋值传入this.login2
,this===struct
。
个人理解
JS中的this
之所以和其他常规语言不同,是源于this的动态绑定机制。这种机制下,结合call、apply、bind
方法,为JS语言提供了极大的灵活性,但事物总是有两面性,灵活到极致也意味着混乱。开发者不得不为了这样的机制去注意更多语法细节,才能编写安全的代码。
箭头函数的推出应该就是为了改善这个问题。但箭头函数的this并不是完全不变的,依然取决于外层函数的this。
当箭头函数为属性成员,外层为类或ArkTS的结构体时(案例二和三),无论直接调用还是赋值后调用,this===obj/struct。
日常进行鸿蒙开发中,我们要重点区分普通函数和箭头函数对this的使用。欢迎交流指正。
转载自:https://juejin.cn/post/7377026952786313253