我的Javascript语言精粹-基础语法篇

变量


Javascript是一门解释型的动态语言。

所谓解释型,区别于 C/C++/Java 等编译型语言,并不会在运行将源代码编译为类文件,而是在运行时动态解析,边解释边运行。

所谓动态,即是在 编写代码时时,不用强制声明变量的类型,而是在执行时,动态确定变量的类型。动态语言的特性,确定了Javascript这门语言 随性 的气质,也是Javascript的灵动飘逸所在。

理解Javascript的精髓,首先应该理解Javascript中的变量。

声明与定义

var a = 1;

这是一句最简单的变量定义。而实际上基本等价于做了两个操作:声明变量a,然后将数值1复制给变量a。

var a;
a = 1;

var a; 称为 变量的声明。如果只是对变量声明,而未赋初始值,则默认的值为 undefined

a = 1;为对变量的赋值。将数值1 赋值给 变量a。

在JavaScript中,允许省略var,直接对未声明的变量赋值。也就是说,var a = 1;a = 1;,这两条语句的在执行时的效果看上去是相同的。

但是,需要特别注意的是,这两种方法并不完全相同。

如果对变量未声明而直接赋值的话(如 a = 1;),此时的变量将定义为 全局变量(即便语句在函数内部)!此时往往会不知不觉的造成 全局变量污染。所以一定要使用关键字 var 对变量先进行声明。

还有一种写法也会造成类似的问题,如:

function foo() {
    var a = b = 0;
    // ...
}

这段代码,声明了一个 局部变量a 与一个 全局变量b

原因是这里的计算顺序是从右至左的。首先计算表达式 b=0,这里的b是未声明的。然后通过var创建了 局部变量a,并赋值为0。等价于:

var a = (b = 0);

原则1:变量要先声明再赋值,不要忘记使用var。

动态类型

Javascript中变量的一大特点,即为动态类型。变量的类型没有限制,可以赋予各种类型的值。

var a = 1;
a = "hello";

在首次赋值时,变量 a 为数值型,而赋值为 hello 后,则变为字符串型。

变量提升(hoisting)

变量提升的概念并不难理解,但却很容易被忽略。先看这么一段代码:

var myvar = "my value";
(function() {  
    console.log(myvar);
    var myvar = 'local value';  
})();  

console.log(myvar) 时,结果是什么呢?答案是 undefined。不知道是否和你想的一样。

在上面的例子中,结果为 undefined 原因,正是因为 变量提升这个概念。

在Javascript中,会自动将变量的声明进行提升。这意味着 var表达式function 声明 都将会被提升到当前作用域的顶部。在上文的例子中,实际的执行顺序,我们可以理解为这样:

var myvar = "my value";  
(function() {  
    var myvar;
    console.log(myvar);
    myvar = 'local value';  
})();

所以便不难理解,为什么会输出 undefined ,因为变量的声明已经被提升了。

为了避免这种情况可能带来的问题,我们应该将变量的声明显示的放在作用域最上方。这样做的好处有:

  • 在同一个位置可以查找到函数所需的所有变量
  • 避免当在变量声明之前使用这个变量时产生的逻辑错误
  • 提醒你不要忘记声明变量,顺便减少潜在的全局变量
  • 代码量更少(输入更少且更易做代码优化)

原则2:在作用域开始时,先声明好所有可能需要使用的变量。

区块(block)


JavaScript支持一对花括号创建的代码段,花括号所包裹的部分,就称为 区块

C 等语言中,区块使用独立的作用域不同的是,在Javascript中,区块可以说是 然而并没有什么卵用。区块中的变量与区块外的变量,属于同一个作用域。

{ 
    var a = 1;
}
console.log(a); // 1

原则3:只在 functionifswitchwhilefordotry等有意义的代码块中使用花括号。

条件语句


JavaScript提供 if结构switch结构,完成条件判断。

比较运算符

Javascript中,一般有两种运算符。严格运算符 === 与非严格运算符 ==

使用 === 时,等式两边不会进行隐式转换,而是直接比较,只有等式两边数值与类型完全一样时,才返回 true

使用 == 时,在比较前会将两个被比较的值转换为相同类型,然后再进行比较。

var a = "1";
var b = 1;
a == b  // true
a === b // false

表达式的值

下面这些值将被计算为 false :

  • false
  • undefined
  • null
  • 0
  • NaN
  • 空字符串 (“”)

当传递给条件语句时,所有其他值,包括所有 对象 会被计算为 true

switch结构

下例为switch的标准代码风格。参考自JavaScript模式一书。

var inspect_me = 0,
var result = '';
switch (inspect_me) {
case 0:
    result = "zero";
    break;
case 1:
    result = "one";
    break;
default:
    result = "unknown";
}
  • 每个case和switch对齐(这里不考虑花括号相关的缩进规则)
  • 每个case中的代码整齐缩进
  • 每个case都以break作为结束
  • 避免连续执行多个case语句块(当省略break时会发生),如果你坚持认为连续执行多case语句块是最好的方法,请务必补充文档说明,对于其他人来说,这种情况看起来是错误的
  • 以default结束整个switch,以确保即便是在找不到匹配项时也会有正常的结果

循环


Javascript中的循环与其他编程语言没有太大区别。主要有 whiledo...whilefor循环三种。重点说一下 for循环 的两种形式。

基本for循环

基本for循环 中,可以对数组或类似数组的对象作遍历。

for (var i = 0; i < myArray.length; i++) {

    // do something with myarray[i]
}

但是这样的使用方式,在某些情况下可能并不是最佳实践。因为每次进入循环时,都会读取 myArray.length 属性。当 myArray 为一个 HTMLCollection 对象时,如 document.imagesdocument.forms 时,每次循环都会先调用dom的查询操作,非常耗费资源。

最佳实践是这种形式:

var i = 0,
var max,
for (i = 0, max = myArray.length; i < max; i++) {
    // do something with myarray[i]
}

使用变量 max 记录 myArray的长度。同时将 imax 都使用 var 单独进行声明。

for-in循环

for-in 循环用于对非数组对象作遍历。通过for-in进行循环也被称作 枚举

对对象使用 for-in 循环,可以遍历对象的所有属性,也包括从 原型链上继承 的的属性。如果不想包含从原型链上继承的的属性,需要使用 hasOwnProperty 方法。

Object.prototype.bar = 1;
var foo = {moo: 2};

/* 不使用hasOwnProperty过滤 */
for(var i in foo) {
    console.log(i); // 输出两个属性:bar 和 moo
}
/* 使用hasOwnProperty过滤 */
for(var i in foo) {
    if (foo.hasOwnProperty(i)) {
        console.log(i);// 只输出属性moo
    }
}

数据类型


测试

null && undefined

数值(number)

字符串(string)

布尔值(boolean)

对象(object)

数组(array)

函数(function)

typeof与类型转换

参考资料