TypeScript 面向对象中的属性和方法
属性和方法
接下来,我们学习面向对象中最基本的概念:属性和方法,在 TS 中是如何定义的?
class Cat { // 属性 name; // 方法 sayHi() { return `Meow, my name is ${this.name}`; } } let tom = new Cat(); tom.name = 'Tom'; console.log(tom.sayHi()); // Meow, my name is Tom 复制代码
修改下代码,我们就完成 TS 中对类的属性和方法的类型定义。
class Cat { // 属性 name: string; // 方法 sayHi(): string { return `Meow, my name is ${this.name}`; } } let tom = new Cat(); tom.name = 'Tom'; console.log(tom.sayHi()); // Meow, my name is Tom 复制代码
习题:关于类的属性和方法,下面用法正确的是?
// A class Animal { age = 10; getAge() { return Animal.age; } } new Animal().getAge(); // B class Animal { age = 10; getAge() { return this.age; } } new Animal().getAge(); // C class Animal { age = 10; getAge() { return this.age; } } new Animal().age; // D class Animal { age = 10; getAge() { return this.age; } } Animal.age; 复制代码
答案:BC
解析:
类的属性默认为实例属性,类的方法默认为实例方法。访问控制修饰符章节会详细介绍。
new Animal() 复制代码使用
new
关键字对类Animal
进行了实例化并返回实例化结果。
new Animal().getAge() 复制代码链式写法,可以看作是
(new Animal()).getAge()
的省略写法。使用new Animal()
得到类 Animal 的实例,接着实例执行getAge
方法。
Animal.age 复制代码当属性为静态属性(方法)时,可以直接使用类名调用该属性(方法)。
下图示意了静态属性和实例属性的区别,实例属性(方法)存在于实例对象中,每个实例对象都会单独拥有。静态属性(方法)存在于类中,所有实例对象共享静态属性(方法)。
我们开始分析四个选项:
A -
age
是类的实例属性,选项中Animal.age
是静态方法的调用方式。故错误。B - 选项中使用
this.age
是正确的。this
指向实例对象。C - 选项中
new Animal().age
调用是正确的。因为age
是类的实例属性。D - 基于
C
选项可以看出,此选项是错误的。
资料:值为箭头函数的属性
假设我们要设计一个 Person 类,代码如下
class Person { name: string age: number constructor(name: string, age: number) { this.name = name; this.age = age; } logName = function(name: string) { console.log('my name is ' + name) } getName = function() { this.logName(this.name) } } const p1: Person = new Person('Richard', 12); const { getName } = p1; // 通过对象的解构赋值,提取出 getName p1.getName(); // my name is Richard getName(); // TypeError: this.logName is not a function 复制代码
当我们将 getName 函数单独提取出来使用的时候,this 会指向 getName 运行时所在的作用域,这时候会因为找不到 logName 方法而报错。
一个可行的解决方案是用 bind 方法来为 getName 绑定作用域
class Person { name: string age: number constructor(name: string, age: number) { this.name = name; this.age = age; } logName = function(name: string) { console.log('my name is ' + name) } // 利用 bind 方法为 logName 绑定作用域 getName = function() { this.logName(this.name) }.bind(this) } const p1: Person = new Person('Richard', 12); const { getName } = p1; // 通过对象的解构赋值,提取出 getName p1.getName(); // my name is Richard getName(); // my name is Richard 复制代码
与 bind 方法相比,更简明的方法是利用 ES6 中提供的箭头函数。
class Person { name: string age: number constructor(name: string, age: number) { this.name = name; this.age = age; } logName=function (name: string) { console.log('my name is ' + name) } // 利用箭头函数为 logName 绑定作用域 getName = () =>{ this.logName(this.name) } } const p1: Person = new Person('Richard', 12); const { getName } = p1 // 通过对象的解构赋值,提取出 getName p1.getName(); // my name is Richard getName(); // my name is Richard 复制代码
资料:存取器
在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为
class User { constructor(name: string) { this.name = name; } get name() { return 'Evan'; } set name(value) { console.log('setter: ' + value); } } let a = new User('Peter'); // setter: Peter a.name = 'Richard'; // setter: Richard console.log(a.name); // Evan 复制代码
类的构造函数
在面向对象中,有一个特殊的方法,就是构造函数。构造函数就是在创建一个函数的时候,会被调用的函数。
接着上一个例子,我们希望创建这个 tom 的时候,就是初始化好它的姓名。
那如何去实现呢?
class Cat { // 属性 // name: string; // 这里可以定义好传入参数的类型 constructor(name: string) { this.name = name; } // 方法 sayHi(): string { return `Meow, my name is ${this.name}`; } } let tom = new Cat('Tom'); // tom.name = 'Tom'; console.log(tom.sayHi()); // Meow, my name is Tom 复制代码
资料:参数属性
通常我们定义一个类的时候会采用如下写法
class User { name: string; age: number; constructor() { //... } } 复制代码
实际上我们也可以在构造函数的参数定义时即声明和赋值属性
class User1 { constructor(public name: string = 'Richard', private age: number = 18) { //... } } 复制代码
习题:关于类的构造函数,下面描述正确的是?
A. 主要用于初始化类的成员变量属性。
B. 类的对象创建时自动调用执行。
C. 没有返回值。
答案:A B C
解析:
类的构造函数主要用于,创建类的时候初始化类的成员变量属性。构造函数会在类的对象创建时候自动调用。需要注意的是父类的构造函数需要手动调用,因为构造函数主要用于初始化操作,故没有返回值。
作者:追梦玩家
链接:https://juejin.cn/post/7031024288270385189