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 练习题
基础练习
- 创建一个计算器类,支持基本的四则运算
- 实现一个学生管理系统,包含学生类和课程类
- 编写一个简单的事件系统,支持事件注册和触发
- 实现一个带重试机制的网络请求函数
进阶练习
- 设计一个插件系统,支持动态加载和卸载插件
- 实现一个观察者模式的消息系统
- 创建一个函数式编程工具库(map、filter、reduce等)
- 设计一个状态机类,支持状态转换和事件处理
挑战练习
- 实现一个简单的模板引擎
- 创建一个异步任务调度器
- 设计一个数据验证框架
- 实现一个简单的依赖注入容器
下一章预告: 在第4章中,我们将学习Sciter中的事件处理与交互,包括DOM事件、自定义事件、用户交互处理,以及如何创建响应式的用户界面。