-
Notifications
You must be signed in to change notification settings - Fork 23
/
tiscript-article.htm
213 lines (211 loc) · 12.8 KB
/
tiscript-article.htm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="generator" content="h-smile:richtext"/>
<meta name="source"/>
</head>
<body>
<h1>TIScript, 一个优雅地JavaScript扩展脚本语言</h1>
<h2>简介</h2>
<p>作为一门脚本语言, TIScript是ECMAScript(JavaScript 1.X)的一种扩展版本。如果你愿意,你可以认为它是JavaScript++。</p>
<p>TIScript的设计初衷是基于JavaScript使用中的一些实践分析。从某些方面来说,它简化和协调了JavaScript的一些特性。例如,<em>prototype</em>(原型)机制使用起来就是比较简单的。而从另外一些方面来说,它又扩展了JavaScript,它对使用者来说保留了JS的原本"外观和感觉"。 </p>
<p>TIScript脚本引擎支持一下功能:</p>
<ul>
<li><em>编译器</em> - 产生字节码</li>
<li><em>虚拟机</em>(VM) - 执行字节码</li>
<li><em>堆管理器</em> - 使用了一个内存拷贝垃圾回收器(GC)</li>
<li><em>运行时</em> - 本地对象类的实现</li></ul>
<p>这篇文章描述TIScript的主要特征,即它与JavaScript相比,去掉的和与它不同的特征。在看完这篇文章后,你应该可以感觉到TIScript与JavaScript或Python、Perl、Lua、Ruby等动态语言是相似的。</p>
<h2>名称空间</h2>
<p>名称空间使用<code>namespace</code>关键字来声明。它们可以包含类、函数、变量和常量:</p>
<pre><em>namespace</em> MyNamespace
{
var nsVar = 1;
function Foo() { nsVar = 2; } // 设置nsVar为2
}
MyNamespace.Foo(); // 调用Foo函数
</pre>
<p>JavaScript不支持名称空间。你可以通过使用对象来模拟名称空间,但那也确实只是模拟而已。</p>
<p>TIScript中的名称空间是一个简单的命名生命周期域。例如,当处理名称空间内的赋值操作时,它看起来就像一个全局变量,TIScript运行时首先会尝试在该函数所属的当前名称空间链内查找这个变量。</p>
<h2>类, 构造器, 属性</h2>
<p>TIScript引入了真正的类。一个类可以使用<code>class</code>关键字来声明,它可以包含函数、属性方法、变量、常量和其他类:</p>
<pre><em>class</em> Bar
{
function this() { // 名称为'this'的函数是该定义类的对象的构造器。
this._one = 1;
}
function foo(p1) { // 一个方法
this._one = p1;
}
<em> property</em> one(v) { // 属性方法
<em> get</em> { return this._one; } // 获取器
<em> set</em> { this._one = v; } // 设置器
}
}
</pre>
<p>注意声明的<code>property</code>函数。这是一种特殊的函数,它用于声明可计算属性。通常,属性的访问被封装在这个函数中:</p>
<pre>var bar = new Bar(); // 调用 Bar.this()函数
bar.one = 2; // 调用 Bar.one()::set 设置器
</pre>
<p>注意,有一只特殊情况是:当一个属性的设置器在设计期时是未知的时,TIScript允许你通过<code>property undefined()</code>方法来实现对未知属性的访问控制:</p>
<pre>class Recordset
{
function getFieldValue(idx) { ... }
function getFieldIdx(byName) { ... }
<em> property undefined</em>(name, val)
{
get { var fieldIdx = this.getFieldIdx(name);
return this.getFieldValue(fieldIdx); }
}
}
</pre>
<p>这个属性处理器可以用于下面的代码:</p>
<pre>var rs = DB.exec( "SELECT one, two FROM sometable" );
var one = rs.one; // 通过上面的特殊处理器来访问数据集中名称为"one"的列信息
</pre>
<h2>轻量的匿名函数</h2>
<p>TIScript引入了定义匿名函数的轻量语法。TIScript中共有3中方式来声明匿名函数:</p>
<dl>
<dt>单语句的lambda函数:</dt>
<dd><code>':' [参数列表] ':' <语句>;</code></dd>
<dt>Lambda函数块:</dt>
<dd><code>':' [参数列表] '{' <语句列表> '}'</code></dd>
<dt>经典的匿名函数:</dt>
<dd><code>'function(' [参数列表] ')' '{' <语句列表> '}'</code></dd></dl>
<p>下面的代码演示如何通过一个匿名比较函数来实现对一个数组进行降序排序:</p>
<pre>var arr = [1,2,3];
arr.sort( :a,b: a < b? 1:-1 );
</pre>
<p>其中, <code>:a,b: a < b? 1:-1</code>是一个lambda函数,它将被传递到<code>Array.sort()</code>方法中。</p>
<h2>装饰器</h2>
<p>装饰器是一种元编程特性,它借鉴于Python语言。在TIScript脚本中, 一个装饰器是一个普通的函数。它的名称必须以'@'字符开头,并且它必须至少有一个参数。这个参数(第一个)是一个指向那些被装饰的函数(或类)的引用。 下面是一个装饰器函数声明示例:</p>
<pre>function @KEY(func, keyCode)
{
function t(event) // 封装对func()的调用到一个过滤函数
{
if(event.keyCode == keyCode)
func();
if(t.next)
t.next.call(this,event);
}
t.next = this.onKey; // this变量这里指向func()函数所属的类
this.onKey = t; // 建立事件处理链
}
</pre>
<p>如果在我们的代码中有上面的代码,那么我们可以定义当不同的按键按下时激活不同的代码块:</p>
<pre>class MyWidget : Widget
{
@KEY 'A' : { this.doSelectAll(); }
@KEY 'B' : { this.doSomethingWhenB(); }
}
</pre>
<p>上面的代码中, 两个<code>@KEY</code>装饰器装饰了两个匿名方法(见上面的节)。上面的代码假设某处存在一个有<code>onKey(event)</code>回调函数的<code>class Widget</code>类定义。</p>
<p>装饰器是一种高级特性,它需要花费一些努力来理解。当装饰器被创建,它可以很优雅的递增你的实现代码。关于装饰器的更多内容,请点击<a href="/wiki/tiscript/decorators" class="wikilink1" title="tiscript:decorators">这里</a>和<a href="http://www.terrainformatica.com/index.php/?p=108" rel="bookmark">这里</a>。</p>
<h2>迭代器</h2>
<p>JavaScript(以及TIScript)有个非常有用的for-each语句: <code>for( var item in collection ){..}</code>,其中,<em>collection</em>是一个对象或数组的实例。</p>
<p>在TIScript中,可枚举的对象列表中增加了函数实例。因此<code>for( var item in <em>func</em> )</code>语句可以调用<em>func</em>函数,并且在循环体的每次迭代中包含函数的返回值。如下面的函数:</p>
<pre>function range( from, to )
{
var idx = from - 1;
return function() { if( ++idx <= to ) return idx; }
}
</pre>
<p>在下面的for循环中,将会生成[from..to]范围的连续数字:</p>
<pre>for( var item in range(12,24) )
stdout << item << " ";
</pre>
<p>你将会得到从12到24一个一个的打印出来。</p>
<p>这里是另一个关于class-collection的示例,它允许在两个方向上枚举它的成员:</p>
<pre>class Fruits
{
function this() {
this._data = ["apple","orange","lime","lemon",
"pear","banan","kiwi","pineapple"]; }
property forward(v)
{
get {
var items = this._data; var idx = -1;
// 返回一个函数,该函数为每次迭代中下一次调用的函数
// 在这个返回函数中如果没有返回值将终止for循环
return function() { if( ++idx < items.length ) return items[idx]; }
}
}
property backward(v)
{
get {
var items = this._data; var idx = items.length;
return function() { if( --idx >= 0 ) return items[idx]; }
}
}
}
</pre>
<p>就像你看到的,这个类有两个属性,它们分别定义了两个方向上浏览集合的迭代器:</p>
<pre>var fruits = new Fruits();
stdout << "正方向上打印Fruits:\n";
for( var item in <strong>fruits.forward</strong> ) stdout << item << "\n";
stdout << "反方向上打印Fruits:\n";
for( var item in <strong>fruits.backward</strong> ) stdout << item << "\n";
</pre>
<p>Mozilla社区引入了<a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators" class="l" onmousedown="return clk(this.href,'','','res','1','&sig2=Z5S9FJOQ4UcsNkUoe67JEQ')">Iterators in Spider Monkey</a>迭代方式,我想他们借鉴了Python中的"as is"语句。不过我认为,我的这种迭代器方式更匹配JavaScript的精髓,至少我没有引入新的实体和类。</p>
<h2><code>prototype</code>(原型)属性</h2>
<p>相比于JavaScript,TIScript中的原型机制使用起来更为简单。</p>
<p>TIScript中的每个对象都有一个名称为<code>prototype</code>的属性。一个对象的prototype属性指向该对象的类(类实际上也是一个对象)。一个类的prototype属性指向它的父类。同样地,一个名称空间(它实际上也是一个对象)的prototype属性指向它的父名称空间。</p>
<p>例如,下面的语句求值都将为<code>true</code>:</p>
<pre>"somestring".prototype === String; //字符串的prototype是String类对象。
{ some:"object", literal:true }.prototype === Object;
</pre>
<p>声明的规则同样适用于用户自定义的类:</p>
<pre>class Foo { ... }
var foo = new Foo();
foo.prototype === Foo;
</pre>
<h2>类型系统</h2>
<p>JavaScript中将整数和浮点数都统一成<code>数字</code>类型。而在TIScript中,它们被分拆成了两个不同的类: <a href="Integer.htm">Integer</a>和<a href="Float.htm">Float</a>,而且它们也确实表现为两个不同的实体。</p>
<p>TIScript也引入了一些新的类型:</p>
<dl>
<dt><a href="Stream.whtm"><em>Stream</em></a></dt>
<dd>Stream是一个字符或字节序列。 TIScript运行时支持3种类型的流: 内存流(又名字符串流)、socket流、文件流。内存流是一种生成文本的有效方式,引入它的目的类似于"big Java"中的StringBuffer/StringBuilder类。</dd>
<dt><a href="Bytes.whtm"><em>Bytes</em></a></dt>
<dd>一个Bytes对象的实例是一个字节数组。</dd>
<dt><a href="Storage.htm"><em>Storage</em></a>和<a href="Index.htm"><em>Index</em></a></dt>
<dd>则两个类构成了TIScript内建的持久化机制。通过将一个TIScript对象赋值到<code>storage.root</code>属性,可以将该对象(和它引用的所有对象)持久化,这三个对象将透明的存储到存储文件中。<strong>这个文件在哪里? </strong>本质上, 这是一个对象数据库。我称它为JSON-DB, 因为JS对象的JSON子集才可以被持久化。例如socket流自然不能持久化。</dd>
<dt><em>标识符</em>(又名原子)</dt>
<dd>几乎所有的动态语言都有一种形式的原子概念。TIScript同样也有:</dd></dl>
<h3>标识符(Symbol)</h3>
<p>一个对象的名称就是一个标识符。一个标识符是一个关联到一个数字的名称。TIScript中有一个全局的<code>name<->int</code>对的映射表(内部实际是一个<em>三元搜索树</em>)。编译时,每个名称被翻译成一个int32数字。</p>
<p>在某项情况时,你可能需要显示声明标识符。这种情况,你可以使用标识符声明文字。标识符声明文字是一个以<code>'#'</code>字符开头的字符序列。它可以包含字母-数字字符、下划线(<code>'_'</code>)、减号(<code>'-'</code>)。标识符中的减号用来兼容CSS中的名称。</p>
<p>标识符的使用示例:</p>
<pre>function ChangeMode( mode )
{
if( mode == <strong>#edit</strong> )
this.readOnly = false;
else if( mode == <strong>#read-only</strong> )
this.readOnly = true;
else
throw mode.toString() + " - bad symbol!";
}
</pre>
<p>如你说看到的,标识符是自描述、方便、有效的自动枚举值。</p>
<p>标识符的另一个特征:<em>标识符访问 语句</em>。</p>
<p>如下:</p>
<code><pre>var aa = obj#name;
obj#name = val;
</pre></code>
<p>将会翻译成</p>
<pre><code>var aa = obj[#name]; //</code> 和
<code>obj[#name] = val</code>
</pre>
<p>没改变态度,不会会使语句变得更可读。</p>
<p>这种方式经常用在<a href="/sciter/main.whtm">Sciter</a>——一种嵌入的HTML/CSS/脚本 引擎。例如,访问DOM元素的样式属性:</p>
<pre>var elem = self.select("#some");
elem.style#display = "block";
elem.style#border-left-width = px(1); // 或 elem.style[#border-left-width] = "1px";
elem.style#border-right-width = px(2);
...
</pre>
<h2>总结</h2>
<p>上面的内容是TIScript的一个简要概览。在下一篇文章中,我将会解答如何将TIScript引擎集成到你的应用程序中。</p>
<p>这篇文章是用WYSIWYG HTML编辑器scapp(是<strong>Sc</strong>iter <strong>app</strong>lication的简写形式)写的。它可以在<a href="sciter-sdk.zip">Sciter SDK</a>/scapps/blocknote.net中找到:</p>
<p><img src="sciter-blocknote.png"/></p>
</body>
</html>