[TS基础]对象,类,属性

本文详细介绍了 TypeScript 中类(class)的概念及其使用方法,包括属性与方法、构造器、继承、super 关键字等内容,并进一步探讨了抽象类与抽象方法、接口、属性封装及泛型的应用。

一.类(class)

  • 类可以理解为对象的模型,通过类来创建对象

1.属性与方法

  • 一个类主要包含两个部分,属性与方法
// 使用class关键字定义一个类
/**
 * 类主要包含两部分:
 * 1.属性
 * 2.方法
 */
class Person {
    // 静态属性,从属于类  可以直接通过类来访问
    static id: number = 9527;
    // 示例属性,从属于对象  需要通过类的实例对象去访问
    name: string = "iFinder";
    age: number = 24;
    // 只读属性, readonly开头,不能更改只能读取
    readonly country:string = "china";

    // 方法
    // 同理如果方法用static修饰,该方法便可以通过类去调用
    sayGood() {
        console.log("Good !");
    }
}

const per = new Person();
per.sayGood();
console.log(per.name);
console.log(per.age);
// 静态属性
console.log(Person.id);

2.构造器

  • 创建多个对象的时候,我们希望在创建对象时就赋予它不同的属性,这时就可以通过构造器来实现这样的需求
class Dog {
    name: string
    age: number
    /**
     * 构造器,在创建多个对象的时候,创建对象时调用的函数
     */
    constructor(name: string, age: number) {
        // this表示的就是当前的示例,可以通过他向新建的对象中添加属性
        this.name = name
        this.age = age
    }
    bark() {
        alert('汪 !')
    }
}

class Cat {
    // ts也可以将属性快速的定义在构造器当中, 简化了属性的写法
    constructor(public name:string, public age:number) {

    }
}

const dog1 = new Dog('dog1', 4)
const dog2 = new Dog('dog2', 5)
const dog3 = new Dog('dog3', 6)
console.log(dog1)
console.log(dog2)
console.log(dog3)

3.继承

  • 可以通过继承,将多个类共有的属性与方法抽象出来,作为公共的父类.这样可以大大提高代码的利用率
  • 子类直接继承父类,可以直接拥有父类的属性与方法
(function(){
    // 创建一个表示动物的类
    // 通过继承,可以把多个类共有得属性与方法抽象成公共得父类,这样就提高了代码得复用性
    class Animal {
        name: string 
        age: number
        constructor(name: string, age: number){
            this.name = name
            this.age = age
        }
        sayHello() {
            console.log(`${this.name} 在 叫 !`)
        }
    }

    // 让Dog类继承自动物类
    // 这时Animal就是Dog的父类,子类将会拥有父类所有的方法与属性
    class Dog extends Animal {
        // 可以实现自己独有得方法
        run() {
            console.log(`${this.name} 在 跑!`)
        }
        // 可以重写父类的方法实现自己特有的功能
        // 子类的方法会覆盖父类的方法
        sayHello() {
            console.log(`${this.name} 在 汪汪汪 !`)
        }
    }
    class Cat extends Animal {
        sayHello() {
            console.log(`${this.name} 在 喵喵喵 !`)
        }
    }
    const dog = new Dog('小黑狗', 4)
    const cat = new Cat('小花猫', 6)
    dog.sayHello()
    cat.sayHello()
})()

4.super关键字

  • 子类继承父类之后,如果想调用父类中的属性或者方法,可以通过super关键字
  • 子类如果重新实现了构造器,那么需要在自己的构造器中通过super调用父类的构造器
(()=>{
    class Animal {
        name: string
        constructor(name:string) {
            this.name = name
        }
        sayHello() {
            console.log("动物叫")
        }
    }

    class Dog extends Animal {
        age: number
        // 如果在子类中重新实现了构造器,那么需要通过super调用父类的构造器
        constructor(name:string, age:number) {
            super(name)
            // 子类新添加的属性需要自己实现
            this.age = age
        }
        sayHello() {
            // 在类的方法中,super表示当前类的父类
            // 这里可以理解为直接调用了父类的sayHello方法
            super.sayHello()
        }
    }

    const dog = new Dog('小黑', 8)
    dog.sayHello()
})()

二.抽象类与抽象方法

  • 当你定义了一个公共父类,并且只想被继承,不想拥有具体实例的时候就可以通过abstract修饰,使之成为抽象类
  • 抽象类中你想定义子类必须实现的某些方法,可以定义抽象方法
(()=>{
    /**
     * 以abstract开头的类是抽象类
     * 只能被继承,不能直接实例化
     * 
     * 抽象类中可以添加抽象方法(规定一个必须实现的方法,让不同的类去实现)
     */
    abstract class Animal {
        name: string
        constructor(name:string) {
            this.name = name
        }

        // 不定义具体的实现,由子类去实现具体的方法体
        // 抽象方法只能被定义在抽象类中
        abstract sayHello(): void
    }

    class Dog extends Animal {
        // 抽象方法会强制子类去添加具体的实现
        sayHello(): void {
            console.log(`${this.name} 在 汪汪汪 !`)
        }
    }
    const dog = new Dog('小黑')
    dog.sayHello()
})()

三.接口

(()=>{
    // type关键字描述对象的类型
    type myType = {
        name: string,
        age: number
    }

    /**
     * 接口用来定义一个类的结构
     * 可以定义一个类型中应该包含哪些方法
     */
    interface myInterFace {
        name: string
        age: number
    }

    // 接口可以重复声明,但是在作为类型使用的时候属性是叠加的
    interface myInterFace {
        gender: string
    }

    /**
    * 接口可以在定义类的时候限制类的结构
    * 接口中的所有属性都不能有实际的值
    * 接口值定义对象的结构,不考虑实际的值
    */
    interface inter{
        name: string
        sayHello(): void
    }

    // 定义类的时候可以让这个类实现这个接口
    class MyClass implements inter {
        name: string = "iFinder"
        sayHello(): void {
            //...
        }
    }
})()

四.属性的封装

  • TS的类中的属性,如果不加以修饰的话就是任何人都能访问与修改的,这样就造成的属性数据的不安全
  • 通过封装,可以将属性一定程度上的保护起来
  • 封装后只提供给调用方相应的方法,这样设置与获取数据的方法就是开发者可控的,提高了数据的安全性
(()=>{
    // 定义一个人类
    class Person {
        /**
         * 有的时候我们不希望对象中的属性被随意的修改,这时就需要将属性封装起来
         * 这时候可以通过属性修饰符来对属性进行封装
         * 
         * public: (默认)修饰的属性可以再任意位置被访问
         * private: 私有属性,只能在类的内部进行访问与修改
         *   --这时通过在类中添加方法,让外部通过这个方法访问这个属性
         *   --这时修改与访问的方法都是开发者提供的,这样这个属性的访问就变得可控了
         * protected: 当前类和子类能都访问与修改,外部不能访问与修改
         */
        private _name:string
        private _age:number

        constructor(name:string, age:number) {
            this._name = name
            this._age = age
        }

        // 给外部的类提供一个访问私有属性的方法
        getName():string {
            return this._name;
        }
        // set的时候限制name的类型
        setName(name:string) {
            this._name = name;
        }
        // set的时候限制年龄不会为负值
        setAge(age:number) {
            if(age >= 1) {
                this._age = age;
            }
        }

        // TS中独有的getter和setter方法
        get age():number {
            return this._age >= 1 ? this._age : 1;
        }
        set age(age:number) {
            if(age >= 1) {
                this._age = age;
            }
        }
    }

    // TS独有的写法可以直接用类似属性的方法来进行设置
    const p = new Person("iFinder", 25);
    console.log(p.age);  // 这时会调用get age方法获取属性
    p.age = -1;  // 这时会调用set age方法设置属性
    console.log(p.age);  // 方法中有判断,此时的打印依旧是25
})()

五.泛型

  • 定义函数或者声明一个类的时候如果暂时不能确定具体的类型,但是又想保持某种类型的统一,不想通过any破环TS的类型检查机制,就可以通过指定泛型的方式来达到这个要求
(()=>{
    /**
     * 定义函数或者类的时候,如果遇到类型不明确的,可以使用泛型
     *   <V>定义了一个叫V的泛型
     */
    function fn<V>(a: V): V {
        return a;
    }

    // 直接调用具有泛型的函数,这时泛型的类型就是number
    let a:number = fn(10)
    // 可以直接指定泛型的类型
    let s:string = fn<string>('iFinder')

    //可以指定多个泛型
    function fn1<K, T>(a: T, b: K): T {
        return a;
    }

    // 定义的时候就指定泛型的类型
    interface Inter{
        length:number
        getLength(): number
    }
    abstract class Animal {
        info:string;
        constructor(private _name:string, private _age:number) {
            this.info = `name: ${_name}, age: ${_age}`
        }
        get name(){
            return this._name
        }
        set age(age:number) {
            this._age = age
        }
    }

    function fn2<T extends Inter, K extends Animal>(a: T, b: K): K {
        return b
    }

    // 通过泛型指定类中的某种类型
    class MyClass<T>{
        constructor(public name:T) {}
    }
    // 创建类的时候确定泛型类型
    let my = new MyClass<string>("iFinder")
})()

笔记说明

笔记markdown文件+项目源码
B站对应视频

<think>好的,用户想知道在TypeScript中如何判断对象是否存在某个属性。我需要结合之前的引用内容来给出正确的解答。首先,用户的问题涉及到属性检查,可能需要用到型守卫或者特定的方法。 我记得引用[3]提到过keyof型运算符,这可能和检查属性有关。另外,引用[2]中使用了getter和setter,可能涉及属性访问。不过用户的问题更基础,是如何判断一个对象是否有某个属性。 在JavaScript中,通常使用in操作符或者hasOwnProperty方法。但在TypeScript中,型系统需要更严格的检查。例如,如果直接使用in,TypeScript可能会报错,因为对象型可能没有声明该属性。 例如,假设有一个对象obj: { a: number },检查'name' in obj会提示错误,因为型中不存在该属性。这时候需要使用型断言或者更宽泛的型声明,如将obj声明为any或Record<string, any>,但这可能不够安全。 另一种方法是使用型断言和型保护。比如,在条件判断中使用key in obj,然后TypeScript会缩小型范围,允许访问该属性。例如: if ('key' in obj) { // 这里可以安全访问obj.key } 引用[5]中的型收窄例子,比如使用typeof和型判断,可能与此相关。此外,型谓词函数可能也是解决方案的一部分,如自定义一个型守卫函数,使用keyof来确保属性存在。 还需要考虑可选属性和联合型的情况。比如,当对象型是联合型时,不同的分支可能有不同的属性,这时候使用in操作符可以帮助TypeScript正确收窄型。 总结可能的解决方案:1. 使用in操作符进行运行时检查,并让TypeScript进行型收窄。2. 使用型守卫函数,结合keyof进行型检查。3. 处理对象可能为null或undefined的情况,使用可选链操作符。4. 使用型断言,但需谨慎。 另外,需要提到的是,如果对象型是unknown或any,可能需要先进行型检查,再使用属性判断。例如,先判断是否是对象,再检查属性是否存在。 最后,结合引用中的例子,比如引用[2]中的Employee,可以举一个具体的例子说明如何检查属性是否存在。同时,引用[3]中的keyof可能用于更安全的属性访问。</think>在 TypeScript 中判断对象是否存在某个属性,可以通过以下方法实现: --- ### 方法 1:使用 `in` 操作符 `in` 操作符可检查属性是否存在于对象或其原型链中,并触发 TypeScript型收窄: ```typescript interface User { name?: string; age: number; } function checkProperty(obj: User, key: string) { if (key in obj) { // 此处 TypeScript 知道 key 是 'name' 或 'age' console.log(obj[key as keyof User]); // 需配合型断言 } } ``` --- ### 方法 2:使用型谓词函数 通过自定义型守卫明确属性存在性: ```typescript function hasProperty<T>(obj: T, key: PropertyKey): key is keyof T { return key in obj; } const obj = { id: 1, value: "test" }; if (hasProperty(obj, "value")) { console.log(obj.value); // 型安全访问 } ``` --- ### 方法 3:联合型场景的收窄 当对象型为联合型时,`in` 操作符可帮助 TypeScript 自动缩小型范围: ```typescript type Square = { size: number }; type Circle = { radius: number }; function getArea(shape: Square | Circle) { if ("size" in shape) { return shape.size ** 2; // 型收窄为 Square } else { return Math.PI * shape.radius ** 2; // 型收窄为 Circle } } ``` --- ### 方法 4:处理未知对象型 对于 `unknown` 型的对象,需先验证对象型再检查属性: ```typescript function safeGet(obj: unknown, key: string) { if (typeof obj === "object" && obj !== null && key in obj) { return (obj as Record<string, unknown>)[key]; } return undefined; } ``` --- ### 注意事项 1. **`hasOwnProperty` 的局限性**: `obj.hasOwnProperty(key)` 只能检查对象自身的属性(不包含原型链),但需配合型断言: ```typescript if (Object.prototype.hasOwnProperty.call(obj, key)) { /*...*/ } ``` 2. **可选链操作符 `?.`**: 用于安全访问可能不存在的属性: ```typescript const value = obj?.property?.nestedProp; // 自动处理 null/undefined ``` ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值