65
提升JavaScript生产力 异样编程手段 赵劼 - 2011.7

Javascript Uncommon Programming

  • Upload
    jeffz

  • View
    2.866

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Javascript Uncommon Programming

提升JavaScript生产力的“异样”编程手段

赵劼 - 2011.7

Page 2: Javascript Uncommon Programming

关于我• 赵劼 / 老赵 / Jeffrey Zhao / 赵姐夫

• 日写代码三百行,不辞长作程序员• 博客:http://blog.zhaojie.me/

• 微博:@老赵

• F#, JavaScript, Scala, C#, Python, .NET, Mono...

• 痛恨Java语言

Page 3: Javascript Uncommon Programming

请用您熟悉的语言实现以下功能

Page 4: Javascript Uncommon Programming

构建书籍索引

Page 5: Javascript Uncommon Programming

// C#List<string> keywords = ...;var result = keywords .GroupBy(k => k[0].ToUpper()) .ToDictionary( g => g.Key, g => g.OrderBy(k => k).ToList());

// F#let keywords = ...let result = keywords |> Seq.groupBy (fun k -> k.[0].ToUpper()) |> Seq.map (fun (k, v) -> (k, v |> Seq.sort)) |> Map.ofSeq

Page 6: Javascript Uncommon Programming

# Rubykeywords = ...result = keywords \ .group_by { |k| k[0,1].upcase } \ .each { |key, value| value.sort! }

// JavaScript(基于类库扩展)var keywords = ...;var result = keywords .groupBy(function (k) { return k[0].toUpperCase(); }) .each(function (key, value) { value.sort(); });

Page 7: Javascript Uncommon Programming

JavaList<String> keywords = ...;Map<Character, List<String>> result = new HashMap<...>();

for (String k: keywords) { char firstChar = k.charAt(0); if (!result.containsKey(firstChar)) { result.put(firstChar, new ArrayList<String>()); } result.get(firstChar).add(k);}

for (List<String> list: result.values()) { Collections.sort(list);}

Page 8: Javascript Uncommon Programming

东施效颦*

* 类似C#的API扩展

Map<Character, List<String>> result = keyword .groupBy( new F<String, Character> { public Character f(String s) { return s.charAt(0); } }) .toMap( new F<Grouping<Character, String, Character> { public Charactor f(Grouping<Char, String> g) { return g.getKey(); } }, new F<Grouping<Character, String>, List<String>> { public List<String> f(Grouping<Character, String> g) { return g .orderBy( new F<String, String> { public String f(String s) { return s; } }) .toList(); } });

Page 9: Javascript Uncommon Programming

语言特性

• 影响编程思维• 决定编程方式• 限制开发效率

Page 10: Javascript Uncommon Programming

Java丑在哪里?

• 命令式编程(怎么做)• 复杂的匿名类型语法• 强制写清所有类型名• 无法扩展既有类型

Page 11: Javascript Uncommon Programming

现代语言常见特性

• 声明式编程(做什么)• 易用的Lambda表达式语法

• 类型推断(对于静态语言)• 无侵入的类型扩展

Page 12: Javascript Uncommon Programming
Page 13: Javascript Uncommon Programming

JavaScript常用特性• 一等公民函数• 作用域,闭包• prototype扩展

• 动态this引用

• apply,call,arguments,constructor,callee

Page 14: Javascript Uncommon Programming

JavaScript Ninja已经语言实践推向了极致

Page 15: Javascript Uncommon Programming

要进一步提高生产力,必须从语言本身入手。

Page 16: Javascript Uncommon Programming

例1:迭代生成器

Page 17: Javascript Uncommon Programming

var fib = function () { var state = 0, last0, last1;

return { moveNext: function () { if (state == 0) { // 第一项 this.current = 0; state = 1; } else if (state == 1) { // 第二项 this.current = 1; last1 = 0; state = 2; } else { last0 = last1; last1 = this.current; this.current = last0 + last1; } return true; } }}

var iter = fib();while (iter.moveNext()) { print(iter.current);}

无限菲薄纳契数列

Page 18: Javascript Uncommon Programming

JavaScript 1.7*

* Firefox 2.0+ only: https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators

function fibonacci() { yield 0; yield 1;

var a = 0, current = 1; while (true) { var b = a; a = current; current = a + b;

yield current; }}

for (var i in fibonacci()) { print(i);}

Page 19: Javascript Uncommon Programming

例2:异步编程支持

Page 20: Javascript Uncommon Programming

冒泡排序var compare = function (x, y) { return x - y; }

var swap = function (a, i, j) { var t = a[i]; a[i] = a[j]; a[j] = t;}

var bubbleSort = function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { if (compare(array[y], array[y + 1]) > 0) { swap(array, y, y + 1); } } }}

Page 21: Javascript Uncommon Programming

冒泡排序动画var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); });}

var swap = function (a, i, j, callback) { var t = a[i]; a[i] = a[j]; a[j] = t; repaint(a); setTimeout(20, callback);}

var outerLoop = function (array, x, callback) { if (x < array) { innerLoop(array, x, 0, function () { outerLoop(array, x + 1, callback); }); } else { callback(); }}

var innerLoop = function (array, x, y, callback) { if (y < array.length - x) { compare(array[y], array[y + 1], function (r) { if (r > 0) { swap(array, y, y + 1, function () { innerLoop(array, x, y + 1, callback); }); } else { innerLoop(array, x, y + 1, callback); } }); } else { callback(); }}

outerLoop(array, 0, function () { console.log("done!");});

Page 22: Javascript Uncommon Programming

冒泡排序动画var compare = function (x, y, callback) { setTimeout(10, function () { callback(x - y); });}

var swap = function (a, i, j, callback) { var t = a[i]; a[i] = a[j]; a[j] = t; repaint(a); setTimeout(20, callback);}

var outerLoop = function (array, x, callback) { if (x < array) { innerLoop(array, x, 0, function () { outerLoop(array, x + 1, callback); }); } else { callback(); }}

var innerLoop = function (array, x, y, callback) { if (y < array.length - x) { compare(array[y], array[y + 1], function (r) { if (r > 0) { swap(array, y, y + 1, function () { innerLoop(array, x, y + 1, callback); }); } else { innerLoop(array, x, y + 1, callback); } }); } else { callback(); }}

outerLoop(array, 0, function () { console.log("done!");});

这TMD是什么啊!

Page 23: Javascript Uncommon Programming

如何扩展语言功能?*

• 视JavaScript代码为DSL

• 使用函数的toString方法获得代码

• 使用eval动态执行代码

* 只需JavaScript(ECMAScript 3)标准的支持

Page 24: Javascript Uncommon Programming

函数的toString方法

Page 25: Javascript Uncommon Programming

运行时代码改写var f = function () { ...}

var f = eval(compile(function () { ...}));

Page 26: Javascript Uncommon Programming

运行时编译代码var compile = function (f) {

// 获得函数代码 var code = f.toString();

// 解析代码至语法树 var ast = parse(code);

// 生成新代码 return generateCode(ast);}

Page 27: Javascript Uncommon Programming

动态编译

优点• 载体即为普通JS代码,行为一致• 无需额外编译步骤• 可以使用原有

JavaScript编辑环境

缺点• eval和编译器让人“害怕”

• 必须是合法的JavaScript

Page 28: Javascript Uncommon Programming

平易近人的eval和编译器• eval仅用于保证编程体验(例如:无需额外编译步骤)

• eval和compile仅在启动时执行一次

• 可以在发布前生成编译后的代码• 去除eval和compile的开销• 解除与编译器的依赖• 能够在ECMAScript 5的Strict Mode下执行

Page 29: Javascript Uncommon Programming

Jscex

Page 30: Javascript Uncommon Programming

Jscex是什么?

• JavaScript Computation EXpression

• JavaScript语言扩展,用于某些常见场景下的编程

• F#计算表达式特性的JavaScript移植• 受“计算表达式”特性启发• 为JavaScript设计,基于JavaScript实现

Page 31: Javascript Uncommon Programming

Jscex不是什么?

• 另一种语言• Jscex是百分百的JavaScript

• 框架• Jscex是类库,能够与几乎任何类库/框架一起使用

• JavaScript引擎/运行时• 在任何ECMAScript 3引擎上执行

Page 32: Javascript Uncommon Programming

浅尝辄止

Page 33: Javascript Uncommon Programming

异步编程十分困难

• 破坏代码局部性• 程序员习惯线性地表达算法• 异步代码将逻辑拆分地支离破碎

• 难以• 异步操作之间的协作及组合• 处理异常及取消

Page 34: Javascript Uncommon Programming

冒泡排序动画var compareAsync = eval(Jscex.compile("async", function (x, y) { $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. return x - y;}));

var swapAsync = eval(Jscex.compile("async", function (a, i, j) { var t = a[i]; a[i] = a[j]; a[j] = t; // swap repaint(a); // repaint after each swap $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms.}));

var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } }}));

http://files.zhaojie.me/jscex/samples/async/sorting-animations.html?bubble

Page 35: Javascript Uncommon Programming

冒泡排序动画var compareAsync = eval(Jscex.compile("async", function (x, y) { $await(Jscex.Async.sleep(10)); // each "compare" takes 10 ms. return x - y;}));

var swapAsync = eval(Jscex.compile("async", function (a, i, j) { var t = a[i]; a[i] = a[j]; a[j] = t; // swap repaint(a); // repaint after each swap $await(Jscex.Async.sleep(20)); // each "swap" takes 20 ms.}));

var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } }}));

http://files.zhaojie.me/jscex/samples/async/sorting-animations.html?bubble

Page 36: Javascript Uncommon Programming

Jscex异步函数

// 使用异步构造器执行编译后的代码var somethingAsync = eval(Jscex.compile("async", function (...) { // 实现 }));

Page 37: Javascript Uncommon Programming

function () { var res = $await(<async work>);}

响应

Page 38: Javascript Uncommon Programming

function () { var res = $await(<async work>);}

响应

HTTP请求UI事件时钟回调查询响应

Web Service响应代理消息

Page 39: Javascript Uncommon Programming

初始代码

var f = eval(Jscex.compile("async", function () { var img = $await(readAsync("http://...")); console.log("loaded!"); $await(writeAsync("./files/...")); console.log("saved!");}));

Page 40: Javascript Uncommon Programming

var f = eval('(function () { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { _b_.Bind(readAsync(...), function (img) { console.log("loaded!"); return _b_.Bind(writeAsync(...), function () { console.log("saved!"); return _b_.Normal(); }); }); }) );})');

等价于…

Page 41: Javascript Uncommon Programming

var f = (function () { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { _b_.Bind(readAsync(...), function (img) { console.log("loaded!"); return _b_.Bind(writeAsync(...), function () { console.log("saved!"); return _b_.Normal(); }); }); }) );});

等价于…

Page 42: Javascript Uncommon Programming

使用Express框架var app = express.createServer();

app.get('/', function (req, res) { /** * 问题:如何进行异步操作?例如: * * 1. 从数据库中获取多个键值。 * 2. 对于每个键,从缓存中获取数据。 * 如果缓存失效,则从数据库里获取数据。 * 3. 将结果列表发送至res中。 * * 注意:所有的“获取”操作都是异步的。 **/});

app.listen(3000);

Page 43: Javascript Uncommon Programming

基于Jscex编程app.getAsync('/', eval(Jscex.compile("async", function (req, res) { var keys = $await(db.getKeysAsync(...)); var results = []; for (var i = 0; i < keys.length; i++) { var r = $await(cache.getAsync(keys[i])); if (!r) { r = $await(db.getItemAsync(keys[i])); }

results.push(r); } res.send(generateList(results));})));

Page 44: Javascript Uncommon Programming

Jscex vs. 其他异步方案• 许多项目都在设法简化异步编程难度• 异步类库• Virtual Panel: How to Survive Asynchronous

Programming in JavaScript

• 异步语言• StratifiedJS

• Narrative JavaScript

• Streamline(可能是与Jscex最相似的项目)

Page 45: Javascript Uncommon Programming

Jscex vs. 异步类库• 异步类库• 在类库建立的规则内编写代码• 有限的灵活程度与表达能力• 需要较多学习

• Jscex• 使用JavaScript表达逻辑• 极高的灵活度和表达能力• 只需少量学习

Page 46: Javascript Uncommon Programming

Jscex vs. 异步语言• 异步语言• 学习新语法• 学习新语义• 改变传统编程体验

• Jscex• 完全使用JavaScript语法• 完全保留JavaScript语义• 完全保留JavaScript编程体验

Page 47: Javascript Uncommon Programming

Streamline

• 可能是与Jscex最相似的项目• 保留JavaScript语法、语义• 简单修改即可JIT编译• 无需独立的异步操作语句

• 与Jscex相较最大的区别• 目标代码生成方式• Yield – Resume vs. Asynchronous Callbacks – An

Equivalence

Page 48: Javascript Uncommon Programming

Streamline (input)

var bubbleSortAsync = function (array, _) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { if (compareAsync(array[y], array[y + 1], _) > 0) swapAsync(array, y, y + 1, _); } }}

Page 49: Javascript Uncommon Programming

Streamline (compiled)

http://sage.github.com/streamlinejs/examples/streamlineMe.html

var bubbleSortAsync = function __1(array, _) { if (!_) { return __future(__1, arguments, 1); }; var __then = _; var x = 0; var __4 = false; return function(__break) { var __loop = __nt(_, function() { var __then = __loop; if (__4) { x++; } else { __4 = true; } ; if ((x < array.length)) { var y = 0; var __3 = false; return function(__break) { var __loop = __nt(_, function() { var __then = __loop; if (__3) { y++; } else { __3 = true; } ;

if ((y < (array.length - x))) { return compareAsync(array[y], array[(y + 1)], __cb(_, function(__0, r) { if ((r > 0)) { return swapAsync(array, y, (y + 1), __cb(_, __then)); } ; return __then(); })); } else { return __break(); } ; }); return __loop(); }(__then); } else { return __break(); } ; }); return __loop(); }(__then);};

Page 50: Javascript Uncommon Programming

Jecex (input)

var bubbleSortAsync = eval(Jscex.compile("async", function (array) { for (var x = 0; x < array.length; x++) { for (var y = 0; y < array.length - x; y++) { var r = $await(compareAsync(array[y], array[y + 1])); if (r > 0) $await(swapAsync(array, y, y + 1)); } }}));

Page 51: Javascript Uncommon Programming

Jecex (compiled)var bubbleSortAsync = (function (array) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { var x = 0; return _b_.Loop( function () { return x < array.length; }, function () { x++; }, _b_.Delay(function () { var y = 0; return _b_.Loop( function () { return y < (array.length - x); }, function () { y++; } _b_.Delay(function () { return _b_.Bind(compareAsync(array[y], array[y + 1]), function (r) { if (r > 0) { return _b_.Bind(swapAsync(array, y, y + 1), function () { return _b_.Normal(); }); } else { return _b_.Normal(); } }); }), false ); }), false ); }) );})

Page 52: Javascript Uncommon Programming

Jecex (compiled)var bubbleSortAsync = (function (array) { var _b_ = Jscex.builders["async"]; return _b_.Start(this, _b_.Delay(function () { var x = 0; return _b_.Loop( function () { return x < array.length; }, function () { x++; }, _b_.Delay(function () { var y = 0; return _b_.Loop( function () { return y < (array.length - x); }, function () { y++; } _b_.Delay(function () { return _b_.Bind(compareAsync(array[y], array[y + 1]), function (r) { if (r > 0) { return _b_.Bind(swapAsync(array, y, y + 1), function () { return _b_.Normal(); }); } else { return _b_.Normal(); } }); }), false ); }), false ); }) );})

outer loop

inner loop

$await(compareAsync(...))$await(swapAsync(...))

Page 53: Javascript Uncommon Programming

Jscex代码生成模式• 代码生成模式简单而直接• 能够轻松映射至原有代码• 易于调试:使用“debugger”语句暂停代码,或在调试器中设置断点

• 为JavaScript设计的标准方法• Start, Delay, Combine

• Loop, Try

• Normal, Return, Break, Continue, Throw

• Bind

Page 54: Javascript Uncommon Programming

调试浏览器脚本

Page 55: Javascript Uncommon Programming

调试Node.js脚本

Page 56: Javascript Uncommon Programming

... the principle we go by is, don't expect to see a particular concurrency model put into C# because there're many different concurrency model ... it's more about finding things are common to all kinds of concurrency ...

- Anders Hejlsberg

Page 57: Javascript Uncommon Programming

语言与类库• 编译器(语言扩展)• JIT:在运行时生成代码(用于开发环境)• AOT:在运行之前生成代码(用于生成环境)

• 构造器(类库)• 异步构造器:简化异步编程(如F#,Scala,Python

Twisted,C# vNext等等)• 序列构造器:延迟加载的迭代器(如Python,C#,

JavaScript 1.7等等)• 更多…

Page 58: Javascript Uncommon Programming

Jscex构造器

• 编译时提供构造器名称• 构造器向编译器提供“绑定”操作名称

• 编译器根据“绑定”操作名称转化代码

• 由定义在构造器上的标准方法执行代码

Page 59: Javascript Uncommon Programming

不仅仅是异步编程// 无限的菲波纳契数列var fib = eval(Jscex.compile("seq", function () { $yield(0); $yield(1);

var a = 0, current = 1; while (true) { var b = a; a = current; current = a + b;

$yield(current); }}));

https://github.com/JeffreyZhao/jscex/blob/master/samples/seq/fib.html

Page 60: Javascript Uncommon Programming

函数式扩展

https://github.com/JeffreyZhao/jscex/blob/master/samples/seq/fib.html

fib() .filter(function (n) { return n % 2 == 0; }) .skip(2) .take(5) .zip(infinite(1)) .map(function (a) { return a[1] + ": " + a[0]; }) .each(print);

Page 61: Javascript Uncommon Programming

轻松构建扩展var filter = eval(Jscex.compile("seq", function (iter, predicate) { while (iter.moveNext()) { if (predicate(iter.current)) { $yield(iter.current); } }}));

var map = eval(Jscex.compile("seq", function (iter, mapper) { while (iter.moveNext()) { $yield(mapper(iter.current)); }}));

var zip = eval(Jscex.compile("seq", function (iter1, iter2) { while (iter1.moveNext() && iter2.moveNext()) { $yield([iter1.current, iter2.current]); }}));

https://github.com/JeffreyZhao/jscex/blob/master/src/jscex.seq.powerpack.js

Page 62: Javascript Uncommon Programming

还有Maybe Monadvar maybeFunc = function () { var maybeA = getA(); if (maybeA != Maybe.None) { var maybeB = getB(); if (maybeB != Maybe.None) { return maybeA.value + maybeB.value; } else { return Maybe.None; } } else { return Maybe.None; }}

// 简化后的版本var maybeFunc = eval(Jscex.compile("maybe", function () { var a = $try(getA()); var b = $try(getB()); return a + b;}));

Page 63: Javascript Uncommon Programming

总结

• 视JavaScirpt为DSL• 视引擎为编译+运行环境• JavaScript提供了完备的“无缝”改写代码的机制

• 现在开始尝试Jscex• 基于BSD协议发布• https://github.com/JeffreyZhao/jscex(英文)• http://www.sndacode.com/projects/jscex(中文)

Page 64: Javascript Uncommon Programming

Q & A

Page 65: Javascript Uncommon Programming

谢谢