NO END FOR LEARNING

Writing blog if you feel tired | 学海无涯 苦写博客

学好JavaScript - 初学者忽视和困惑的东西

| Comments

JavaScript普通对象

JavaScript中除了数字、字符串、布尔值、null和undefined之外的就是对象了。对象是属性的集合,每个属性都由“名/值对”构成,值可以是原始值,比如数字、字符串,也可以是对象。普通的JavaScript对象就是“命名值”的无序集合。(JavaScript同样定义了一种特殊对象-数组,表示带编号的值的有序集合)

可选的分号

JavaScript使用分号将语句分开。但如果语句各自独占一行,通常可以省略语句之间的分号。是否省略分号是一种风格,对于不知道什么时候可以省略分号的开发,在任何时候都采用分号分割是一种常见风格。

强制类型转换和弱类型

如果使用的数据类型和JavaScript所需的数据类型不同,那么它会尽量使这个操作变得有意义,而不是直接报错。

为完成操作,JavaScript会在背后进行数据类型转换,这被称作强制类型转换。例如,表达式(‘1’ > 0)中,字符串‘1’会被转换成数值1,从而导致该表达式的结果为true。

JavaScript被称作弱类型语言,因为值的数据类型是可以变的。其他一些语言则要求指定每个变量的数据类型,这样的语言被称为强类型,比如说Java,C++。

强制类型转换可能会导致代码出现意外情况。因此,当检测两个值是否相等时,推荐使用更加严格的===和!==来取代==和!=,因为严格相等和严格不等会同时检测值及其类型。

布尔值

因为存在强制类型转换,JavaScript中的每个值都可以被当做true或者false处理,这就导致了有趣的现象。

任意JavaScript的值都可以转换为布尔值,下面这些值会被转换为false,

1
2
3
4
5
6
undefined
null
0
-0
NaN
""

所有其他值,包括所有对象(数组)都会转换成true,false和上面6个可以转换成false的值有时称作“假值”,其他值称作“真值”。JavaScript期望使用一个布尔值的时候,假值会被当做false,真值会被当做true。

null和undefined

null是JavaScript语言的关键字,它表示一个特殊值,常用来描述“空值”。对null执行typeof运算,结果返回字符串“object”,也就是说,可以将null认为是一个特殊的对象值,含义是“非对象”。但实际上,通常认为null是它自由类型的唯一一个成员,它可以表示数字,字符串和对象是“无值”的。

JavaScript还有第二值来表示值的空缺。用未定义的值表示更深层次的“空值”。它是变量的一种取值,表示变量没有初始化,如果要查询对象属性或者数组元素的值时,返回undefined则说明这个属性或者元素不存在。如果函数没有任何返回值,则返回undefined。

undefined是预定义的全局变量,它和null不一样,它不是关键字,它的值就是“未定义”。

尽管null和undefined是不同的,但它们都表示“值的空缺”,两者往往可以互换。判断相等运算符“==”认为两者是相等的,所以需要使用严格相等运算符“===”来区分它们。

如果你想将它们赋值给变量或者属性,或将它们作为参数传入函数,最佳的选择是使用null。

检测相等和存在

因为对象或者数组被视为真值,所以这种方法经常被用来判断页面中的元素是否存在。

1
2
3
4
5
if (document.querySelector('#main')) {
  
} else {
  
}

由于强制类型转换,严格等于操作符===和严格不等于操作符!==所得结果比==和!=更容易预料。

如果使用==操作,那么下列值被视为相等的:false、0以及’‘(空字符串),但在严格的===下,它们是不相等的。

虽然null和undefined都是假值,但它们除了自身之外不等于任何值。同样在使用严格相等时,也是不相等的。

虽然NaN被视为假值,但是它不等于任何值,它升值不等于它自己(因为NaN表示不可定义的数字,两个不可定义的数字是不相同的)。

短路值

逻辑操作符是从左向右计算的,当它们获得确定结果时,立刻会发生“短路”(停止运算),但是它们会返回停止运算时的值(不一定是true或者false),这里很有意思。

1
2
3
4
5
6
7
8
9
10
11
var a = 'a'
var b = (a||'c')
// a

var a = ''
var b = (a||'c')
// c

var a = ''
var b = (a||{})
// Object{}

逻辑操作符并非总返回true或者false,原因在于:

它们会返回停止运算时的值,那个值可能是真值或者假值,但不一定是布尔类型。这种技巧常常被使用于变量赋值和创建对象。

因为这种短路机制,在“或”运算符常常把最可能返回true的放在第一位,在“与”操作符把最可能返回false的放在第一位,或者把最耗时的操作放在最后一位。

全局对象

全局对象的属性是全局定义的符号,JavaScript程序可以直接使用。当JavaScript解释器启动时(或者任何Web浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性:全局属性,undefined,全局函数,isNaN(),构造函数,Date(),全局对象,Math。

全局对象的初始属性并不是保留字,但它们应该当做保留字来对待。

在代码的最顶级——不在任何函数内的JavaScript代码——可以使用JavaScript关键字this来引用全局对象:

1
var global = this; // 定义一个引用全局对象的全局变量

在客户端JavaScript中,在其表示的浏览器窗口中的所有JavaScript代码中,Window对象充当了全局对象。这个全局对象Window对象有一个属性window引用其自身,它可以代替this来引用全局对象。

当初次创建的时候,全局对象定义了JavaScript中所有的预定义全局值。这个特殊对象同样包含了为程序定义的全局值。如果代码声明了一个全局变量,这个全局变量就是对象的一个属性。

1
2
var a = 'a';
window.a; //输出"a"

包装对象

我们看到字符串也同样具有属性和方法,字符串既然不是对象,为什么它会有属性呢?
只要引用了字符串的属性,JavaScript就会将字符串通过调用new String(“some string”)的方法转换成对象。

同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象。

JavaScript会在必要时将包装对象转换成原始值。“==”等于运算符将原始值和其他包装对象视为相等,但“===”全等运算符将它们视为不等。

in运算符

in运算符希望它的左操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象。如果右侧的对象拥有一个名为做操作数值的属性名,那么表达式返回true。

1
2
a = {text:"hi"}
"text" in a

for/in语句也使用for关键字,但它是和常规的for循环完全不同的一类循环。for/in循环语句的语法如下:

1
2
for (variable in object)
  statement

variable通常是一个变量名,也可以是一个可以产生左值的表达式或者一个通过var语句声明的变量,总之必须是一个适用于赋值表达式左侧的值。object是一个表达式,这表达式的计算结果是一个对象。

for/in循环则是用来更方便地遍历对象属性成员:

1
2
for (var p in o)
  console.log(o[p]);

在执行for/in语句的过程中,JavaScript解释器首先计算object表达式。如果表达式为null或者undefined,JavaScript解释器将会跳出循环并执行后续的代码。如果表达式等于一个原始值,这个原始值将会转换为与之对应的包装对象。否则,expression本身已经是对象了。JavaScript会依次枚举对象的属性来执行循环。

“use strict”

“use strict”是ECMAScript5引入的一条指令。指令不是语句。

它不含任何语言的关键,指令仅仅是一个包含一个特殊字符串直接量的表达式,对于那些没有实现ECMAScript5的JavaScript解释器来说,它只是一条没有副作用的表达式语句,它什么也没做。

它只能出现在脚本代码的开始或者函数体的开始,任何实体语句之前。但他不必一定要出现在脚本的首行或者函数体首行,因为“use strict”指令之后或者之前都可能有其他字符串直接量表达式语句,并且JavaScript的具体实现可能将它们解析为解释器自有的指令。在脚本或者函数体第一条常规语句之后字符串直接量表达式语句只当做普通表达式语句对待;它们不会当做指令解析,它们也没有任何副作用。

使用“use strict”指令的目的是说明(脚本或函数中)后续的代码将会解析为严格代码。

严格代码以严格模式执行。ECMAScript5中的严格模式是该语言的一个受限制的子集,它修正了语言的重要缺陷,并提供健壮的查错功能和增强的安全机制。

原型

每个JavaScript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。

所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。因此,同使用{}创建对象一样,通过new Object()创建的对象也继承自Object.prototype。同样,通过new Array()创建的对象的原型就是Array.prototype,通过new Date()创建的对象的原型就是Date.prototype。

没有原型的对象为数不多,Object.prototype就是其中之一。他不继承任何属性。其他原型对象都是普通对象,普通对象也有原型。所有的内置的构造函数都具有一个继承自Object.prototype的原型。例如,Date.prototype的属性继承自Object.prototype,因此由new Date()创建的Date对象的属性同时继承自Date.prototype和Object.prototype。这就是所谓的“原型链”。

关联数组

object.property object[“property”]

第二种语法使用方括号和一个字符串,看起来更像数组,只是这个数组是通过字符串索引而不是数字索引,这种数组就是所说的关联数组。

在C++、Java等强类型语言中,对象只能拥有固定数目的属性,并且这些属性名称必须提前定义好。由于JavaScript是弱类型语言,因此不必遵循这个条规定,在任何对象中程序都可以创建任意数量的属性。但当通过点运算符访问对象的属性时,属性名用一个标识符来表示。标示符必须直接出现在JavaScript程序中,它们不是数据类型,因此程序无法修改它们。

反过来讲,当通过[]来访问对象的属性时,属性名通过字符串来表示。字符串是JavaScript的数据类型,在程序运行时可以修改和创建它们。

for(i = 0; i < 4; i++) addr += customer[“address” + i];

当whenever遇到capistrano和rbenv - Linux下的cron Job

| Comments

当你遇到这样一个需求:用户订阅了很多信息,服务器需要每周,或者每月给用户发送邮件,告知用户订阅信息的更新内容。你需要怎么做?

在RoR的环境下,发送邮件使用Rails Action Mailer。参考资料:http://guides.ruby-china.org/action_mailer_basics.html

那么如何定时呢?答案是Cron Job。

如果大家谷歌rails cron job,就可以得到答案,一个是关于Cron Job的答案, http://www.gotealeaf.com/blog/cron-jobs-and-rails ,一个是whener gem, https://github.com/javan/whenever

Cron是*nux系统中一个基于时间的任务安排软件。通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。

更多关于crontab定时任务,可以查看: http://linuxtools-rst.readthedocs.org/zh_CN/latest/tool/crontab.html

关于时间的定义:

1
2
3
4
5
6
7
# +---------------- minute (0 - 59)
# |  +------------- hour (0 - 23)
# |  |  +---------- day of month (1 - 31)
# |  |  |  +------- month (1 - 12)
# |  |  |  |  +---- day of week (0 - 6) (Sunday=0)
# |  |  |  |  |
  *  *  *  *  *  command to be executed

这里,我就不细谈crontab定时任务,大家可以去看上面那个链接。

whenever是一个Ruby的gem,它可以提供一个清晰的语法来编写定时任务。

如果你刚才有停下来看crontab的内容,你会发现crontab任务的语法非常的复杂。

whenever的目的就是为了让你以ruby的语法来编写要执行的任务,再由它来生成真正的crontab任务。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
set :environment, "staging"
set :output, {:error => "log/cron_error_log.log", :standard => "log/cron_log.log"}

weekly_of_day = :sunday # every sunday
weekly_of_time = {:at => '0:00 am'} # every sunday 0:00am

monthly = '0 0 1 * *' # monthly 0 0 1 * * (every first day of month at 0:00am)

quarterly = '0 0 1 1,4,7,10 *' # quarterly 0 0 1 1,4,7,10 * (every 3 months at 0:00am start from the first day in january)

job_type :rbenv_rake, %Q{export PATH=~/.rbenv/shims:~/.rbenv/bin:/usr/bin:$PATH; eval "$(rbenv init -)"; cd :path && RAILS_ENV=staging bundle exec rake :task --silent >> log/cron_log.log 2>> log/cron_error_log.log}

every weekly_of_day, weekly_of_time do
  rbenv_rake "email_notification:send_email_notification_weekly"
end

every monthly do
  rbenv_rake "email_notification:send_email_notification_monthly"
end

更多关于如何使用whenever的内容,请查看: https://github.com/javan/whenever

whenever提供提供了四种job类型,如下:

1
2
3
4
job_type :command, ":task :output"
job_type :rake,    "cd :path && :environment_variable=:environment bundle exec rake :task --silent :output"
job_type :runner,  "cd :path && bin/rails runner -e :environment ':task' :output"
job_type :script,  "cd :path && :environment_variable=:environment bundle exec script/:task :output"

但是,你在我给的例子中可以看到,我并没有使用其中的任何一个,而是自定义了一个rbenv_rake,这便是接下来的重点。

如果你产品环境的Ruby环境是通过rbenv配置,那么在你使用crontab任务的可能会遇到这个问题。

1
../spec_set.rb:92:in `block in materialize': Could not find rake-10.4.2 in any of the sources (Bundler::GemNotFound)

关于原因,可以查看这个: http://benscheirman.com/2013/12/using-rbenv-in-cron-jobs/

Crontab运行在一个受限的环境下,所以.bash_profile的配置方式,并不起作用。需要在每次运行该任务前,重新初始化一次rbenv。

最后一点:

关于whenever和capistrano的集成,其实官方网站上也给出了,但是测试过不起作用,至少我这没起作用,所以我在部署的ruby文件中添加了一个task。

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace :deploy do

  desc "Update crontab with whenever"
  task :update_cron do
    on roles(:app) do
      within current_path do
        execute :bundle, :exec, "whenever --update-crontab #{fetch(:application)}"
      end
    end
  end

  after :finishing, 'deploy:update_cron'
end