# 类

## 功能
类用于把一组字段和方法封装成同一种对象结构。字段保存实例状态，方法描述实例行为，方法内部使用 `this` 访问当前实例。

BT 的类语法保持 JS 风格，但构造实例使用 `类名::构造方法(...)`。最常用的构造方法名是 `new`，所以通常写成 `User::new(...)`。

## 语法

### 定义类
```text
class 类名 {
    字段名: 初始值
    字段名

    pub 公共字段: 初始值

    方法名(参数1, 参数2) {
        方法体
    }

    pub 公共方法名(参数) {
        方法体
    }

    new(参数) {
        初始化逻辑
        this
    }
}
```

### 创建实例
```text
对象 = 类名::new(参数)
对象 = 类名::其他公共构造方法(参数)
```

`Class::new()` 是 BT 当前的实例化写法，不是 `new Class()`。

## 成员
类成员分为字段和方法。

字段使用 `名称: 表达式` 声明：
```bt
class User {
    name: '未命名'
    age: 0
}
```

### 裸字段
字段也可以只写名称，不写 `:` 和初始值，这种写法叫裸字段。裸字段会提前在实例里占一个成员名，默认值是 `empty`。

```bt
class User {
    name

    new(name) {
        this.name = name
        this
    }

    pub get_name() {
        this.name
    }
}

user = User::new('Tom')
println user.get_name()
```

裸字段 `name` 和 `name: ''` 不一样：`name` 的默认值是 `empty`，`name: ''` 的默认值是空字符串。构造方法里通常用 `this.name = name` 覆盖默认值。

方法不需要写 `fn`，直接写 `方法名(参数) { ... }`：
```bt
class User {
    name: 'BT'

    pub get_name() {
        this.name
    }
}
```

方法和普通函数一样有返回值。没有显式 `return` 时，方法体最后一条语句的值就是返回值。

## this
`this` 表示当前正在操作的实例，只在类方法和构造方法里使用。

```bt
class Counter {
    value: 0

    pub inc() {
        this.value = this.value + 1
        this
    }

    pub get() {
        this.value
    }
}

counter = Counter::new()
counter.inc().inc()
println counter.get()
```

`this.value` 用来读取当前实例字段，`this.value = 1` 用来写入当前实例字段，`this.method()` 用来调用当前实例的其他方法。

访问私有成员时必须直接通过 `this.xxx`。不要把 `this` 先赋给别的变量再访问私有成员，因为权限判断只把直接来自 `this` 的访问视为类内部访问。

```bt
class User {
    password: ''

    new(password) {
        this.password = password
        this
    }

    check(password) {
        this.password == password
    }

    pub login(password) {
        this.check(password)
    }
}
```

## new
`new` 是最常用的构造方法名。创建实例时写 `类名::new(...)`。

```bt
class User {
    name: ''
    age: 0

    new(name, age) {
        this.name = name
        this.age = age
        this
    }

    pub info() {
        this.name + ':' + this.age
    }
}

user = User::new('Tom', 18)
println user.info()
```

如果类里没有定义 `new`，`Class::new()` 仍然会创建一个实例，并使用类字段里的默认值：

```bt
class Config {
    pub host: '127.0.0.1'
    pub port: 8080
}

config = Config::new()
println config.host
println config.port
```

构造方法返回规则需要特别注意：BT 方法默认返回最后一条语句的值。构造方法执行后，如果返回 `empty` 或 `null`，`Class::new()` 会返回新实例；如果返回其他值，就会返回那个值。

因此构造方法里最后一行建议写 `this`，明确返回当前实例：

```bt
class User {
    name: ''

    new(name) {
        this.name = name
        this
    }
}
```

不要只把赋值语句放在构造方法最后一行：

```bt
class User {
    name: ''

    new(name) {
        this.name = name
    }
}

user = User::new('Tom')
```

上面这个 `new` 的最后一条语句是赋值表达式，赋值表达式返回右侧的 `'Tom'`，所以 `User::new('Tom')` 返回的是字符串，不是实例。

## 多个构造入口
BT 的构造入口不固定为 `new`。任何公开方法都可以用 `类名::方法名(...)` 作为构造入口：运行时会先创建实例，再把 `this` 绑定到这个实例执行指定方法。

```bt
class User {
    name: ''
    role: 'user'

    new(name) {
        this.name = name
        this
    }

    pub admin(name) {
        this.name = name
        this.role = 'admin'
        this
    }

    pub label() {
        this.name + ':' + this.role
    }
}

tom = User::new('Tom')
root = User::admin('Root')

println tom.label()
println root.label()
```

除 `new` 以外，作为构造入口的方法需要是 `pub`，否则类外不能访问。

## 公共和私有
类成员默认是私有成员。只有加了 `pub` 的字段或方法，才能在类外部通过实例访问。

```bt
class Account {
    balance: 0

    new(balance) {
        this.balance = balance
        this
    }

    pub deposit(amount) {
        this.balance = this.balance + amount
        this
    }

    pub get_balance() {
        this.balance
    }

    can_withdraw(amount) {
        this.balance >= amount
    }

    pub withdraw(amount) {
        if this.can_withdraw(amount) {
            this.balance = this.balance - amount
            true
        } else {
            false
        }
    }
}

account = Account::new(100)
account.deposit(50)
println account.get_balance()
println account.withdraw(80)
println account.get_balance()
```

外部可以访问：
```bt
account.deposit(50)
account.get_balance()
```

外部不能访问：
```bt
account.balance
account.can_withdraw(10)
```

私有成员仍然可以在类内部通过 `this` 访问：
```bt
this.balance
this.can_withdraw(10)
```

BT 当前没有 `private` 关键字。默认就是私有，需要公开时才写 `pub`。

## 字段写入
公共字段可以在类外部读写：

```bt
class User {
    pub name: ''
}

user = User::new()
user.name = 'Tom'
println user.name
```

私有字段只能在类内部通过 `this` 写入：

```bt
class User {
    name: ''

    pub set_name(name) {
        this.name = name
        this
    }

    pub get_name() {
        this.name
    }
}
```

如果在 `this` 上新增一个类定义里没有声明过的字段，这个字段会作为私有字段保存；如果在外部实例上新增字段，这个字段会作为公共字段保存。

```bt
class User {
    pub set_token(token) {
        this.token = token
        this
    }
}

user = User::new()
user.set_token('abc')
user.nickname = 'Tom'
```

上面的 `token` 是类内部通过 `this` 新增的私有字段，`nickname` 是外部新增的公共字段。

## 链式调用
BT 支持链式调用。类方法如果要继续链式调用，返回 `this` 即可。

```bt
class Query {
    table: ''
    where_sql: ''
    limit_size: 0

    new(table) {
        this.table = table
        this
    }

    pub where(sql) {
        this.where_sql = sql
        this
    }

    pub limit(size) {
        this.limit_size = size
        this
    }

    pub sql() {
        'select * from ' + this.table + ' where ' + this.where_sql + ' limit ' + this.limit_size
    }
}

sql = Query::new('users')
    .where('age >= 18')
    .limit(10)
    .sql()

println sql
```

## 参数
类本身没有调用参数。参数写在构造方法或普通方法上。

```bt
class Page {
    page: 1
    size: 20

    new(page = 1, size = 20) {
        this.page = page
        this.size = size
        this
    }
}

p1 = Page::new()
p2 = Page::new(2, 50)
```

## 返回值
- `class Name { ... }` 会创建一个类值，并把它保存到类名变量中。
- `Name::new(...)` 默认返回类实例。
- `Name::method(...)` 会先创建类实例，再执行指定方法作为构造入口。
- 实例方法返回方法体最后一条语句的值，也可以使用 `return` 强制返回。
- 构造方法返回 `empty` 或 `null` 时，构造调用返回新实例；返回其他值时，构造调用返回该值。

## 代码示例

### 完整封装示例
```bt
class User {
    name: ''
    password: ''
    pub role: 'user'

    new(name, password) {
        this.name = name
        this.password = password
        this
    }

    pub admin(name, password) {
        this.name = name
        this.password = password
        this.role = 'admin'
        this
    }

    pub get_name() {
        this.name
    }

    pub rename(name) {
        this.name = name
        this
    }

    check_password(password) {
        this.password == password
    }

    pub login(password) {
        this.check_password(password)
    }

    pub label() {
        this.name + '[' + this.role + ']'
    }
}

user = User::new('Tom', '123456')
admin = User::admin('Root', 'root_pwd')

println user.rename('Tommy').label()
println user.login('123456')
println admin.label()
```

### 私有成员保护状态
```bt
class Counter {
    value: 0

    new(start = 0) {
        this.value = start
        this
    }

    pub inc() {
        this.value = this.value + 1
        this
    }

    pub dec() {
        this.value = this.value - 1
        this
    }

    pub get() {
        this.value
    }
}

counter = Counter::new(10)
counter.inc().inc().dec()
println counter.get()
```

## 注意事项
- `Class::new()` 是实例化语法；不要写 `new Class()`。
- `new` 是推荐的构造方法名；如果没有定义 `new`，`Class::new()` 也会返回带默认字段的实例。
- 构造方法最后一行建议写 `this`，避免最后一条赋值语句把字符串、数字等普通值当作构造结果返回。
- 成员默认私有；公开字段或方法必须写 `pub`。
- 私有成员只能通过 `this.xxx` 在类内部访问。
- 当前没有 `private`、`extends`、`super`、`static` 语法；不要按 JS 类继承写法使用。
- 字段初始值适合放标量默认值；数组、对象等可变值建议在 `new` 中用 `this.xxx = []` 或 `this.xxx = {}` 初始化，避免多个实例共享同一个默认引用。
