3.1 TIScript概述

什么是TIScript?

TIScript是Sciter专用的脚本语言,它基于ECMAScript标准,但针对桌面应用开发进行了优化和扩展。TIScript提供了丰富的内置对象和API,使开发者能够轻松地创建功能强大的桌面应用程序。

TIScript特性

1. 语言特性

  • ECMAScript兼容:支持大部分JavaScript语法
  • 强类型支持:可选的类型注解和检查
  • 面向对象:完整的类和继承支持
  • 函数式编程:支持高阶函数和闭包
  • 模块系统:内置的模块导入导出机制

2. 性能特性

  • 即时编译:JIT编译提高执行效率
  • 内存管理:自动垃圾回收
  • 原生集成:直接调用系统API
  • 异步支持:内置异步编程模型

3. 开发特性

  • 调试支持:完整的调试工具链
  • 错误处理:详细的错误信息和堆栈跟踪
  • 热重载:开发时代码热更新
  • 智能提示:IDE集成和代码补全

3.2 基础语法

变量和数据类型

1. 变量声明

// 变量声明方式
var globalVar = "全局变量";
let blockVar = "块级变量";
const CONSTANT = "常量";

// 类型注解(可选)
var name: string = "张三";
var age: integer = 25;
var isActive: boolean = true;
var items: array = [1, 2, 3];
var config: object = { theme: "dark" };

2. 基本数据类型

// 数值类型
var intValue: integer = 42;
var floatValue: float = 3.14159;
var hexValue = 0xFF;
var binaryValue = 0b1010;
var octalValue = 0o755;

// 字符串类型
var singleQuote = '单引号字符串';
var doubleQuote = "双引号字符串";
var templateString = `模板字符串: ${name}, 年龄: ${age}`;
var multilineString = """
    多行字符串
    第二行
    第三行
""";

// 布尔类型
var isTrue = true;
var isFalse = false;

// 空值类型
var nullValue = null;
var undefinedValue = undefined;

// 符号类型
var symbol1 = #symbol;
var symbol2 = #"symbol with spaces";

3. 复合数据类型

// 数组
var numbers = [1, 2, 3, 4, 5];
var mixed = ["字符串", 42, true, null];
var nested = [[1, 2], [3, 4], [5, 6]];

// 数组操作
numbers.push(6);           // 添加元素
numbers.pop();             // 移除最后一个元素
numbers.unshift(0);        // 在开头添加元素
numbers.shift();           // 移除第一个元素
numbers.splice(2, 1, 99);  // 替换元素

// 对象
var person = {
    name: "李四",
    age: 30,
    address: {
        city: "北京",
        district: "朝阳区"
    },
    hobbies: ["读书", "游泳", "编程"]
};

// 对象属性访问
stdout.println(person.name);           // 点号访问
stdout.println(person["age"]);         // 括号访问
stdout.println(person.address.city);   // 嵌套访问

// 动态属性
var propName = "age";
stdout.println(person[propName]);

// 属性枚举
for (var key in person) {
    stdout.println(key + ": " + person[key]);
}

运算符

1. 算术运算符

var a = 10, b = 3;

stdout.println(a + b);    // 加法: 13
stdout.println(a - b);    // 减法: 7
stdout.println(a * b);    // 乘法: 30
stdout.println(a / b);    // 除法: 3.333...
stdout.println(a % b);    // 取余: 1
stdout.println(a ** b);   // 幂运算: 1000

// 自增自减
var counter = 0;
counter++;                // 后置自增
++counter;                // 前置自增
counter--;                // 后置自减
--counter;                // 前置自减

2. 比较运算符

var x = 5, y = "5";

stdout.println(x == y);   // 相等比较: true
stdout.println(x === y);  // 严格相等: false
stdout.println(x != y);   // 不等比较: false
stdout.println(x !== y);  // 严格不等: true
stdout.println(x > 3);    // 大于: true
stdout.println(x < 10);   // 小于: true
stdout.println(x >= 5);   // 大于等于: true
stdout.println(x <= 5);   // 小于等于: true

3. 逻辑运算符

var isLoggedIn = true;
var hasPermission = false;

// 逻辑与
if (isLoggedIn && hasPermission) {
    stdout.println("可以访问");
}

// 逻辑或
if (isLoggedIn || hasPermission) {
    stdout.println("部分访问权限");
}

// 逻辑非
if (!hasPermission) {
    stdout.println("没有权限");
}

// 短路求值
var result = isLoggedIn && getUserData();
var fallback = userName || "匿名用户";

4. 位运算符

var flags = 0b1010;  // 二进制: 10

stdout.println(flags & 0b1100);   // 按位与: 8
stdout.println(flags | 0b0101);   // 按位或: 15
stdout.println(flags ^ 0b1111);   // 按位异或: 5
stdout.println(~flags);           // 按位非: -11
stdout.println(flags << 2);       // 左移: 40
stdout.println(flags >> 1);       // 右移: 5

控制流语句

1. 条件语句

// if-else语句
var score = 85;

if (score >= 90) {
    stdout.println("优秀");
} else if (score >= 80) {
    stdout.println("良好");
} else if (score >= 70) {
    stdout.println("中等");
} else if (score >= 60) {
    stdout.println("及格");
} else {
    stdout.println("不及格");
}

// 三元运算符
var grade = score >= 60 ? "及格" : "不及格";
var level = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D";

// switch语句
var dayOfWeek = 3;
var dayName;

switch (dayOfWeek) {
    case 1:
        dayName = "星期一";
        break;
    case 2:
        dayName = "星期二";
        break;
    case 3:
        dayName = "星期三";
        break;
    case 4:
        dayName = "星期四";
        break;
    case 5:
        dayName = "星期五";
        break;
    case 6:
    case 7:
        dayName = "周末";
        break;
    default:
        dayName = "无效日期";
}

stdout.println(dayName);

2. 循环语句

// for循环
for (var i = 0; i < 5; i++) {
    stdout.println("循环次数: " + i);
}

// for-in循环(遍历对象属性)
var obj = { a: 1, b: 2, c: 3 };
for (var key in obj) {
    stdout.println(key + ": " + obj[key]);
}

// for-of循环(遍历可迭代对象)
var arr = ["苹果", "香蕉", "橙子"];
for (var item of arr) {
    stdout.println("水果: " + item);
}

// while循环
var count = 0;
while (count < 3) {
    stdout.println("while循环: " + count);
    count++;
}

// do-while循环
var num = 0;
do {
    stdout.println("do-while循环: " + num);
    num++;
} while (num < 3);

// 循环控制
for (var i = 0; i < 10; i++) {
    if (i === 3) continue;  // 跳过当前迭代
    if (i === 7) break;     // 跳出循环
    stdout.println(i);
}

3.3 函数和作用域

函数定义

1. 函数声明

// 基本函数声明
function greet(name) {
    return "你好, " + name + "!";
}

// 带类型注解的函数
function add(a: integer, b: integer): integer {
    return a + b;
}

// 带默认参数的函数
function createUser(name, age = 18, role = "user") {
    return {
        name: name,
        age: age,
        role: role,
        createdAt: new Date()
    };
}

// 可变参数函数
function sum(...numbers) {
    var total = 0;
    for (var num of numbers) {
        total += num;
    }
    return total;
}

// 使用函数
stdout.println(greet("张三"));                    // 你好, 张三!
stdout.println(add(5, 3));                       // 8
stdout.println(createUser("李四"));               // 使用默认参数
stdout.println(sum(1, 2, 3, 4, 5));             // 15

2. 函数表达式

// 匿名函数表达式
var multiply = function(a, b) {
    return a * b;
};

// 命名函数表达式
var factorial = function fact(n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
};

// 箭头函数(简化语法)
var square = (x) => x * x;
var isEven = (n) => n % 2 === 0;
var processArray = (arr) => {
    return arr.map(x => x * 2).filter(x => x > 10);
};

// 立即执行函数表达式 (IIFE)
(function() {
    var privateVar = "私有变量";
    stdout.println("立即执行: " + privateVar);
})();

// 带参数的IIFE
(function(name) {
    stdout.println("Hello, " + name);
})("World");

作用域和闭包

1. 作用域类型

// 全局作用域
var globalVar = "全局变量";

function outerFunction() {
    // 函数作用域
    var outerVar = "外部变量";
    
    function innerFunction() {
        // 内部作用域
        var innerVar = "内部变量";
        
        // 可以访问所有上级作用域的变量
        stdout.println(globalVar);  // 可以访问
        stdout.println(outerVar);   // 可以访问
        stdout.println(innerVar);   // 可以访问
    }
    
    innerFunction();
    // stdout.println(innerVar);  // 错误:无法访问内部变量
}

// 块级作用域(let和const)
if (true) {
    let blockVar = "块级变量";
    const BLOCK_CONST = "块级常量";
    var functionVar = "函数级变量";
}

// stdout.println(blockVar);     // 错误:块级变量不可访问
// stdout.println(BLOCK_CONST);  // 错误:块级常量不可访问
stdout.println(functionVar);     // 可以访问:函数级变量

2. 闭包

// 基本闭包
function createCounter() {
    var count = 0;
    
    return function() {
        count++;
        return count;
    };
}

var counter1 = createCounter();
var counter2 = createCounter();

stdout.println(counter1());  // 1
stdout.println(counter1());  // 2
stdout.println(counter2());  // 1 (独立的计数器)

// 闭包工厂
function createMultiplier(factor) {
    return function(number) {
        return number * factor;
    };
}

var double = createMultiplier(2);
var triple = createMultiplier(3);

stdout.println(double(5));   // 10
stdout.println(triple(5));   // 15

// 模块模式
var Calculator = (function() {
    var history = [];
    
    function log(operation, result) {
        history.push({ operation: operation, result: result });
    }
    
    return {
        add: function(a, b) {
            var result = a + b;
            log(`${a} + ${b}`, result);
            return result;
        },
        
        subtract: function(a, b) {
            var result = a - b;
            log(`${a} - ${b}`, result);
            return result;
        },
        
        getHistory: function() {
            return history.slice(); // 返回副本
        },
        
        clearHistory: function() {
            history = [];
        }
    };
})();

// 使用模块
Calculator.add(5, 3);
Calculator.subtract(10, 4);
stdout.println(Calculator.getHistory());

高阶函数

1. 函数作为参数

// 回调函数
function processData(data, callback) {
    var result = data.map(x => x * 2);
    callback(result);
}

processData([1, 2, 3], function(result) {
    stdout.println("处理结果: " + result);
});

// 事件处理
function addEventListener(element, event, handler) {
    element.on(event, handler);
}

// 函数组合
function compose(f, g) {
    return function(x) {
        return f(g(x));
    };
}

var addOne = x => x + 1;
var multiplyTwo = x => x * 2;
var addThenMultiply = compose(multiplyTwo, addOne);

stdout.println(addThenMultiply(3)); // (3 + 1) * 2 = 8

2. 函数作为返回值

// 柯里化
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...nextArgs) {
                return curried.apply(this, args.concat(nextArgs));
            };
        }
    };
}

function add(a, b, c) {
    return a + b + c;
}

var curriedAdd = curry(add);
stdout.println(curriedAdd(1)(2)(3)); // 6
stdout.println(curriedAdd(1, 2)(3)); // 6
stdout.println(curriedAdd(1)(2, 3)); // 6

// 偏函数应用
function partial(fn, ...presetArgs) {
    return function(...laterArgs) {
        return fn(...presetArgs, ...laterArgs);
    };
}

var multiply = (a, b, c) => a * b * c;
var multiplyByTwo = partial(multiply, 2);
stdout.println(multiplyByTwo(3, 4)); // 2 * 3 * 4 = 24

3.4 面向对象编程

类和对象

1. 类定义

// 基本类定义
class Person {
    // 构造函数
    function this(name, age) {
        this.name = name;
        this.age = age;
        this.id = Person.generateId();
    }
    
    // 实例方法
    function introduce() {
        return `我是${this.name},今年${this.age}岁`;
    }
    
    function celebrateBirthday() {
        this.age++;
        stdout.println(`${this.name}过生日了!现在${this.age}岁`);
    }
    
    // 静态方法
    function generateId() {
        return "ID_" + Math.random().toString(36).substr(2, 9);
    }
    
    // 属性访问器
    property isAdult(v) {
        get return this.age >= 18;
        set this.age = v ? 18 : 17;
    }
    
    // 私有方法(约定以_开头)
    function _validateAge(age) {
        return age >= 0 && age <= 150;
    }
}

// 创建实例
var person1 = new Person("张三", 25);
var person2 = new Person("李四", 17);

stdout.println(person1.introduce());
stdout.println(person2.isAdult); // false
person1.celebrateBirthday();

2. 继承

// 基类
class Animal {
    function this(name, species) {
        this.name = name;
        this.species = species;
        this.energy = 100;
    }
    
    function eat(food) {
        this.energy += 10;
        stdout.println(`${this.name}吃了${food},能量增加到${this.energy}`);
    }
    
    function sleep() {
        this.energy += 20;
        stdout.println(`${this.name}睡觉了,能量恢复到${this.energy}`);
    }
    
    function makeSound() {
        stdout.println(`${this.name}发出了声音`);
    }
}

// 派生类
class Dog extends Animal {
    function this(name, breed) {
        super(name, "犬类");  // 调用父类构造函数
        this.breed = breed;
        this.loyalty = 100;
    }
    
    // 重写父类方法
    function makeSound() {
        stdout.println(`${this.name}汪汪叫`);
    }
    
    // 新增方法
    function fetch(item) {
        this.energy -= 5;
        this.loyalty += 5;
        stdout.println(`${this.name}捡回了${item}`);
    }
    
    function wagTail() {
        stdout.println(`${this.name}摇尾巴表示开心`);
    }
}

class Cat extends Animal {
    function this(name, color) {
        super(name, "猫科");
        this.color = color;
        this.independence = 80;
    }
    
    function makeSound() {
        stdout.println(`${this.name}喵喵叫`);
    }
    
    function climb() {
        this.energy -= 3;
        stdout.println(`${this.name}爬到了高处`);
    }
    
    function purr() {
        stdout.println(`${this.name}发出呼噜声`);
    }
}

// 使用继承
var dog = new Dog("旺财", "金毛");
var cat = new Cat("咪咪", "橘色");

dog.eat("骨头");
dog.makeSound();  // 汪汪叫
dog.fetch("球");

cat.eat("鱼");
cat.makeSound();  // 喵喵叫
cat.climb();

3. 多态和接口

// 接口定义(通过约定实现)
class Drawable {
    function draw() {
        throw new Error("draw方法必须被实现");
    }
    
    function getArea() {
        throw new Error("getArea方法必须被实现");
    }
}

// 实现接口的类
class Circle extends Drawable {
    function this(radius) {
        this.radius = radius;
    }
    
    function draw() {
        stdout.println(`绘制半径为${this.radius}的圆形`);
    }
    
    function getArea() {
        return Math.PI * this.radius * this.radius;
    }
}

class Rectangle extends Drawable {
    function this(width, height) {
        this.width = width;
        this.height = height;
    }
    
    function draw() {
        stdout.println(`绘制${this.width}x${this.height}的矩形`);
    }
    
    function getArea() {
        return this.width * this.height;
    }
}

// 多态使用
function drawShapes(shapes) {
    for (var shape of shapes) {
        shape.draw();
        stdout.println(`面积: ${shape.getArea()}`);
    }
}

var shapes = [
    new Circle(5),
    new Rectangle(4, 6),
    new Circle(3)
];

drawShapes(shapes);

混入和组合

1. 混入模式

// 混入对象
var Flyable = {
    fly: function() {
        stdout.println(`${this.name}在飞行`);
    },
    
    land: function() {
        stdout.println(`${this.name}降落了`);
    }
};

var Swimmable = {
    swim: function() {
        stdout.println(`${this.name}在游泳`);
    },
    
    dive: function() {
        stdout.println(`${this.name}潜水了`);
    }
};

// 混入函数
function mixin(target, ...sources) {
    for (var source of sources) {
        for (var key in source) {
            if (source.hasOwnProperty(key)) {
                target[key] = source[key];
            }
        }
    }
    return target;
}

// 使用混入
class Duck extends Animal {
    function this(name) {
        super(name, "鸭科");
    }
    
    function makeSound() {
        stdout.println(`${this.name}嘎嘎叫`);
    }
}

// 为Duck类添加飞行和游泳能力
mixin(Duck.prototype, Flyable, Swimmable);

var duck = new Duck("唐老鸭");
duck.makeSound();
duck.fly();
duck.swim();
duck.land();
duck.dive();

2. 组合模式

// 组合优于继承
class Engine {
    function this(power) {
        this.power = power;
        this.running = false;
    }
    
    function start() {
        this.running = true;
        stdout.println(`引擎启动,功率: ${this.power}马力`);
    }
    
    function stop() {
        this.running = false;
        stdout.println("引擎停止");
    }
}

class GPS {
    function this() {
        this.currentLocation = { lat: 0, lng: 0 };
    }
    
    function navigate(destination) {
        stdout.println(`导航到: ${destination}`);
    }
    
    function getCurrentLocation() {
        return this.currentLocation;
    }
}

class Car {
    function this(brand, model) {
        this.brand = brand;
        this.model = model;
        this.engine = new Engine(200);
        this.gps = new GPS();
        this.speed = 0;
    }
    
    function start() {
        this.engine.start();
        stdout.println(`${this.brand} ${this.model}准备出发`);
    }
    
    function drive(destination) {
        if (this.engine.running) {
            this.gps.navigate(destination);
            this.speed = 60;
            stdout.println(`以${this.speed}km/h的速度行驶`);
        } else {
            stdout.println("请先启动引擎");
        }
    }
    
    function stop() {
        this.speed = 0;
        this.engine.stop();
        stdout.println("车辆停止");
    }
}

// 使用组合
var car = new Car("丰田", "凯美瑞");
car.start();
car.drive("购物中心");
car.stop();

3.5 异常处理

异常捕获和处理

1. 基本异常处理

// try-catch语句
try {
    var result = riskyOperation();
    stdout.println("操作成功: " + result);
} catch (error) {
    stderr.println("捕获到错误: " + error.message);
    stderr.println("错误类型: " + error.name);
    stderr.println("堆栈跟踪: " + error.stack);
}

// try-catch-finally
try {
    var file = openFile("data.txt");
    var content = file.read();
    processContent(content);
} catch (error) {
    stderr.println("文件处理错误: " + error.message);
} finally {
    // 无论是否出错都会执行
    if (file) {
        file.close();
        stdout.println("文件已关闭");
    }
}

2. 抛出异常

// 抛出基本异常
function divide(a, b) {
    if (b === 0) {
        throw new Error("除数不能为零");
    }
    return a / b;
}

// 抛出自定义异常
class ValidationError extends Error {
    function this(message, field) {
        super(message);
        this.name = "ValidationError";
        this.field = field;
    }
}

class NetworkError extends Error {
    function this(message, statusCode) {
        super(message);
        this.name = "NetworkError";
        this.statusCode = statusCode;
    }
}

function validateUser(user) {
    if (!user.name) {
        throw new ValidationError("用户名不能为空", "name");
    }
    if (!user.email || !user.email.includes("@")) {
        throw new ValidationError("邮箱格式不正确", "email");
    }
    if (user.age < 0 || user.age > 150) {
        throw new ValidationError("年龄必须在0-150之间", "age");
    }
}

// 使用自定义异常
try {
    var user = { name: "", email: "invalid-email", age: -5 };
    validateUser(user);
} catch (error) {
    if (error instanceof ValidationError) {
        stderr.println(`验证错误 [${error.field}]: ${error.message}`);
    } else if (error instanceof NetworkError) {
        stderr.println(`网络错误 [${error.statusCode}]: ${error.message}`);
    } else {
        stderr.println("未知错误: " + error.message);
    }
}

3. 异常链和错误恢复

// 异常链
function processData(data) {
    try {
        return parseData(data);
    } catch (parseError) {
        var wrappedError = new Error("数据处理失败");
        wrappedError.cause = parseError;
        throw wrappedError;
    }
}

function parseData(data) {
    try {
        return JSON.parse(data);
    } catch (jsonError) {
        throw new Error("JSON解析失败: " + jsonError.message);
    }
}

// 错误恢复策略
class RetryableOperation {
    function this(maxRetries = 3, delay = 1000) {
        this.maxRetries = maxRetries;
        this.delay = delay;
    }
    
    function execute(operation) {
        var attempts = 0;
        
        while (attempts < this.maxRetries) {
            try {
                return operation();
            } catch (error) {
                attempts++;
                
                if (attempts >= this.maxRetries) {
                    throw new Error(`操作失败,已重试${attempts}次: ${error.message}`);
                }
                
                stdout.println(`操作失败,${this.delay}ms后重试 (${attempts}/${this.maxRetries})`);
                
                // 延迟重试
                view.timer(this.delay, function() {
                    return false; // 停止定时器
                });
            }
        }
    }
}

// 使用重试机制
var retryable = new RetryableOperation(3, 2000);

try {
    var result = retryable.execute(function() {
        // 模拟可能失败的操作
        if (Math.random() < 0.7) {
            throw new Error("随机失败");
        }
        return "操作成功";
    });
    
    stdout.println(result);
} catch (error) {
    stderr.println("最终失败: " + error.message);
}

3.6 本章总结

在本章中,我们深入学习了TIScript脚本语言的核心特性:

语言基础

  • 变量声明和数据类型
  • 运算符和表达式
  • 控制流语句

函数编程

  • 函数定义和调用
  • 作用域和闭包
  • 高阶函数和函数式编程

面向对象

  • 类和对象的定义
  • 继承和多态
  • 混入和组合模式

异常处理

  • 异常捕获和处理
  • 自定义异常类型
  • 错误恢复策略

3.7 练习题

基础练习

  1. 创建一个计算器类,支持基本的四则运算
  2. 实现一个学生管理系统,包含学生类和课程类
  3. 编写一个简单的事件系统,支持事件注册和触发
  4. 实现一个带重试机制的网络请求函数

进阶练习

  1. 设计一个插件系统,支持动态加载和卸载插件
  2. 实现一个观察者模式的消息系统
  3. 创建一个函数式编程工具库(map、filter、reduce等)
  4. 设计一个状态机类,支持状态转换和事件处理

挑战练习

  1. 实现一个简单的模板引擎
  2. 创建一个异步任务调度器
  3. 设计一个数据验证框架
  4. 实现一个简单的依赖注入容器

下一章预告: 在第4章中,我们将学习Sciter中的事件处理与交互,包括DOM事件、自定义事件、用户交互处理,以及如何创建响应式的用户界面。