免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: send_linux
打印 上一主题 下一主题

[JavaScript] Web开发技术讨论之二:如何编写高效JavaScript代码 [复制链接]

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
31 [报告]
发表于 2014-03-23 20:39 |只看该作者
回复 28# starwing83

既然说到ORM,就继续说R。。。 O就不想说了。。。
谦虚点说是我理解不了R的深意。 不客气点说。。。 这种非人道的东西是怎么占据统治地位这么多年的?

比如许多optional field。
有无数的relational evangelist写了无数的反对EAV的文章。
但optional field的需要是客观存在的。
There’s an old saying in the relational database world: on a long enough timeline, all fields become optional.
- Seven Databases in Seven Weeks

反对了半天也不给个更好的解决方案。

比如evangelist说:用foreign key维持data integrity!
听上去很美好,但前段时间ZR的一个问题彻底把我问傻了。。。
比如可以对游戏、相册、用户、文章等等进行评论。而所有的commet都有共同的属性。于是问题就来了。
如果要按正统的、王道的使用方法,就需要game_comment,album_comment,user_comment,article_comment。。。否则没法建立FK。
但这种使用方法的弊端 —— 比如增加或删除comment本身的属性时 —— 没人提。

我很怀疑大量与R有关的文章的作者到底用不用R做实际开发??????

PostgreSQL本身很美好。 有array/intarray, hstore/json/xml, custome type/table inheritance来作为one to many, optional fields, 以及后续那个都不知道该怎么命名的该死的问题的另一种解决方式。
但都要被jdbc卡住。。。  而且jdbc(或者java without jni)还不支持unix domain socket。。。  有精力我都想绕过jdbc了。。。
或者逼急了直接存Clojure(语言强相关无所谓时)或JSON(需要语言无关时)数据进去。。。

感觉R是有一些brilliant idea在里面的。 但从它的发明、发展到现在已经有一些东西失传了 —— 尤其是R适合做什么。
而requirement is always changing在7,80年代可能根本就不是一个requirement。 类似的也许还有modular, reusable, composable。

比如authentication。它只需要account里的login与password(暂时把password究竟应该怎么处理抛开)信息。如果需要reset password,可能还需要email。
但根据具体的系统需要,注册的时候可能还需要填写其他一些信息avatar,age,invite_code...。

且不说具体实现时是分开的table还是monolithic table,问题在于怎么把sign up得到的信息分派到不同组件里去。
就只对authentication这个具体情况来说, 让sign up时只填写login与password, 其他信息等注册完后再慢慢填写可能恰好是一个比较好的方案。 让用户注册时少想,少填一些乱七八糟的东西。
但其他案例不一定都可以这样。

我现的打算之一是让不同组件只提需求(不仅仅是查询还包括修改的)而不实际进行操作,然后在处理request的时候将它们组合起来一并处理, 将结果分派下去。
在request的时候就把transaction打开,所有组件在查询时可以获得一致的信息, 在某个组建修改失败时可以整体回滚。
至于怎么实现或者能不能实现还不清楚。

打算之二是直接Restful -> Relational,然后再上面加一个权限控制。至于另一个方向。。。 我越是深入的看SQL的东西越是觉得双向映射不可能。

一陷web深似海,从此节操是路人。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
32 [报告]
发表于 2014-03-23 20:55 |只看该作者
回复 28# starwing83

继续说lightweight? CPS transform。
有这样一个库,或者可能说它是工具更恰当一些: A compiler for JavaScript asynchronous Continuation-Passing Style transformation
说它轻量是因为写的依然是Javascript,只是添加了几个伪关键字。 并且翻译后的结果依然是可读的。 而不像{Clojure,Coffee,Typed,...}Script那样写的完全是另一种语言。
没有实际使用过,因为。。。 你知道目前前端是谁在写。。。

BYW: CoffeeScript的wiki里有一条List of languages that compile to JS
我突然对它有好感了。。。 倒不是说对CoffeeScript本身, 而是它的这种态度。
不是自己闷头搞一堆, 然后各种宣传: 哦, 看我这XXX, 多美妙啊。 而是让自己以及顾客货比三家。。。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
33 [报告]
发表于 2014-03-23 21:11 |只看该作者
回复 31# OwnWaterloo


    太好了,我今天上午还在犹豫是不是继续回个贴……因为有东西没说完……而且刚刚好就是ORM方面的经验……对了,等会儿也顺带说一下关于REST->ORM的经验,因为最近客户端正在这么用。

是这样的,如果我没理解错,ORM是指Object Relation Model,Object指的是你程序里面能操纵的数据结构(表、对象、结构体、XML——这SB东西有人用?),R貌似是有两层意思的,第一,是指这玩意儿最终映射的是关心型数据库——至于这东西(包括SQL)有多坑爹你也知道的。所以,这里的关系其实还有另外一个含义——把所有的Model之间的关系,很简单的表示成为下面的几种:
  - 1对1
  - 1对多
  - 多对1
  - 多对多

分别有不同的实现方法,我们先说接口,再说实现。

接口上,sequelize和ORM2是不一样的,先说一样的部分,一样的部分是,都有两个核心的接口:hasOne和hasMany,至于是什么意思你肯定懂的。不一样的部分在于,sequelize有一个很特殊的接口叫做belongTo,而ORM2则是采取在options里面放reversed的方法来指定反向的连接。现在倾向于认为sequelize的方法更好——初期来看可能觉得反直觉——而且我到现在为止都觉得这肯定是个Bug,不是库的就是作者脑子的bug……但是不管怎么说,将“我有啥”和“啥属于谁”分离,的确是正确的做法。

至于这些接口如何和上面四种类型对应,那你自己都能脑补出来,我就不细说了。

然后就是实现,实现无非就是外键,而外键又有两种方式:嵌入表,或者是采用所谓的“连接表”(只有外键的表)。后者在sequelize里面本质上还是一个Model,所以其实就一种方式(其实RDB也就这种好吧?)

然后说说你提到的什么数据冗余性的事儿——我才不管呢,能工作就成,管你optional不optional,反正现在大部分的域都是optional,也没觉得有多不好,反正硬盘便宜。

在这种情况下,我觉得ORM和R是没什么太大关系的,因为这套东西照样也可以用NoSQL的东西实现。我觉得这种关系分类(上面提到的四种,三个接口——hasOne,hasMany,belongsTo)就完全可以应付所有的需求了——换句话说,我觉得就已经完备了。完备神马的你懂的。

最后说写SQL的问题,说句实话,有的,但是主要原因其实是库的接口渣(明明对着一个Model可以进行的操作,对着其他model(比如连接表model)就失效),其实如果你对这个关系有足够多的理解,你会发现所有的查询还是可以用接口写出来,只不过是效率高低的问题,这里的”足够理解”是指:
1. R其实就是外键
2. 连接表也是表
3. 接口的最大支持什么

ORM的查询接口的最大支持,就可以支持对任意信息的查询。举个例子,假设有个接口叫findAndCountAll,能一边查一边数。OK,那就意味着所有的Model(包括【看上去】不支持的)都支持了这个操作,只是你会不会写罢了。

退一万步说,如果你真的想写SQL也不会有人阻止你,如果你想写SQL又不想SQL的麻烦,还有很多侵入式的DSL可以给你用的。

这里的核心思想是,ORM最重要的点是把关系给限制到四种了(其实这四种可以表达所有的关系了),这个“关系限制”瞬间让我想到了“操作限制”——没错,就是REST。

我前段时间找了很多REST的文章看看——主要目的是看看这货是不是又是一个卖名词的,后来发现还是有点儿货的。什么状态转换都是装逼的,其实就是很简单的一件事儿:
1. 所有东西都有地址
2. 所有东西都支持共同的操作

你看,像不像被完全阉割版本的OO?其实如果按照OO的标准消息模型来看,OO这玩意儿本身就应该是没状态的,但是问题就是这里面完全没保证——现在REST保证了这点。

说到这里问个问题:我在REST里面传一个token,然后服务器保有一份状态,然后REST的操作就改变这份状态,这不算传输过程中传递状态吧?其实我觉得传输就是传输,传输哪里会有状态,不明白,解释一下哈……

反正现在服务器最开始的时候完全没想着REST,只是觉得“怎么漂亮怎么来”,这边的安卓小哥之前还抱怨过说用户id嵌入请求路径怎么怎么个难用。结果我自己跑去写Android的时候就发现了retrofit了,然后终于为了这货下决心看了REST(理解就是上面的东西),发现这完全值得啊= =

然后就是这么一个结构,服务器Java定义了一堆的Beans(就是结构体啦,Javaer神马的,你懂的),然后这些Beans继承自ActiveAndroid的Model,一下子瞬间就从网络打通到了数据库了——通过retrofit,数据可以通过REST获得,然后通过gson转换成Beans,然后Beans继承自Model——这就意味着可以直接save,bingo!

恩,反正我的经验就是这样的——REST传输的representative本身,是可以转换成Model的,这样就直接可以搭上ORM的车了,中间可能需要一些处理——很显然,这些处理是类似“管道过滤器的”——哈,Promise就用上了!

目前服务器和客户端就都是这个架构了,数据从客户端->Model->REST->Model->数据库,写起来非常爽。

额,我还有什么忘了回的么?

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
34 [报告]
发表于 2014-03-23 21:13 |只看该作者
回复 32# OwnWaterloo


    恩,这种感觉的确很好,选bluebird也是因为这个,github页直接就有和其他promise的比较,瞬间就好感了……

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
35 [报告]
发表于 2014-03-23 22:06 |只看该作者
本帖最后由 OwnWaterloo 于 2014-03-23 22:30 编辑

回复 28# starwing83

关于poll。

1. 假设需要两个异步操作。但后一个的结果是依赖与前一个的。比如先通过用户名查询id,然后通过id查询该用户的其他信息。
这种情况下无论是CPS还是Promise都没有poll或并发的需求?

2. 如果两个异步操作的结果是没有依赖的。比如同时搜索CSP的文章与搜索Communicating Sequential Processes的图片,然后展示它们。
哪个结构先到就先展示哪个。有并发需求但依然没有poll的需求?
CPS需要传递两次render —— 如果render不是一个简单的函数就需要写两次, 你说的是这个问题?
而Promise的那个map, 呃, 完成的工作是什么?

  1. (a -> b) -> [Promise a] -> [Promise b]?
  2. (a -> Promise b) -> [a] -> [Promise b]?
  3. (a -> b) -> [Promise a] -> Promise [b]?
复制代码
还是别的什么?

3.再假设,同样是发起两个搜索, 但增加一个超时。 超时后就给用户报告一个错误并且忽略后续可能得到的搜索结果。
这种情况有并发且有poll的需求。

看这个: Google Search 2.1

  1.     c := make(chan Result)
  2.     go func() { c <- Web(query) } ()
  3.     go func() { c <- Image(query) } ()
  4.     go func() { c <- Video(query) } ()

  5.     timeout := time.After(80 * time.Millisecond)
  6.     for i := 0; i < 3; i++ {
  7.         select {
  8.         case result := <-c:
  9.             results = append(results, result)
  10.         case <-timeout:
  11.             fmt.Println("timed out")
  12.             return
  13.         }
  14.     }
  15.     return
复制代码
依然发起搜索操作。得到的结果往一个channel里塞。然后发起一个timeout操作,得到的结果往另一个channel里塞。最后select(poll)两个channel。
搜索依然是并发的。先到先收集。并且在timeout到了之后就放弃收集结果。
同样可以解决2里面render需要写两处的问题。

只是怎么在JS里实现还不清楚。

而如果用promise该怎么处理这种需要coordination的操作?
感觉promise就是一个one element的定长channel。 还不一定能找到poll多个promise的方法。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
36 [报告]
发表于 2014-03-23 22:33 |只看该作者
回复 33# starwing83

纳尼? ORM的M是model不是mapping?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
37 [报告]
发表于 2014-03-23 23:23 |只看该作者
回复 28# starwing83

关于剩下的一些东西。

>> 其实promise并不会用Q的example那么用,几乎每一个then都是一个匿名函数——说白了,除了能节省嵌套的缩进以外,目前我还没用它做太多事。
也就是前面提到的这种情况?
>> 而如果整个Q.fcall.then.then...done就是one shot的, 那它与generator或者让那人肉CPS能自动化的方案相比的优势在哪里?
而非:
>> 如果promisedStepI是打算被复用的,在实现的时候怎么能预先知道究竟应该传递多少?

那确实可以用下面这些办法:
>> 另外的办法就是两个了:
>> 1. 外面用var,里面回调赋值
>> 2. 嵌套的then,但是只是在需要某些参数的时候嵌套。
>> 其实还有一个办法,就是把v1v2v3v4这些一并传下去(只需要传递需要的参数即可)。

>> 说白了,这东西就是一个尽量不改变语法,尽量保持一致性的前提下做的一个管道过滤系统。实际开发中的确很好用。
如果promise的目的是不改变语法,能在现有的各种JS基础上容易实现的话。。。 研究的动力小一些了。。。
我还是更倾向于转换为CPS的方式。。。 无论是轻量的(JS+一些特殊关键字)还是重量的(干脆就是另一门语言)。。。 不在乎写的还是不是真正的JS。。。 我已经把JS看作web上的asm或C了。。。

一方面是因为JS在这方面太混乱了。。。
ES6的promise貌似现在都没有定论,而其他的类似的库太多太多。。。不知道如果ES6早一点会不会改善这个情况。

并且另一方面,因为JS这方面现在很混乱,就不敢轻易地学甚至尝试。
对于这个
>> 解决你说的问题的一个办法是tap——即忽略本次的返回值,下个then还是得到上次的返回值——这用来做“不影响上下文的插件处理”。
也是一种特殊的组合的方式。
得去找选的那个promise库里面有没有。。。 如果没有又该怎么办? 如果不能像tap这样在库外可以实现时该怎么办?
至少得确定有一个绕过抽象的方式吧。。。

而且需要的转换并不多。。。 比如do

  1. do x1 <- action1
  2.    x2 <- action2
  3.    action3 x1 x2
复制代码

  1. bind(action1,function (x1) {
  2.   bind(action2,function (x2) {
  3.     action3(x1,x2);
  4.   });
  5. });
复制代码
很多其他语言求而不得的语法糖却被Considered Harmful。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
38 [报告]
发表于 2014-03-23 23:30 |只看该作者
starwing83 发表于 2014-03-23 21:13
回复 32# OwnWaterloo

    恩,这种感觉的确很好,选bluebird也是因为这个,github页直接就有和其他promise的比较,瞬间就好感了……


wiki home,wiki pages里貌似都没有。
难道是首页里features列出的那些?
Promises A+ 2.0.2
Cancellation
Progression
Synchronous inspection
.bind
Complete parallel for C# 5.0 async and await
Collection methods such as All, any, some, settle, map, filter, reduce, spread, join, race...
Practical debugging solutions such as unhandled rejection reporting, typed catches, catching only what you expect and very long, relevant stack tra

这些里面提到的都支持?比如 A+, async/await, 等等?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
39 [报告]
发表于 2014-03-24 00:40 |只看该作者
starwing83 发表于 2014-03-23 21:11
回复 31# OwnWaterloo

说到这里问个问题:我在REST里面传一个token,然后服务器保有一份状态,然后REST的操作就改变这份状态,这不算传输过程中传递状态吧?其实我觉得传输就是传输,传输哪里会有状态,不明白,解释一下哈……

没懂。。。

感觉S更倾向与application state:
* 请求的是那个uri
* 请求的方法
* 带有的query-string
* body
* Headers(content-type,cookies...)
就是完成该request所需要的所有信息。

而T部分,有一个与RAII不相上下的黑话:Hypermedia as the Engine of Application State。。。
用HATEOAS来控制引导客户端转变(T)application state。

http/html里的Hypermedia就是在返回的Representation里包含
*

  1. <a href="http://DOMAIN/hello&x=12">click me</a>
复制代码
用户点击后application state就变成了 method=get,  uri=hello, query=x=12。。。
*

  1. <form method="post" action="https://DOMAIN/world">
  2.   x: <input name="x" value="12" />
  3.   <input type="submit" value="post" />
  4. </form>
复制代码
用户提交后,application state就变成了 method=post, uri=world, scheme=https。Header里, Host=DOMAIN, Content-Type=application/x-www-form-urlencoded, 而body里有 x=12。。。
* 如果返回的status=401,并且header里带上WWW-Authenticate就会会引导浏览器向用户索要用户名与密码, 并且继续产生请求。
之后application state的header里就会多一个Authentication。

诸如此类。。。

不清楚token是打算做什么。。。 不过可以顺带吐槽一下REST。。。 尤其是关于登入的问题。。。
It doesn’t really matter which authentication mechanism I choose since I’m not actually implementing this service, but let’s say I go with the simplest choice: HTTP Basic authentication.
- RESTful Web Services

我去, 一句话就把难题给绕过去了。。。
作为Web Services,其实Basic authentication+HTTPS也没什么问题。
而如果是website, user-agent是浏览器。。。 浏览器收到401后弹出的那个框是没法改的。。。  要让浏览器停止发送Authentication header(logout)是很困难的。。。
怎么实现login/logout才地道? 至今没找到答案。。。 和SQL一样, 一堆不要怎么不要怎么不要怎么, 但就是不给出解决问题的方式。。。 自己悟去。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
40 [报告]
发表于 2014-03-24 01:12 |只看该作者
回复 33# starwing83

如果将M理解为Modeling而不是Mapping,感觉问题突然就变容易了。。。
Modeling好了之后(有那些,有那些关系,支持什么操作)只要能实现出来就完备了。
Modeling是主体而存储只是实现方式。至于实现是SQL还是NoSQL都不管。甚至那个R都可以淡化。。。 直接是OM。

而如果是Object-relational mapping呢?比如前面说的那个:

  1. UPDATE 账户 SET 积分 = 积分 * 1.10;
复制代码
或者CU鼓励新用户,只为积分小于1212的用户加分:

  1. UPDATE 账户 SET 积分 = 积分 * 1.10 WHERE 积分 < 1212;
复制代码
这该怎么映射回Object里去?选出所有(或者积分小于1212的)用户,增加10%的积分,然后又挨个存回去?
有ORM库能直接映射到上面两种UPDATE语句么?

relational database本身描述的是集合。。。 SELECT,INSERT,UPDATE,DELETE操作的也是集合而不是独立的Object。。。
即使有上面说的库,它还叫ORM么。。。

而且SQL还有一些很复杂的语句。。。
子查询,查询的交、并、差集,将查询的结果用来INSERT/UPDATE/DELETE,Common Table Expressions...
又该怎么映射回去?

相比映射回去的方法,更关心的是这种方法到底是否存在。。。我很怀疑它不存在。。。
如果真的不存在也不用纠结了。只要不是让简单的问题更简单让复杂的问题更复杂,能满足:
* 快速解决80%的问题
* 为剩下的20%留有后路
就行了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP