看书真的是件很有收获的事,特别是看好书。在看一些有思想的作者的书的时候,你能得到的不仅仅是一些语法api之类的,你能理解到事情的前因后果。除此之外,还会有很多作者自身的一些观点,针对我们实际工作中经常会遇到的问题,大师们的观点是盏明灯,吸收这些观点,往往是比语法更大的收获。也许你见不到大牛,也许你一辈子没法跟他们面对面交谈,也许你的周围全是些不喜欢技术或者在你看来技术过于落后的人,没关系,读书吧,挑本好书,读书的过程本身就是同他们交谈。
想从今天起,以后多收录一些大师们的语录。
“ 要是看到你在模板中写这种代码,很多人会怒不可遏。别理他们——他们都是教条主义的受害者。在模板里写代码没有任何不对,只要别写太多(尤其是别把业务逻辑放进模板)。”
—— David Heinemeier Hansson 评论mvc模式中,v中带有少量逻辑判断
摘自《应用rails进行敏捷Web开发》中文版 第343页
【感想】:看到这句话的时候,我相当有感触,不仅仅是对mvc模式,而是更进一步的“教条主义”。我发觉“教条主义”是件非常普遍的事,哪个领域都存在这种现象,很多很多的工程师死守着教条在做事,好的坏的用一刀切的形式去判断,往往这种人不在少数,还非常地固执。这似乎是个顽疾,起初在遇到这种工程师我还试图想去跟他们解释,但发现这种解释要花相当大的精力才有可能成功。
教条主义其实是观点片面造成的结果,一方面可能是实战经验不够,另一方面可能是对“教条”的起因不明,或者干脆是人云亦云缺乏自己思考造成的结果。比如说在早期的重构中,很多人就坚持不用table,非要使用div来模拟table,这就是非常典型的教条主义。虽然table的问题现在已经广泛解决了,绝大多数工程师都有了可以合理使用table的意识了,但教条主义仍然没有就此结束。很典型的就是“页面是否一定要通过w3c验证”,我一直觉得w3c的验证不过是促进web标准化推进的手段,它本身不是目的。web标准的目的是1)让标签语义化; 2)结构、样式和行为分离。这是表象,更进一步,为什么要让标签语义化啊?因为标签语义化有助于seo,有助于特殊终端在无css支持的情况下仍然让网页有很好的可读性。为什么要让结构、样式和行为分离啊?因为分享结构样式和行为有助于提高代码的可维护性。制定web标准的验证,不过是对我们的重构代码进行一次评估,提供一个标准的模板供你去匹配,它是种一刀切式的判断。一定要完全通过w3c验证才算是好的页面吗?这个叫本末倒置。我们的目的是seo、去样式可读还有代码的高可维护性。只要实现这个目标,是否通过验证其实并不重要。死守“通过w3c验证”其实就非常的“教条主义”了。
另外,还有mvc,css sprite,oo编程等等等等,很多方面都需要根据具体情况来决定怎么做,比如说我个人就觉得小型网站使用css sprite非常不值得,可维护性下降了许多,开发成本提高了很多,换来的好处却非常有限,你本身流量就不大,减少http请求数有什么价值?不是说流行的就一定是好东西,一定要知道这东西为什么流行,它能解决什么问题,然后权衡一下得失再做决定是否使用。
“要是看到你在模板中写这种代码,很多人会怒不可遏”,这的确是教条主义者们的典型写照,教条主义者们通过靠“坚持死守某个教条”来证明自己“很专业”,这其实是非常苍白的自我保护,很没劲,自己开发会很辛苦,和你一起配合的其它工程师也会非常辛苦。
“别理他们——他们都是教条主义的受害者”。是的,别理他们,他们是受害者,我们没必要跟他们大费周章地解释为什么我要这么做,为什么我没有完全遵照教条,只要认为你自己的处理非常合理,你完全可以不受他们的思想所左右,死守教条的他们才是受害者,他们会为死守教条而付出“额外的开发成本”,“可维护性下降”或者别的代价。这些代价是真的,教条救不了他们。当然,如果你非常不幸地和教条主义者共事,那么你不得不向它们解释你的视角,我知道这个过程是非常艰险的,但没办法,和人打交道的确比和代码打交道困难得多。
在webrebuild北京站上做的一次分享,总结了一下javascript的分层概念。
这里特别推荐一下YUI3给我们带来的分层概念:
1)底层(框架提供):封装DOM和Event相关操作,提供跨浏览器兼容的接口,扩展原生javascript语言本身不提供的但又特实用的接口,例如namespace;
2)抽象类层(框架提供):提供类的抽象层,用于统一框架组件层,自定义组件层和应用层中所有类的格式,例如统一初始化方法,毁灭方法,属性如何定义,监听事件在什么方法里等等等等;
3)通用组件层(框架提供):依赖底层的接口,继承自抽象类的抽象类,提供通用型组件,和项目具体需求没有直接关系,也就是说,不是定制型的组件。
4)自定义组件层(开发工程师提供):根据项目需求,定制型的组件,它依赖底层和通用组件层,继承自抽象类层,是根据具体项目进行的封装,定制型组件。
5)应用层(开发工程师提供):这一层是和页面具体需求相关的,面向应用的。用mvc来划分的话,前面四层全是m,这一层是c。它调用前面四层提供的m,集中精力在应用逻辑上。这一层如果需要自定义类,也需要继承自抽象类层。
ppt见:http://www.adanghome.com/share/javascripthierarchical.ppt
1)rails自动生成的目录中,lib目录和vendor目录分别用于存放你自己编写的组件或者第三方提供的组件,它们需要被你的应用程序用到,但又不专属于你的项目。log目录下存放的是日志文件,日志文件会针对开发环境,测试环境和生产环境分别记录。config目录是用来设置rails的配置的,其中包括用于配置服务器链接的yml文件和运行时环境配置文件等。运行时环境配置可以针对三个环境分别进行设置,公共的部分在config根目录下的environment.rb文件中设置,而针对某个环境的设置,则在config/environments/目录下的development.rb、production.rb、test.rb中进行个性设置。
2)在启动web服务器时,可以通过加上-e选项,指定启动时选用哪个环境
==================================
ruby script/server -e development | test | production
==================================
3)rails里强调“惯例重用配置”,从数据库中的表,到控制器,到模型,再到视图,命名是有关联的!文件的存放路径也是有关联的。我们在命名和存放文件时需要遵守这种惯例。一旦我们遵守了这种惯例这四者之间的映射都是rails自动替我们去完成的,这也是rails为何能“敏捷”的重要原因。
4)在编写普通ruby代码时,如果我们想要使用另一个文件中的类和模块,需要先require它们,但在rails中编程,因为rails有命名约定,所以可以不用require它们就直接使用,rails会根据命名约定(也包括路径约定),去自动加载该文件!不过,也有例外的时候,如果被引用的类要在session中保存,自动加载就会失效,但即便如此,我们也仍然不需要require,我们只需要在类里加上一句 model :类文件名(注,小字加下划线分隔)。
==================================
class StroeController < ApplicationController
model :line_item #rails会去加载line_item.rb文件,其中包含LineItem类。
==================================
5)控制器的路径默认是存放在app/controllers根目录下的,但如果我们需要在根目录下再组织一下控制器的路径,那么可以将控制器的声明位于ruby模块之下。例如,我们要将book_controller.rb文件放置于admin目录下,也就是app/controllers/admin/book_controller.rb,那么,我们就应该这样声明:
==================================
class Admin::BookController < ApplicationController
..
end
==================================
幸运的是,rails的生成器很智能,你可以直接使用
==================================
ruby script/generate controller Admin::Book action1 action2
==================================
来生成你需要的路径。
6) rails用ActiveRecord来处理对象-关系映射(ORM)。数据库中的一张表对应着模型中的一个类,表名是小写的,多单词用下划线分隔,而且是复数形式。对应的类名是单词首字母大写来进行分隔的,而且是单数形式。表中的字段直接映射成类的属性,除此之外,我们还可以给类再另外添加一些属性。表名和类名是通过命名惯例去自动对应的,(rails对ruby的字符串类进行了扩展,让它直接支持单复数之间的转变,例如 puts “cat”.pluralize #=>cats puts “cats”.singularize #=>cat),仅管pluralize和singularize方法已经很智能了,能处理child和children这样的单复数转换,但它仍然还是有缺陷,例如sheep会对应到sheeps。我们可以在模型类中,cefpset_table_name去显示地指明对应的表,而不使用缺省的惯例。
==================================
class Sheep < ActiveRecord::Base
set_table_name “sheep”
end
==================================
7)ActiveRecord从表中取出值,变成类的对象时,会自动将数据库中的类型转换成ruby支持类型,例如数据库字段是timestamp类型的,就会返回Time对象。如果你希望得到一个属性的原始值,可以在属性名称后面加上_before_type_cast。
==================================
account.balance_before_type_cast
==================================
如果在模型内部,可以使用私有方法read_attribute()和write_attribute()。
8)布尔型的数据在转换的时候会有些麻烦,有些数据库是不支持布尔型的,所以我们在工作中,可以使用0,1这样的数字来代表布尔值的真假,而ruby中只有false和nil为假,0其实是为真的。所以在数据库映射到类时,我们要判断某个属性的真假时,记得要加一个?号,这个是rails为解决布尔型问题而设置的。
==================================
# 错误写法
if user.superuser
doSomeThing
end
# 正确写法
if user.superuser?
doSomeThing
end
==================================
9) 在编程时,可以某个属性对应的是一个对象,而不是一个简单类型的数据。在类和表的映射的时候,这种复杂类型的属性也是可以直接保存到表中的一个字段的,这个字段必须是text类型的,属性会以字符串的形式存进数据据。而从数据库中映射到类时,需要在类中加以声明,某个字段是个对象,不是普通字符串。声明的方法是serialize()。复杂类型包括对象,数组,hash等等。
==================================
class Purchase < ActiveRecord::Base
serialize :last_five
…
end
==================================
10)主键。一般来说,rails都会用id作为每张表的主键,但表并不是由我们自己来建的,可能表会用别的字段作为主键。我们可以用set_primary_key来显示地指定主键字段。
==================================
class BadBook < ActiveRecord::Base
set_primary_key “isbn”
end
==================================
但需要注意的是,既然指定主键为其它字段,但在访问时,仍然可以通过名为id的属性来设置主键值,也就是说,只要使用ActiveRecord,主键字段在对象中的属性名称就永远都是id。
1) 在windows系统上安装rails很简单,首先需要安装ruby,完整安装,然后先在命令行输入gem –version,如果显示出版本号,就可以直接使用RubyGems来安装rails。直接在命令行输入 gem install rails –include-dependencies就可以自动完成安装了。需要注意的是,在windows系统上配置ror环境,还会出现一些奇怪的问题,具体安装过程推荐一篇博客:http://hi.baidu.com/haoweiren1234/blog/item/1d84b53875b8ea2cb9998f79.html,博主讲解得非常清楚。
2) ruby和其它服务器端语言一样,可以运行动态脚本,格式是<% %>。如果想输出显示,可以使用<%= %>。ruby的服务器使用的是WEBrick,不是appache,不是IIS,不是tomcat哦。
3)用rails新建一个项目很简单,在命令行输入 rails xxx,xxx就是项目的根目录文件夹名。然后rails会为你自动创建好目录结构,并提供好部分代码。rails提倡的敏捷开发,很大意义上依靠于这种带有“强制性”的自动操作。rails会为你建好目录,搭好框架,我们需要做的,只是填充框架里的内容。不用置疑rails自动搭建的框架是否优秀,是否弹性十足,是否可维护性良好。rails的敏捷开发目标,目的就是为了搭一个最优势的框架。
4)新建项目之后,在命令行输入 ruby script/server,运行rails自动为我们生成的代码中的server脚本,就可以启动WEBrick服务器了。
|
String 类
1)单引号和双引号。 单引号不会对里面的字符进行转义,类似于”\n”这样的转义会直接输出,也不能包含表达式。双引号则可以包含转议字符,也能包含表达式,表达式的格式为 #{xxxx}。需要注意的是,如果单引号里的字符串也包含单引号,还是要用“\’”进行转义的,这个转义是支持的。
=======================
a = “abc”
b = “1\n2\n3\n#{a}”
c = ‘1\n2\n3\n#{a}’
puts b #=> 1 2 3 abc
puts c #=> 1\n2\n3\n#{abc}
=======================
2) 新建字符串。
<1> 直接使用 “”和”生成字符串。
<2> 使用String.new()生成字符串。
<3> 使用%Q和%q。 如果字符串中包括”"或”时,不想使用\”和\’频繁转义,可以使用%Q和%q来生成字符串。其中%Q相当于”,%q相当于’。和%w一样,分界符可以用<> @@之类的。
=======================
%Q(1\n2\n3\n#{a}”5678″‘9012′)
%q<1\n2\n3\n#{a}’5678′”9012″>
=======================
<4> 如果是多行文本,可以使用<<”xxx” 或 <<-”xxx”来实现多行。一般xxx多使用EOB或EOF,表示end of black和end of file。注意,如果是xxx可以用单引号或双引号引起来,分别代表单引和双引,也可以不带引号,默认代表双引。注意<<和xxx之间要连在一起,不要有空格。
======================
a = 123
s = <<-”EOB”
i : abc
j : #{a}
adang is me
EOB
print s #=> i : abc
j : 123
adang is me
======================
3) 字符串的长度。 字符串的长度可以用str.size或者str.length来获得。如果是英文字符,那么可以正常返回字符个数,但如果是非英文字符,比如说是中文字符,size返回的值就不正确了。因为ruby中返回的是字节数,不是字符数,这点和js不同。如果是中文,跟编码格式有关,如果是utf-8编码,那么一个中文占用三个字节,如果是ANSI编码,那么一个中文占用两个字节。为了解决这个问题,可以借助于正则表达式,换个思路获得字符串的字符长度。例如utf-8编码的,用str.split(//u).length,ANSI编码的,用str.split(//e).length。
=======================
utf-8编码
a = “我是个中文abc”
puts a.size #=> 18 (5X3+3 = 18)
puts a.split(//u).length #=> 8 (5+3 =
ANSI编码
a = “我是个中文abc”
puts a.size #=> 13 (5X2+3 = 13)
puts a.split(//e).length #=> 8 (5+3 =
=======================
4) empty? ruby中字符串提供了empty?方法判断是否为空。注意,如果有空格或者tab,都不算为空的。
=======================
a = “”
p a.empty? #=> true
a = ” ”
p a.empty? #=> false
=======================
5) split和unpack。 ruby中分割字符串有两种方法,一种是split,按特定字符分隔,另一种是unpack,按“字节”数分隔。unpack的参数格式是aXaX,“X”表示要截取的字节数。需要注意提,如果X+X的字符数不够整个字符串长度的话,字符串后面的字符就没了,如果超过总长度的话,会按最大数来截取,如果不确定最后一节的长度,可以使用a*来截取至结尾处的部分。
=======================
a = “1:2:3:4:5:6″
p a.split(“:”) #=>["1","2","3","4","5","6"]
p a.unpack(“a2a3″) #=>["1:","2:3"]
p a.unpack(“a2a30″) #=>["1:","2:3:4:5:6"]
p a.unpack(“a2a3a*”) #=>["1:","2:3","4:5:6"]
=======================
6) +,<<,和concat。 ruby中字符串的连接有三个方式。用+号可以新建一个字符串,用<<和concat可以修改原字串的值(不新建字符串)。因为<<和concat是修改址的方法,影响会比较大,切忌不要乱用。
=======================
a = “123″
b = “456″
p a + b #=> “123456″
p a #=> “123″
a = “123″
b = “456″
a << b
p a #=> “123″
a = “123″
b = “456″
a.concat(b)
p a #=> “123456″
=======================
7) chomp! 。 删除行尾换行符。 以gets等方法从标准输入读入字符串时,行尾会接着换行符,但实际在处理字符串时,换行符经常很碍事,因此,加上chomp!几乎是一种固定写法。
=======================
a = “123\n”
p a.size #=>4
a.chomp!
p a.size #=>3
while line = gets
line.chomp!
..
end
=======================
8) chop和chop!。 用于删除字符串的最后一个字符。
Array 类
1)新建数组。 ruby中新建数组有多种方法。
<1> 使用[],类似于js。
================================
a = [1,2,3]
p a #=> [1,2,3]
================================
<2> 使用Array.new。
================================
a = Array.new
p a #=> []
a = Array.new(5)
p a #=> [nil,nil,nil,nil,nil]
a = Array.new(5,0)
p a #=> [0,0,0,0,0]
================================
<3> 使用%w。 %w方法很奇怪,后面接的可以直接是字符串,而不必带”号。通常情况下,可以选用()做为参数的边界符,如果字符串本身包含()这样的字符,为免造成影响,还可以使用<> || !! @@ AA这样的来做边界符。%w方法会以空格为分隔,将字符拆分成数组。
================================
a = %w(abc 123 def 456 ghi)
p a #=> ["abc","123","def","456","ghi"]
a = %w<(abc) 1(23 g()h>
p a #=> ["(abc)",“1(23”,"g()h"]
================================
<4>其它对象的to_a方法。 比如说hash对象就可以通过to_a转换成数组。
================================
h = {“name” => “adang”,”sex” => “male”}
a = h.to_a
p a #=>[["name","adang"],["sex","male"]]
================================
<5>字符串的split方法。
================================
s = “ab-123-cd-45″
a = s.split(“-”)
p a #=>["ab","123","cd","45"]
================================
2) 数组的索引。
<1> a[n]
这个用法和别的语言没啥区别,唯一奇怪的是n可以为负值,表示倒数。另外,a.slice(n)和a.at(n)效果和a[n]相同。
================================
a = ["a","b","c","d"]
p a[0] #=> a
p a[-2] #=> c
p a.slice(-2) #=> c
p a.at(-2) #=> c
================================
<2> a[n..m]
会将a[n]到a[m]之间的范围建立出新的数组返回。a.slice(n..m)和a[n..m]效果相同。
================================
a = ["a","b","c","d"]
p a[1..3] #=> ["b","c","d"]
p a[1..6] #=> ["b","c","d"]
p a.slice(1..3) #=> ["b","c","d"]
p a[a] #=> ["a","b","c","d"]
================================
<3> a[n,len]
从a[n]处开始获取len个元素,建立新数组并返回。a.slice(n,len)和a[n,len]效果相同。
================================
a = ["a","b","c","d"]
p a[1,2] #=>["b","c"]
p a[1,6] #=>["b","c","d"]
p a.slice(1,2) #=>["b","c"]
p a #=>["a","b","c","d"]
================================
<4> a.values_at(n1,n2,…)
a[n..m],a[n,len]方法新建的数组都只能是连续位置上元素,a.values_at方法可以返回不连续的索引组成的新数组。
================================
a = ["a","b","c","d"]
p a.values_at(1,3,0) #=> ["b","d","a"]
p a #=> ["a","b","c",“d”]
================================
3) 改写数组。a[n],a[n..m],a[n,len]不仅可以读数据,还能写数据。注a.at,at.slice方法都只能读,不能写,只有a[]可以写。
================================
a = ["a","b","c","d","e","f","g"]
a[1..3] = ["B","C","D"]
a[4,2] = [1,2]
p a #=> ["a","B","C","D",1,2,"g"]
================================
4) 插入元素。 ruby中插入数据原理和js里一样。js中是通过splice方法先删几个元素,再添几个元素,ruby中也是如此。
================================
a = ["a","b","c"]
a[2,0] = [1,2]
p a #=> ["a","b",1,2,"c"]
a = ["a","b",“c”]
a[1,1] = [1,2,3]
p a #=> ["a",1,2,3,"b","c"]
a = ["a","b","c"]
a[1..2] = [1,2,3]
p a #=> ["a",1,2,3]
================================
5) 交集、并集和差集。 ruby中数组还可以进行交集和并集的计算,分别使用 & 和 | 作为运算符。
================================
a = ["a","b","c","d"]
b = ["b","e","a"]
p a & b #=> ["a","b"]
p a | b #=> ["a","b","c",“d”,"e"]
p a #=> ["a","b","c","d"]
================================
对数组直接使用 + – 运算符,可以分别得到数组连接的效果和差集的效果。
================================
a = ["a","b","c","d"]
b = ["b","e","a"]
p a – b #=> ["c","d"] (a有,但b没有的元素)
p a + b #=> ["a","b","c","d","b","e","a"] (a和b简单地叠加)
p a | b #=> ["a","b","c","d","e"] (a和b的并集,重复元素去除)
================================
6)数组的队列性。 和js一样,ruby中的数据也有push,pop,unshift,shift方法,此外a << 赞同于a.push。
================================
a = ["a","b","c"]
a.unshift(1)
a << 2
a.push(3)
p a #=> [1,"a","b","c",2,3]
p a.shift #=> 1
p a #=> [“a”,"b","c",2,3]
p a.pop #=> 3
p a #=> ["a","b","c",2]
================================
7) a.concat和 + 。 在js中数组是不能直接相加的,如果需要组合两个数组,是用a.concat(b)来实现的,但js中a.concat(b)是会返回一个新数组的,还可以利用这一点轻松复制一个复杂类型的数组。但ruby中concat是具有破坏性的,会影响到a。如果想不影响到a,可以使用+号。
================================
a = ["a","b","c"]
b = [1,2,3]
c = a + b
p c #=>["a","b","c",1,2,3]
p a #=>["a","b","c"]
c = a.concat(b)
p c #=>["a","b","c",1,2,3]
p a #=>["a","b","c",1,2,3]
================================
8)a.compact。 a.compact可以从数组中删除nil元素。有compcat和compact!之分。
================================
a = [nil,"a",nil,"b","c",nil]
a.compact!
p a #=> ["a","b","c"]
================================
9)a.uniq。 a.uniq可以删除数组中的重复元素。有a.uniq和a.uniq!之分。
================================
a = [1,2,3,4,3,2,1]
a.uniq!
p a #=> [1,2,3,4]
================================
10) a.delete(x)、a.delete_at(n)、a.delete_if{|item| …}
a.delete(x)可以直接从数组中删除指定元素(为啥js中没有这么好用的方法?T_T )。
a.delete_at(n)删除指定索引的元素(js的splice方法去死吧 T_T)。
a.delete_if{|item| …} 遍历数组元素,如果区块返回结果为真,则删除。 (for循环去死吧 T_T)。
================================
a = [1,2,3,2,4,nil]
a.delete(2)
p a #=> [1,3,4,nil]
a = [1,2,3,2,4,nil]
a.delete_at(2)
p a #=> [1,2,2,4,nil]
a = [1,2,3,2,4,nil]
a.delete_if{|item| item > 2 if item}
p a #=> [1,2,2,nil]
================================
11) a.slice!()。 前面说过a.slice的效果等同于a[],包括a.slice(n),a.slice(n..m),a.slice(n,len)。slice方法还有slice!的形式,slice!方法的返回值相同,但会从数组中删除掉返回值。
================================
a = [1,2,3,4,5,6]
p a.slice!(1) #=> 2
p a #=> [1,3,4,5,6]
p a.slice!(2..3) #=> [4,5]
p a #=> [1,3,6]
p a.slice!(1,1) #=> [3]
p a #=> [1,6]
================================
12) a.collect和a.map。 如果需要让数组中所有元素统一变化,再也不用for()去循环,再赋值这么麻烦了,a.collect和a.map都可以返回让数组中的值进行处理之后,将返回的结果组成新数组。a.collect和a.map效果相同,且都有!的形式。
================================
a = [1,2,3]
a.collect!{|item| item * 2}
p a #=> [2,4,6]
a.map!{|item| item * 3}
p a #=> [6,12,18]
================================
13) a.fill。 a.fill可以将数组a的元素改写为指定的参数。但我a[n..m]=和a[n,len]=不同的是,a.fill只能将指定范围内的元素修改成一种参数(只有一种)。a.fill有a.fill(value),a.fill(value,begin),a.fill(value,begin,end),a.fill(value,n..m)几种不同的方式。
================================
a = [1,2,3,4,5,6]
a.fill(8)
p a #=> [8,8,8,8,8,8]
a = [1,2,3,4,5,6]
a.fill(8,3)
p a #=> [1,2,3,8,8,8]
a = [1,2,3,4,5,6]
a.fill(8,3,2)
p a #=> [1,2,3,8,8,6]
a = [1,2,3,4,5,6]
a.fill(8,2..4)
p a #=> [1,2,8,8,8,6]
================================
14)a.flatten。 将数组扁平化,多层数组可以变成一个单层的数组。有a.flatten和a.flatten!之分.
================================
a = [1,[2,3],[[4,5],6,[7,[8,9]]]]
a.flatten!
p a #=> [1,2,3,4,5,6,7,8,9]
================================
15)a.reverse。 逆转数组,有a.reverse和a.reverse!之分。
================================
a = [1,2,3]
a.reverse!
p a #=> [3,2,1]
================================
16)sort和sort_by。 sort对数组排序,对应的还有sort!方法。另外ruby还提供sort_by方法,sort_by返回新数组,没有对应的sort_by!
=================================
a = [2,4,3,5,1]
a.sort!
p a #=> [1,2,3,4,5]
b = a.sort_by{|i| -i}
p b #=> [5,4,3,2,1]
p a #=> [1,2,3,4,5]
=================================
17) each和each_width_index。 数组遍历最常用的做法是使用each方法,但each方法只会返回一个返回值,只有元素,没有索引,如果需要索引的话,可以使用each_with_index方法。
=================================
a = [1,2,3]
a.each{|i| print i,”\n”}
a.each_with_index{|i,n| print n,” : “,i,”\n”}
=================================
Numeric类
1) 整型和整型相加结果为整形,整形和浮点型相加,结果为浮点型。
================================
p 1+1 #=>2
p 1+1.0 #=>2.0
p 3 * 2.0 #=>6.0
================================
2) divmod。 除了+ – * / %之外,ruby中数值类型还有divmod方法,它也是除的运算符,只是返回值是数组的形式,第一项是商,第二项是余数。
================================
p 16.divmod(5) #=> [3,1]
================================
3) Math模块和round方法。 ruby提供了Math模块,但和一般语言不同的是,round、ceil和floor方法并不是从属于Math模块的,而是直接绑定在数值类上的。Math模块提供的方法有sin、cos、tan、asin、acos、atan、exp、log、log10和sqrt。数值类型的对象转换可以用 to_f、to_i方法。浮点型转整型会直接去掉小数点后的值。字符串调to_i方法相当于js中的parseInt方法。
================================
f = 4
p Math.sqrt(f) #=> 2
include Math
p sqrt(f) #=> 2
p 10.to_f #=> 10.0
p 10.8.to_i #=> 10
p “123″.to_i #=> 123
p “123abc”.to_i #=> 123
p “abc123″.to_i #=> 0 (不是nil哦)
p 1.2.round #=> 1
p 1.2.floor #=> 1
p 1.2.ceil #=> 2
================================
3) 循环。 数值类型可以直接用来循环。
================================
ary = []
3.times{|i| ary << i}
p ary #=>[0,1,2]
ary = []
2.upto(5){|i| ary << i}
p ary #=>[2,3,4,5]
ary = []
5.downto(2){|i| ary << i}
p ary #=>[5,4,3,2]
ary = []
10.step(20,3){|i| ary << i}
p ary #=>[10,13,16,19]
10.step(2,-3){|i| ary << i}
p ary #=>[10,7,4]
================================
1) sleep方法。 ruby中也有定时器,sleep相当于js中的setTimeout函数,不同的是sleep后的参数单位是秒,不是微秒。
2)ruby的函数中return是可以省略的,如果省略,方法中的最后一个语句的计算值会是返回值。
========================================
def area(x,y,z)
xy = x * y
yz = y * z
zx = z * x
(xy + yz +zx) * 2
end
p area(2,3,4) #=>52
========================================
3) 判断对象的类型。 ruby有个和js中判断对象类型相近的方法,instance_of?。除了instance_of?之外,ruby的对象还有个很好用的方法,叫class,通过class方法可以直接得到对象的类型,需要注意的是.class返回的不是字符串,而是类。
========================================
str = “abc”
arr = [1,2,3]
p str.class #=> String
p arr.class #=> Array
p str.instance_of?(String) #=> true
p arr.instance_of?(Array) #=> true
p str.class == String #=> true
========================================
和js一样,ruby中所有的类都继承自Object。但和js不一样的是,js的instanceof可以一直往上追踪,但ruby不行,ruby为往上追踪提供了另外一个方法is_a?。例如
=========================================
js:
var arr = [1,2,3];
alert(arr instanceof Array); //true
alert(arr instanceof Object); //true
ruby:
arr = [1,2,3]
p arr.instance_of?(Array) #=> true
p arr.instance_of?(Object) #=> false
p arr.is_a?(Object) #=> true
==========================================
4) ruby中的常量真是容易定义,用大写字母开头的就是常量了。常量在修改值的时候会报警告。
5)类名。和js不一样的是,js中是出于习惯,提倡类名首字母大写,并没有做硬性要求。ruby的类名却强制性要求首字母大写。原因是ruby要求类名必须为常量。
6)构造函数。ruby的构造函数并不是和类名同名的方法,而是统一使用initialize方法。一般语言都可以设定构造函数的访问性,设计模式中的单独模式就是通过构造函数的访问性做的文章。as3中强制构造函数为public,而ruby强制构造函数为private。
7) 实例化。 ruby的实例化一个类,不是通过new 类名()这种通用方式,而是通过类名.new()。奇怪的ruby语法@_@。
8) 属性的访问性。 和java一样,ruby并不支持直接访问类中的属性,如果要访问类中的属性,需要先通过attr_reader:属性名(只读),attr_writer:属性名(只写),attr_accessor:属性名(读写)来设定属性的访问权限。ruby中没有var关键字,不能通过函数产生变量的作用域。在js里,我们可以在类中定义一个私有变量,在所有的方法中调用。私有变量只要定义为和方法在同一级别,那么它就可以在各个方法中负责通信。ruby中不能使用这种方法,ruby在类里没办法定义变量,只能在方法中定义变量。方法和方法之间如果想通信只能通过@变量,也就是实例变量进行通信。@变量就像是js中的this.xxx变量一样,但js中的this.xxx只能是public的,可读可写,ruby通过attr_xxxx可以控制@变量的访问性。
9)方法的访问性。方法的访问性是通过public,private和protected进行限制的。和一般语言将访问性关键字放在方法前面不同的是,ruby的访问性是通过public :方法名1,方法名2。。。这样的方式进行声明的。属性的访问性可以放在前面,但方法的访问性要放在方法之后,如果放在方法前面会出现方法未定义的报错。除了初始化方法恒化private,其它方法缺省为public。
10)类方法(静态方法)。 ruby中定义类方法有三种方法。
=========================================
1)
class HelloWorld
def HelloWorld.hello(name)
print name,” said Hello.”
end
end
HelloWorld.hello(“John”)
2)
class HelloWorld
..
end
class << HelloWorld
def hello(name)
print name,” said Hello.”
end
end
HelloWorld.hello(“John”)
3)
class HelloWorld
def self.hello(name)
print name,” said Hello.”
end
end
HelloWorld.hello(“John”)
=========================================
需要注意的是,类方法不能访问实例变量。
11)类变量(静态变量)。ruby中类变量是通过加@@标识的。
12)类常量。类中还可以定义常量,还是要求大写字母开头。类中不能定义和方法平缓的变量,但是可以定义和方法平级的常量,常量可以被所有方法调用。类常量的调用方法是类名::常量名。
13)继承。 ruby中的继承不是使用extend关键字,它更简洁,使用<就可以了。ruby也只支持单继承。
14) 类的示例。
========================================
class Dog
attr_accessor:name
def initialize(name = “wang cai”)
@name = name
end
def call
print “I’m a dog , my name is “, @name ,”\n”,Version,”\n”
end
def self.move(num=0)
print “Dog had moved “,num,” bu\n”
print Version,”\n”
end
Version = “1.0″
public :call
end
a = Dog.new
a.name = “huan huan”
a.call
Dog.move(5)
class BigDog < Dog
def call
print “I’m a big dog , my name is “, @name,”\n”
end
end
b = BigDog.new(“abc”);
b.call
print Dog::Version
========================================
15)模块。模块是ruby特有的功能之一。模块和类很像,但和类最大的不同在于模块不能实例化,也不能继承。模块的作用有两个,一个是用于提供命名空间。第二提供Mix-in功能。Mix-in和继承很像,但它不是继承,在《javascript设计模式》一书里,将Min-in称为掺元继承,YUI提供的augment方法,思路就是Mix-in。模块是通过include关键字读入的,它和as中的with()方法有相似的地方,include了文件之后,前辍就可以不用写了。
16)模块的常量和类常用调用方法一样。模块名::常量名。模块的方法调用和类的方法调用不同,可以使用模块::方法名,也可以使用模块.方法名。
17)模块定义的方法默认是不对外部公开的,(模块内没有public private protect关键字),要将方法对模块外部公开,必须使用module_function。
===================================
module HelloModule
Version = “1.0″
def hello(name)
print “Hello, “,name,”.\n”
end
module_function : hello
end
p HelloModule::Version #=>”1.0″
HelloModule::hello(“Alice”) #=> hello, Alice.
include HelloModule
p Version #=>”1.0″
hello(“Alice”) #=>Hello, Alice
===================================
event对象在IE和firefox下表现是不同的。在IE下,event是window对象的一个属性,是在全局作用域下的,而在firefox里,event对象是做为事件的参数存在的:
===========================
<input type=”button” id=”btn” value=”click me” />
<script type=”text/javascript”>
document.getElementById(“btn”).onclick = function(){
alert(arguments.length);
};
</script>
===========================
这代代码在IE下弹出0,而在firefox下弹出1。在firefox下这个参数就是event对象了。
如果在标签内联事件中触发事件会如何呢?
===========================
<input type=”button” id=”btn” value=”click me” onclick = “handler();” />
<script type=”text/javascript”>
function handler(){
alert(arguments.length);
};
</script>
===========================
在IE和firefox下,这段代码弹出的都是0.也就是说,标签内联事件并没有被替换成
===========================
btn.onclick = handler;
function handler(){
alert(arguments.length);
}
===========================
而是替换成了
===========================
btn.onclick = function(){
handler();
}
function handler(){
alert(arguments.length);
}
===========================
在标签内联事件中,我们使用arguments[0]可以在firefox下访问到event对象。
===========================
<input type=”button” id=”btn” value=”click me” onclick = “alert(arguments[0].type)” />
===========================
因为不使用标签内联事件的话,我们可以给处理函数传参,从而指定arguments[0]的变量名,通常情况下,我们平时也的确是这么处理的:
===========================
<input type=”button” id=”btn” value=”click me” />
<script type=”text/javascript”>
document.getElementById(“btn”).onclick = function(e){
e = window.event || e; //e兼容IE和firefox,指向event对象
};
</script>
===========================
在标签内联事件中我们没办法指定参数名,是不是就没办法直接写个变量在IE和firefox下兼容地指event对象呢?不是的,可以用“event”这个变量名兼容地指向event对象,注意,只能是“event”,如果是“a”,“b”,“Event”之类的全都不行。可能是因为考虑到标签内联事件中无法指定参数变量名,所以故意留个了“event”这个关键字吧。
===========================
<input type=”button” id=”btn” value=”click me” onclick=”alert(event.type);” />
===========================
这段代码在IE和firefox下都可以正常地弹出“click”。
有趣的是,标签内联事件中我们甚至可以写注释,可以使用字符串:
===========================
//只弹出1
<input type=”button” id=”btn” value=”click me” onclick=”alert(1);//alert(2);alert(3);” />
//弹出1和3
<input type=”button” id=”btn” value=”click me” onclick=”alert(1);/*alert(2);*/alert(3);” />
//弹出“string”
<input type=”button” id=”btn” value=”click me” onclick=”var a=’abc’;alert(typeof a);”/>
===========================
如果我们既用标签内联事件绑定了事件,又用DomNode.onxxxx绑定了事件,又会如何呢?
===========================
<input type=”button” id=”btn” value=”click me” onclick=”alert(123);” />
<script type=”text/javascript”>
document.getElementById(“btn”).onclick = function(){
alert(456);
};
</script>
===========================
会如何呢?会弹出456,不弹出123。相当于
===========================
<input type=”button” id=”btn” value=”click me” />
<script type=”text/javascript”>
document.getElementById(“btn”).onclick = function(){
alert(123);
};
document.getElementById(“btn”).onclick = function(){
alert(456);
};
</script>
===========================
后面的处理函数把前面的处理函数覆盖掉了。如果我们给DomNode是通过attachEvent和addEventListener来绑定事件的呢?
===========================
<input type=”button” id=”btn” value=”click me” onclick=”alert(123);” />
<script type=”text/javascript”>
function handler(){
alert(456);
}
if(document.all){
btn.attachEvent(“onclick”,handler);
} else {
btn.addEventListener(“click”,handler,false);
}
</script>
===========================
很顺利地,先弹出了123,后又弹出了456。
protype是个很有趣的属性,它是“类”所持有的属性。在javascript里原生提供的一些内置类,其本质也是“类”,内置类提供的方法我们也可以通过prototype来覆盖掉,这是件很有趣的事情。例如:
=========================================
var str = str2 = “ab,cd,ef,g”;
var arr = str.split(“,”);
String.prototype.split = function(a){
return “hello,you input: ” + a;
}
String.prototype.length = 200;
var arr2 = str2.split(“,”);
alert(str.length);
alert(arr);
alert(arr2);
Array.prototype.toString = function(){
return “12345″;
}
alert(arr);
=========================================
猜猜看,会依次弹出什么?“10”,“ab,cd,ef,g”,“hello,you input: ,”,“12345”。呵呵,看来属性没办法覆盖,第一个没弹出“200”,但方法可以覆盖,不论是String类的split方法,还是Array类的toString方法,都被我们覆盖掉了。
当然,覆盖内置类提供的方法这种操作绝大部分时候都是不好的,通常情况下,我们更多的是为内置类提供更多的方法,让程序更好用,例如:
==========================================
var arr = ["a","b","c"];
var str = arr.join(“-”);
alert(str);
Array.prototype.join2 = function(a){
return this.join(“^^^”+a+”^^^”);
}
str = arr.join2(“-”);
alert(str);
==========================================
我们给内置类添加一个join2方法,让它在join数组的时候,能做更多我们自定义的事。因为修改的是内置类的原型,所以js中所有的原生数据都直接获得了新的方法,这是种很方便的功能。但这样的方法其实并不推荐,它会对原生内置类的原型造成“污染”,可能会出现奇怪的问题,特别是多人合作的时候,或者引入第三方js库的时候,出现一些奇怪的bug,查都不好查。我们更推荐的方法是定义一个新的类,然后把所有需要扩展的功能放到这个新的类里去,通过这个新的类来完成功能,而不是直接修改原型。如:
===========================================
var arr = ["a","b","c"];
var str = arr.join(“-”);
alert(str);
arrayManager = {
join : function(oarr,ostr){
return oarr.join(“^^^”+ostr+”^^^”);
}
}
str = arrayManager.join(arr,”-”);
alert(str);
===========================================
prototype是什么意思呢?它表示“原型”,简单地说,js中的类是分两级来实现的,一级是“原型级”,它比较底层,另一级是“实例级”,实例级的优先级比原型级高,如果new一个类,调用方法的时候,先优先在实例级去查找有无这个方法,如果没有,才会去原型中找。实例级在分配内存时会为每个实例分配一个,而原型级只会在内存中分配一个,通过传址的形式传给每个类的实例。所以为了节约内存,我们更推荐写类的时候将方法通过prototype的方式写出来,而不要写在function里,例如:
=========================================
//不推荐的写法
function Dog(){
this.name = “WangCai”;
this.call = function(){
alert(”wang wang”);
}
}
//推荐的写法
function Dog(){
this.name = “WangCai”;
}
Dog.prototype = {
call : function(){
alert(“wang wang”);
}
}
=========================================
正因为prototype是通过传址的方式供各实例调用的,所以如果对prototype进行了修改,无需重新再new一遍,实例的方法就已经更改了:
=========================================
function Dog(name){
this.name = name;
}
Dog.prototype.call = function(){
alert(“I’m “+this.name);
}
var myDog = new Dog(“WangCai”);
myDog.call();
Dog.prototype.call = function(){
alert(“wang wang wang wang wang”);
}
myDog.call();
function Cat(name){
this.name = name;
this.call = function(){
alert(“I’m “+this.name);
}
}
var myCat = new Cat(“Mimi”);
myCat.call();
Cat.prototype.call = function(){
alert(“miao miao miao miao miao”);
}
myCat.call();
=========================================
myDog第一次call的时候,弹的是I’m WangCai,第二次就是wang wang wang wang wang了 ;myCat两次都弹的是I’m Mimi。
正是因为prototype的这种特性,所以open api的系统里,因为会支持第三方开发,为了防止第三方恶意覆盖掉javascript内置类的方法,都会封装一套接口给第三方使用,以阻止它们访问原生内置类的原型。