Stonelee's Blog

如无必要,勿增实体

Underscore源码研究之多种编程范式实现

分享到: 更多

Underscore提供了函数(functional)和对象(object-oriented)两种编程范式,使得其API调用既灵活又直观,可以说是整个类库架构中最为精华之处。

两种调用方式

函数式

1
_.map([1, 2, 3], function(n){ return n * 2; });

对象式

1
_([1, 2, 3]).map(function(n){ return n * 2; });

源码分析

调用入口

1
2
3
4
5
6
7
8
var _ = function(obj) {
  //如果参数为_对象,说明已经实例化过了,所以直接返回
  if (obj instanceof _) return obj;
  //如果实例化时没有使用new,那么在这里包装一下,使得this指向该实例
  if (!(this instanceof _)) return new _(obj);
  //将obj保存在内部属性_wrapped中
  this._wrapped = obj;
};

具体功能实现没什么好说的,直接定义为_的方法,因此可以进行函数式调用。 需要注意的是第一个参数为obj,在对象式调用时会省掉。

1
2
3
4
5
6
7
8
9
_.map = function(obj, iterator, context) {
  var results = [];
  if (obj == null) return results;
  if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
  each(obj, function(value, index, list) {
    results[results.length] = iterator.call(context, value, index, list);
  });
  return results;
};

重头戏来了。将方法附加到_的prototype上,实现对象式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//此方法暴露出来,供自定义扩展使用
_.mixin = function(obj) {
  each(_.functions(obj), function(name){
    //定义为_的方法,供函数式调用
    var func = _[name] = obj[name];
    //定义到prototype上,供对象式调用
    _.prototype[name] = function() {
      //将实例化时传递给构造函数的对象作为第一个参数
      var args = [this._wrapped];
      push.apply(args, arguments);
      //判断是否要链式调用,见下文分析
      return result.call(this, func.apply(_, args));
    };
  });
};
//将之前定义到_上的方法附加到_的prototype上,实现对象式调用
_.mixin(_);

链式调用

两种编程范式都可以实现链式调用,与普通调用的区别是需要使用chain来声明,最后使用value来取得最终结果。

函数式

1
2
3
4
5
_.chain([1, 2, 3]).map(function(n) {
  return n * 2;
}).reduce(function(memo, n) {
  return memo + n;
}).value();

对象式

1
2
3
4
5
_([1, 2, 3]).chain().map(function(n) {
  return n * 2;
}).reduce(function(memo, n) {
  return memo + n;
}).value();

链式调用实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//判断是否要链式调用
var result = function(obj) {
  return this._chain ? _(obj).chain() : obj;
};

//函数式链式调用
_.chain = function(obj) {
  return _(obj).chain();
};

_.extend(_.prototype, {

  // 标记为链式调用,这样返回值就为this而不是计算得到的结果
  chain: function() {
    this._chain = true;
    return this;
  },

  // 取得最终的结果
  value: function() {
    return this._wrapped;
  }

});

链式调用的调试

链式调用非常方便,但是要调试中间步骤的结果比较困难,因此提供了tap方法

1
2
3
4
5
6
7
_([1, 2, 3]).chain().map(function(n) {
  return n * 2;
}).tap(function(obj) {
  console.log(obj);
}).reduce(function(memo, n) {
  return memo + n;
}).value();

源码实现

将中间过程的obj作为回调函数的参数进行处理,最终依然将其返回

1
2
3
4
_.tap = function(obj, interceptor) {
  interceptor(obj);
  return obj;
};

我的2012

分享到: 更多

编程

2012,充实的一年。

这一年我的工作重心完全偏向了前端开发,技术上感觉得到了很大的提升。

JavaScript已经成为我最熟练的开发语言,真心感受到js编程的便利和舒适。 譬如其灵活的原型继承,譬如其优雅的函数式编程,都让我对其爱不释手。

js方面不敢妄称精通,但是应该已经达到了熟练的程度。平常已经遇不到什么费解的难题,需要的就是研究更多的优秀类库源码,以及在不同浏览器中进行不断的开发实践。

css方面可以说已经入门,对于基本样式可以想到至少两种实现方式,在实现之余还能考虑一下浏览器兼容,后期扩展等问题。当然这种程度还只能算得上入门而已,还需要在实践中不断练习,积累更多的经验。

html方面没有什么大的进展,还是只能使用基本的标签,希望来年能有所提升。

设计,交互什么的恐怕目前还是我的软肋,希望来年能有所提升。

作为一名码农,编程是我的职业,也是我的爱好。

有同事问我晚上回家不带电脑,想编程的时候怎么办。 我说很简单啊,家里有个娱乐用的笔记本,想编程了,打开Chrome浏览器,F12开控制台,代码什么的随便写丫。 根本不用理会开发环境配置运行库安装什么滴。

平常上网看到精美的页面,第一时间想到的是这个css该怎么实现,然后开控制台瞅瞅是不是有什么特别的。 LP说,看你整天盯着淘宝首页,干嘛呢?哈!看它的css结构,嗯嗯,有点启发~

当年选择python,貌似也是基于这种考虑,打开命令行随写随运行。现在使用js更加方便,随便找台电脑就可以。

工作

2012,渐进增强的一年。

对前端架构的选择,经历了一个转变的过程:

1
Ext JS -> jQuery UI -> Arale -> 新前端框架

年初参与了一个信息化项目,当时的情况是工期非常紧,工作量非常大,人员非常少,开发水平非常一般,包括负责前端技术研究的我都是半吊子的新手。 在没办法的情况下,使用了Ext JS作为前端开发框架,使用MVC分离耦合,为快出成果大量使用其中的界面控件,并且根据项目需要扩展了几个控件,最终算是按期交出了东西。 但是在使用过程中发现问题很多。最突出的问题是用户反映操作各种慢,然后是代码各种乱,然后是调试各种不顺,系统扩展各种困难。 Ext JS这种一揽子方案恐怕不是银弹!

接着我被抽调参与另一个项目,原项目组之前已经开发过类似项目,但是前端代码写的比较杂乱,不太成体系,因此让我进行一下封装,以方便后续开发。 这次我吸取了上次的教训,直接使用jQuery,从最简单的功能入手,仿照jQuery UI的做法,仅仅用了两个星期就将所有控件封装完毕,然后交给业务开发同事使用。 结果反响还不错,界面响应速度比较快,开发也相对简单容易。不足之处就是调用方法有些别扭(所有控件都以jQuery插件形式提供),this指针到处都u是搞得人头大(初次尝试开发jQuery插件,许多用法不够优雅,比较山寨)。

紧急项目完成了,然后接着做去年的SVG项目。在此期间接触了不少优秀的源码,如SeaJS, Arale, Backbone等等。对原有项目架构的理解也更加深入,因此进行了几次重构,扩充了功能。

在阅读源码,跟随大牛的过程中,我对js模块化、版本控制、测试集成、前端工作流等都有了进一步的认识,因此萌生了开发适合部门业务发展需要的自有前端框架的想法。 简单说来,就是从我们已经做过的项目入手,抽象最基本模块和功能,自己封装控件,然后供业务开发人员调用,并根据实际业务需求不断完善。这样开发出来的模块,因为从底层封装所以高效灵活,因为功能针对性强所以精简易懂。 目前已经完成大部分组件的开发,等待来年项目的检验。

生活

2012,幸福的一年。

7月领证,9月结婚,然后云南蜜月,北京宴请同学。 完成了人生的一件大事,也总算给了相恋七年的LP一个承诺。

喂!工作狂!留一点时间给家庭喽!

做好时间管理,提高工作效率,少做无用之事,腾出时间来陪伴爱我和我爱的人。

2012年 读书14本

虽然读得不算太多,但是尽量读好书,简单罗列如下

技术:

  • 《JavaScript权威指南》对js知识查缺补漏
  • 《JavaScript Web Applications》前端MVC实现
  • 《精通CSS》对css有了全局的认识,入门好书;
  • 《七周七语言》接触更多的编程范式,开阔视野;
  • 《Pro Git》让我更加放心的使用Git,更自如的加入到Github这个乐园中;
  • 《The Definitive Guide to Django》不能忘了后端老本行,django还是挺好用的
  • 《松本行弘的程序世界》,《Ruby元编程》入手ruby
  • 《Hacking Vim 7.2》对每天都在使用的编辑器不了解的多一些怎么对的起它?
  • 《数学之美》了解一下基础知识

社会科普:

  • 《我們最幸福》
  • 《寻路中国》
  • 《青花鱼教练让男人拥有“王”字腹肌》
  • 《大象为什么不长毛》

希望

2013年可以期待的有什么呢?

  1. 在技术上能有更大的突破。js,css,html能够融会贯通,搭建自己的前端框架;建立部门的前端开发测试部署维护流水线;向部门同事分享前端开发心得体会,共同成长;关注移动平台发展,做几个演示Demo。
  2. 保持身体健康,养成定期健身的习惯。
  3. 工资增长能不能跟上物价和房价?我还是相信先尽好人事,然后其他的再说吧~

构建基于spm的前端开发流水线

分享到: 更多

seajs 是由支付宝玉伯开源的一个浏览器端的js模块加载库,最近的项目中一直在使用,确实是解决js代码组织的利器。

seajs解决的是模块书写和加载问题,而另一个开源项目spm则解决了模块打包和部署的流程问题。

这几天抽时间看了下,感觉还不错,对我们自己的前端开发流水线构建有很大启发,记录试验过程如下。

服务器私有源配置

nginx中配置私有源路径为/modules

1
2
3
4
location /modules {
  alias       "/home/vboxadmin/lxd/dev/static";
  autoindex   on;
}

创建私有源目录

1
mkdir -p dev/static/

在目录中配置config.json

1
2
3
4
5
6
7
8
9
10
11
12
13
  "servers": {¬
    "dev": {¬
      "user": "vboxadmin",¬
      "pass": "vboxadmin"¬
  },¬
  "deploy": {¬
    "server": "dev",¬
    "host": "10.10.22.86",¬
    "path": "/home/vboxadmin/lxd/dev/static/"¬

这个配置提供给scp使用,从而可以使用spm deploy来部署远程服务器。 deploy中的server在servers中配置,从中配置登录远程服务器的用户名和密码。 host为远程ip或者域名,path为远程目的路径。

本地配置

创建私有模块开发目录

1
mkdir my-module

目录中放置所有私有模块共同的配置config.json

1
2
3
{
    "sources": ["10.10.22.86:8090/modules"]
}

sources为远程私有源访问路径

创建私有模块,例如kjquery

1
2
3
mkdir kjquery
cd kjquery
spm init

自动创建的package.json需要进行修改 加入 “parent”: “../config.json”,

组件开发

配置完成,在私有模块中进行组件开发。

1
2
3
4
5
6
7
|-- examples
|   `-- index.md
|-- src
|   `-- kjquery.js
`-- tests
    |-- runner.html
    `-- kjquery-spec.js
  • src中放置组件源码
  • tests中对组件进行测试,这里直接调用src中的代码
  • examples中对组件调用方式给出使用例子,调用的是dist中的代码

开发完成后

  • $ spm build 会在当前目录的dist下生成打包后的文件
  • $ spm upload 版本稳定后发布到本地库,默认为~/.spm,按照我司的情况,估计会将这个目录作为hg版本库
  • $ spm deploy 部署到远程服务器

业务开发

调用 共用组件 和私有组件,进行业务系统开发

通过config.map进行映射,从而切换开发和生产环境

当前端遇到Adblock Plus

分享到: 更多

今天将最近开发的演示程序集成到同事的业务框架中去,结果在同事的浏览器中报错:

Firebug中显示报错,但是没有具体错误提示,只显示问题所在代码

1
r.open('GET', url, true)

点进去看发现其上下文只是一般的ajax调用而已,why?

排错

浏览器问题?

最初怀疑跟浏览器有关,我测试使用的是chrome和firefox14,而同事使用firefox15. 于是在测试服务器上安装firefox15,木有报错…

调用库代码问题?

报错的代码属于seajs,google搜索关键字无果,更新到最新版本还是没效果

冷静思考

一共有5个tpl模板文件需要加载,此处加载了4个,出问题的为track.tpl. 于是怀疑模板文件是否格式有问题,难道有未闭合的html标签? 反复对照查看发现没有问题…

索性将调用改为其他模板,这下不再报错了,虽然模板渲染还是有问题。

难道是文件编码问题?隐藏字符?还是文件内容有瑕疵?

猛然想到,同事使用firefox多年,而测试服务器是新安装的纯净版,难道是插件造成的问题?

原因

Firefox插件Adblock Plus的过滤列表ChinaList+EasyList中 有规则:

1
|http://*/track.

导致模板文件track.tpl被屏蔽,从而ajax加载该文件失败

因此解决方案是将该文件改名为tracks.tpl

苦逼前端,除了应付各种浏览器兼容问题,尼玛还要应付浏览器插件兼容!!! 摔!!!

Bootstrap Modal源码分析

分享到: 更多

要分析的代码见: bootstrap-modal

整体结构

用匿名函数将代码整个包裹起来,防止全局变量污染。

1
!function ($) {}(window.jQuery);

但是传递的参数为window.jQuery,这个在使用seajs封装时有点小问题,为了迁就它,不得已只好将jQuery暴露出来。

类定义

提供插件的基本逻辑

1
2
3
4
5
6
7
8
9
10
//定义对象
var Modal = function (element, options) {}

//在prototype中定义对象方法
Modal.prototype = {
    //constructor重新设置原型链,使得Modal.prototype.constructor === Modal,而不是Object
    constructor: Modal
  , show: function () {}
  , hide: function (e) {}
}

jQuery插件定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$.fn.modal = function (option) {
  //return使得方法可以进行链式调用。
  return this.each(function () {
    var $this = $(this)
      , data = $this.data('modal')
      , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
    //将内部使用对象存放到data('modal')中,可以轻松从外部获取
    if (!data) $this.data('modal', (data = new Modal(this, options)))
    if (typeof option == 'string') data[option]()
    else if (options.show) data.show()
  })
}

//使得插件默认配置项可以在插件代码外重新设置。
$.fn.modal.defaults = {
    backdrop: true
  , keyboard: true
  , show: true
}

//代码外面可以通过$.fn.modal.Constructor来获得Modal
$.fn.modal.Constructor = Modal

Data-api

在页面标签中设置data-toggle就可以提供modal调用,为页面编写者提供了最大的便利。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(function () {
  $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
    var $this = $(this)
      , href = $this.attr('href')
      , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
      , option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())

    e.preventDefault()

    $target
      .modal(option)
      .one('hide', function () {
        $this.focus()
      })
  })
})

技术难点

1
2
3
4
5
6
var Modal = function (element, options) {
  this.options = options
  this.$element = $(element)
    .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
  this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
}

上面这段代码中,

this指向的是Modal类的对象,通过new Modal()生成。 this.options和this.$element是该对象的属性。 this.hide存在于Modal的原型链上,作为该对象的方法被调用。

$.proxy(this.hide, this)使得hide中的this指向这个代码中的this,即modal对象。 如果直接在delegate中调用this.hide,会导致hide中的this指向delegate的目标对象,也就是[data-dismiss=”modal”]这个页面组件,通常是个按钮。

结尾

不得不佩服Twitter员工的水平,代码结构相当优雅清晰,有许多设计细节值得借鉴。

弄清了整体结构和技术难点,其他部分作为实现细节就简单了,对照着代码看下就好。

ExtJS 4 Mvc架构演化

分享到: 更多

从年前技术选型开始算起,使用Extjs 4已经四五个月时间了。对比之前的3.x版本,Extjs 4给了我们太多的惊喜:全新的mvc架构、强大的class体系、灵活的js加载方式、纯div布局页面。因此,项目组毫不犹豫地选择其作为前端架构进行新项目的开发。

项目初始时,我们一直遵循推荐的MVC架构:在view中配置页面控件,controller中编写业务逻辑。但随着项目的推进,模块代码越来越多,加之由于种种原因开发时间严重缩短,没有时间进行代码评审,controller中的代码灵活性过强,调用页面控件方式相对繁琐等问题逐渐暴露出来,页面基本无法复用,controller一片狼藉。

第一次重构

在用户桌面widget开发时,发现新widget与原模块中的view很像,但是在store,pagingtoolbar等方面略有差别,而controller中的逻辑非常杂乱,一个文件包含了整个模块的业务逻辑,维护和复用都相当困难。

重构结果总结如下:

  • 将controller中的业务逻辑尽量移到view中,采用js闭包实现逻辑的相对隔离,跨多view的逻辑依然放在controller中,达到维护容易和代码复用的目的。
  • 采用extend的方式简化view开发
  • 操作按钮尽量使用inline方式,便于用户使用,也有利于集成到其他view中。
  • add和edit两个界面合为一个,简化开发和维护工作量
  • store.sync后回传的数据可能与grid中的column不符,这时无法享用自动load的好处,因此应该使用model来save。grid同时使用pagingtoolbar时必须用model来save,然后load,否则pagingtoolbar不能更新。

第二次重构

业务逻辑移到view中,满足了页面间高内聚、低耦合的设计要求,但是view之间相对独立的特点导致了无法实现逻辑代码复用的问题。

经研究,可以使用mixin来实现逻辑的复用。

例如Grid可以拆分为Columns(基本列),Crud(增删改),Operate(操作),filter(筛选)等minin类,这几个类都包含了控件和业务逻辑,可以灵活配置在各个view中。

ExtJS 4打包工具

分享到: 更多

ExtJS 4采用新的MVC思路进行开发。 在项目开发阶段这种分散式的代码组织方式是很爽的,但是应用到生产环境中时为了减少服务端压力,需要对js文件进行压缩打包。 为解决这一问题,官方提供了打包工具JSBuilder, 使用方法如下:

1
2
创建jsb文件:$sencha create jsb -a http://localhost/helloext/index.html -p app.jsb3
构建:$sencha build -p app.jsb3 -d .

但在实际使用时发现,不知道是因为水土不服还是我们的项目结构不合sencha的胃口。在构建jsb3文件的时候不是完全找不到任何路径,就是缺少大量项目文件,可以说是相当鸡肋。

不如自己来吧!

经测试发现,只要按照依赖顺序,将所有文件归置到一个文件中,然后压缩,项目即可正常调用。

打包工具已经放到github上:https://github.com/stonelee/extjs4-build-tool

思路

将app下的js文件按照model, store, view, controller的顺序来创建jsb3。

如果view中使用requires加载其它view,则按照依赖顺序先行加载。

然后针对项目需求做了点修改,将app.js按Ext.application分成两部分,分别加载到最后生成的文件中。

技术点

使用方法

本工具仅仅将项目开发中自己创建的位于app文件夹中的model, store, view, controller以及用于项目整合的app.js打包成一个文件。 其它ExtJS或者ux文件还需要自己打包。 将build-tool放到app同级目录下,项目文档结构如下:

1
2
3
4
5
6
7
|app
|--controller
|--model
|--store
|--view
|build-tool
|all.js

最终生成文件:

  • app.jsb3 压缩文件路径信息
  • all-debug.js 未压缩的打包文件,供调试使用
  • all.js 压缩打包文件,生产环境使用

ps:

从开始研究Ext打包,形成思路,到最终完成完整的打包工具并应用到项目中一共用了两天时间,其中纯python编码时间也就半天时间,看来经过这半年的js编码之后,我的python还没太手生嘛哈哈。动态语言之间的学习可以互相促进?待验证~

Ext Core设计模式

分享到: 更多

参考: http://docs.sencha.com/core/manual/

Singletons 单例模式

适用于只有static method或者只需要实例化一次的类,例如应用入口类。可以隐藏私有variables和methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MyApp = function() {
  var data; //data is private and can't be accessed from outside.
  return {
      init: function() {
          console.log('init');
          //Initialize application here
      },

      getData: function() {
          return data;
      }
  };
} ();
Ext.onReady(MyApp.init, MyApp);

Observable观察者模式

Ext.util.Observable使用events实现在多个objects之间解耦

事件触发流程:state changes -> fire event -> notified

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var MyClass = Ext.extend(Ext.util.Observable, {
  constructor: function(config) {
      this.addEvents('datachanged'); //specify the events we're going to fire
  },

  update: function() {
      this.fireEvent('datachanged', this, arguments.length);
  }
});

// To subscribe to an event
var c = new MyClass();
c.on('datachanged', function(obj, num) {
  console.log(num);
});
c.update(2, 8, 'some'); //3

Composite 组合模式

将对象组与单一对象作统一处理,方便用户调用

例如使用select取得的Ext.CompositeElement跟Ext.Element具有同样的调用接口

Flyweight 享元模式

使用共有的object,避免创建额外对象,减少内存占用,适用于单行原子操作。

  • 使用Ext.fly来以享元的方式获取Element,只能一次性使用
  • Ext.get可以获取Element,然后保存以供未来使用
  • Ext.getDom获取dom node

Ext Core学习笔记

分享到: 更多

Ext Core与jQuery类似,提供了跨浏览器的js封装。 主要功能包括DOM操作,Ajax,Event,Animation,template,OO等。 作为类库它提供了更高层次的抽象,方便具体问题的解决 参考: http://docs.sencha.com/core/manual/

1
2
3
4
Ext.onReady(function() {
    Ext.DomHelper.append(document.body, {tag: 'p', cls: 'some-class'});
    Ext.select('p.some-class').update('Ext Core successfully injected');
});

Class System

  • Ext.extend 创建新class,将新的properties/functions添加到prototype中,使用superclass引用父类
  • Ext.override 用来更改原来的class
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
Person = Ext.extend(Object, {
  constructor: function(first, last) {
      this.firstName = first; //property
      this.lastName = last;
  },

  getName: function() {
      return this.firstName + ' ' + this.lastName;
  }
});

Developer = Ext.extend(Person, {
  getName: function() {
      if (this.isCoding) {
          return 'Go Away!';
      } else {
          // Access the superclass getName method
          return Developer.superclass.getName.call(this);
      }
  }
});

var p = new Developer('John', 'Smith');
console.log(p.getName()); //John Smith
p.isCoding = true;
console.log(p.getName()); //Go Away!

prototype中的会被share,特别要注意{}会被多个子类共享修改,小心!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MyClass = Ext.extend(Object, {
  // This object literal is now shared between all instances of MyClass.
  baseParams: {},

  foo: function() {
      this.baseParams.bar = 'baz';
  }
});

Ext.onReady(function() {

  var a = new MyClass();
  var b = new MyClass();

  a.foo();
  // Modifying baseParams in a affects baseParams in b.
  console.log(b.baseParams);
});
  • apply 复制所有property
  • applyIf 对于重复的property则不复制
1
2
3
4
5
6
7
8
9
10
var person = {
    name: 'John Smith',
    age: 30,
    hobby: 'Rock Band'
};

Ext.applyIf(person, {
    hobby: 'Coding',
    city: 'London'
}); // hobby is not copied over

函数

函数式编程:

1
2
3
4
5
6
7
8
var sayHello = function(firstName, lastName) {
  alert('Hello ' + firstName + ' ' + lastName);
};
Ext.get('myButton').on('click', sayHello.createCallback('John', 'Smith'));
等价于
Ext.get('myButton').on('click', function() {
  sayHello('John', 'Smith');
});

TaskRunner用来固定时间间隔执行函数,例如每隔5s进行ajax请求,TaskMgr是TaskRunner的单例,用起来更方便

1
2
3
4
5
6
7
8
9
10
11
12
13
var stop = false;
var task = {
  run: function() {
      if (!stop) {
          console.log(new Date());
      } else {
          runner.stop(task); // we can stop the task here if we need to.
      }
  },
  interval: 1000 // every 1 seconds
};
var runner = new Ext.util.TaskRunner();
runner.start(task);
1
2
3
4
5
6
7
8
9
var stop = false;
Ext.TaskMgr.start({
  run: function() {
      if (!stop) {
          console.log(new Date());
      }
  },
  interval: 1000
});

DelayedTask 延时触发事件,如果在延时时间内触发两次事件,则后一次事件会覆盖前一次的触发

1
2
3
4
5
6
7
8
var task = new Ext.util.DelayedTask(function() {
  console.log(Ext.getDom('myInputField').value.length);
});
// Wait 500ms before calling our function. If the user presses another key 
// during that 500ms, it will be cancelled and we'll wait another 500ms.
Ext.get('myInputField').on('keypress', function() {
  task.delay(500);
});

延时函数:

1
2
3
4
5
6
7
8
var task = new Ext.util.DelayedTask(function() {
  console.log(Ext.getDom('myInputField').value.length);
});
// Wait 500ms before calling our function. If the user presses another key 
// during that 500ms, it will be cancelled and we'll wait another 500ms.
Ext.get('myInputField').on('keypress', function() {
  task.delay(500);
});
1
2
3
4
var whatsTheTime = function(){
    alert(new Date());
};
whatsTheTime.defer(3000); //Wait 3 seconds before executing.

格式

urlEncode、urlDecode将object-literal与GET request parameter(key1=value1&key2=value2)互相转化

1
2
3
4
5
var params = {
    foo: 'value1',
    bar: 100
};
var s = Ext.encode(params); // s is now foo=value1&bar=100

encode、decode将object与json互相转换

1
2
3
4
var s = Ext.encode({
    foo: 1,
    bar: 2
}); //"{"foo":1,"bar":2}"

字符串格式化:

1
2
3
4
5
var s = String.format(
   'Hey {0} {1}', how are you?', 
   'John', 
   'Smith'
);

DOM

DomQuery提供了高性能的selector/xpath处理,可以应用于html和xml,主要使用select函数,可以使用get预先获取Root来进一步提升性能

  • 多重选择 Ext.select(‘div.foo, span.bar’);
  • 链式选择 Ext.select(‘div.foo[title=bar]:first’);
  • 子选择 Ext.select(‘div span’);
  • 直接子选择 Ext.select(‘ul > li’);
  • 属性选择 Ext.select(‘a[href=http://extjs.com]’);
  • 有alt tag Ext.select(‘img[alt]’);
  • 伪类 Ext.select(‘div:next(span.header));
  • css值选择 E{display=none} //css value “display” that starts with “none”

DomHelper动态生成html片段:

1
2
3
4
5
6
7
8
9
Ext.DomHelper.append(document.body, {
    id: 'my-div',
    cn: [{
      tag: 'a',
      href: 'http://www.yahoo.com/',
      html: 'My Link',
      target: '_blank'
    }]
});

Ext.Element

浏览器加载页面时,将每个tag解析为HTMLElement,建立DOM,保存到全局变量document里。使用getElementById来获取每个HTMLElement Ext.Element屏蔽DOM操作的浏览器差异,功能包括:

CSS & Styling(setStyle, addClass)

  • radioClass实现单选效果,同类中有同样class则remove,否则add
  • toggleClass切换

Dom Querying or Traversal(query, select, findParent)

is 测试element是否符合selector

1
2
3
4
5
6
7
8
9
10
11
12
//向上找parent:
Ext.fly('elId').findParent('div'); //returns a dom node
Ext.fly('elId').up('div'); //返回Ext.Element

//查找
Ext.fly('elId').select('div:nth-child(2)'); //返回CompositeElement
Ext.fly('elId').select('div:nth-child(2)', true); //返回Array of elements
Ext.query('div:nth-child(2)'); //返回Array of dom nodes

//返回Ext.Element,第二参数为true则返回dom node
Ext.fly('elId').child('p.highlight'); //任意深度的single child
Ext.fly('elId').down('span');  //直接single child

还有parent(),next(),prev(),first(),last()

Dom Manipulation(createChild, remove)

使用id或Ext.Element或dom node或selectors或CompositeElement

1
Ext.fly('elId').appendTo('elId2');

使用DomHelper config

1
2
3
4
Ext.fly('elId').insertFirst({
  tag: 'p',
  html: 'Hi I am the new first child'
});

使用Html Fragments

1
Ext.fly('elId').insertHtml('beforeBegin', '<p>Hi</p>')

包括beforeBegin, beforeEnd, afterBegin, afterEnd

Event Handling

Ext.EventObject提供了跨浏览器的event实现,例如按键判断,event-propagation ,preventDefault()

1
2
3
4
5
el.on('click', function(e,t) {
  // e is a normalized event object (Ext.EventObject)
  // t the target that was clicked, this is an Ext.Element.      
});
el.un('click', this.handlerFn);

Event delegation是利用冒泡机制在container中注册event,而不是在每个元素中注册,这样可以减少内存消耗,防止内存泄漏,更好地集中管理event

1
2
3
4
5
6
7
8
9
10
Ext.fly('actions').on('click, function(e,t) {
 switch(t.id) {
     case ''btn-edit':
      // handle the event
      break;
      case 'btn-delete':
      // handle the event
      break;
  }
});

configuration option:

1
2
3
4
5
6
7
8
9
el.on('click', function(e,t) {
    // handle click
}, this, {
    delegate: '.clickable',// will filter target to be a descendant with the class 'clickable'
    single: true, // 只响应一次,然后自动删除
    buffer: 1000, // 延时1s,如果在时间间隔内多次触发会覆盖
    delay: 1000, // 延时1s,不会覆盖
    target: el.up('div') // only handles the event when it has bubbled up to the first 'div'.
});

hover

1
2
3
4
5
6
7
function over(e,t){
    Ext.get(t).toggleClass('red');
}
function out(e,t){
    Ext.get(t).toggleClass('red');
}
Ext.fly('myButton').hover(over, out);

Dimensions(getHeight, getWidth)

可以添加动画configure或直接true

1
2
3
4
5
6
7
8
9
// changes the height to 200px and animates with default configuration
Ext.fly('elId').setHeight(200, true);

// changes the height to 150px and animates with a custom configuration
Ext.fly('elId').setHeight(150, {
    duration : .5, // animation will have a duration of .5 seconds
    // will change the content to "finished"
    callback: function(){ this.update("finished"); }
});
1
2
3
4
5
6
7
8
9
10
11
getPadding('lrtb');//返回四个方向padding之和

//同时得到x,y坐标
var elXY = Ext.fly('elId').getXY() //elXY is an array
Ext.fly('elId').setXY([20,10])

//得到相对位置
var elOffsets = Ext.fly('elId').getOffsetsTo(anotherEl);

//设置位置
setLocation(15,32)

clearPositioning,getPositioning,setPositioning用来存储恢复position

Animations

1
2
3
4
5
6
//共八个方向,'r'表示朝右边滑动
Ext.fly('slideEl').slideOut('r', {
  callback : function(){
      alert('Finished sliding the element out');
  }
});

恶搞jquery

分享到: 更多

有人使用jquery来get服务端数据吗? 相信略微有点功能的jquery程序都会使用把。

trick

前提确定,恶作剧开始。 将下面代码插入任意js文件的可执行位置:

1
2
3
Object.prototype.fuck=function(){
  alert("fuck u!!!");
};

效果

在我的系统中每隔5s向服务端请求一次数据,因此每隔5s会弹出一个温馨的祝福框。

原因

查看console没有任何异常啊,why?? 将那个可爱的alert换为console.log,发现console出现了位置定位jquery-1.7.js (line 7611),点过去看看:

1
2
3
for ( i in { success: 1, error: 1, complete: 1 } ) {
  jqXHR[ i ]( s[ i ] );
}

jquery作者肿么了,object的for循环竟然没用hasOwnProperty来判断一下!! 然后看了下周围的代码,也都是一样滴!! 为了代码的简洁吗?为了运行的效率吗?基于用户的善意考虑吗?在js的prototype继承机制上,一切都是浮云~

如此明显的bug估计在jquery看来是有意为之的,但是为防止自己的代码被问候,建议自己的代码按照这样的方式书写:

1
2
3
4
5
6
var obj={name:"tom",age:13};
for (var i in obj){
  if (obj.hasOwnProperty(i)){
      doSomething(i);
  }
}

这也是每本专业js书里的推荐写法。

ps

如果有人用这种方法捣乱,文件夹搜索Object.prototype即可。