65.9K
CodeProject 正在变化。 阅读更多。
Home

新手入门 JavaScript 必知必会

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (44投票s)

2012 年 2 月 22 日

CPOL

7分钟阅读

viewsIcon

59142

本文总结了 JavaScript 中一些重要的要点和令人意外的怪癖,这些内容对于有经验的开发者,特别是来自 C# 或 Java 背景的开发者来说,是应该了解的。

新手入门 JavaScript 必知必会

与你可以随意选择语言和框架的服务器端或桌面开发不同,在客户端开发中,你基本上被 JavaScript 所束缚。有一些语言可以编译成 JavaScript,如果你真的非常不喜欢 JavaScript,你还有一些其他选项,比如 Script#WebSharperDartCoffeeScriptClojureScriptOpa 等等。话虽如此,不管你喜不喜欢,JavaScript 确实是 Web 的通用语。这意味着如果你想做一个 Web 应用程序,你很可能需要学习 JavaScript,无论你是否喜欢。

在本文中,我将尝试介绍你应该了解的 JavaScript 最重要的要点。我假设你已经是一个有一定经验的程序员,并且你正在学习或打算学习 JavaScript。本文主要面向 C# 和 Java 开发者,但即使你习惯了其他语言,也可能从中获益匪浅。

JavaScript 的语言家族

JavaScript 的名字具有欺骗性,暗示它基于 Java,并且像 Java 一样属于 C 语言家族。JavaScript 实际上并没有什么语言家族。JavaScript 只是它的创造者 Brendan Eich 喜欢其他语言中的一些特性,并在两周内实现出来的一个大杂烩。本质上,JavaScript 是 Java、Scheme、Perl 和 Self 的私生子。

表面上看,你应该能很快适应 JavaScript,因为它的语法几乎完全借鉴了它的 Java 祖先。JavaScript 拥有你已经熟悉和喜爱的所有花括号、分号以及 `if`-`for`-`while`-`do`-`switch`-`throw`-`catch`-`finally` 等控制结构。你应该庆幸这一点,因为 Java/C# 和 JavaScript 之间的相似性仅此而已。

动态类型

JavaScript 是动态类型语言,而 C# 和 Java 是静态类型语言。这意味着在 JavaScript 中,变量可以改变其类型,而且你永远不需要显式声明变量的类型或函数的返回类型。如果在静态类型语言中看到这样的代码
int x = 5;
x = "hello";

你理所当然地会期望编译器抛出一个巨大的、恼人的 `TypeError`。然而,JavaScript 会愉快地运行这段代码,即使类型已经改变。

var x = 5;
x = "hello";

由于变量可以改变类型,编译器无法获取关于它们的大量信息。在代码补全等实用功能方面,你应该期望 JavaScript 的工具不如 Java/C#。更少的错误会在编译时被捕获,而你将不得不进行比你通常习惯的更多的运行时调试。

弱类型

JavaScript 是弱类型的。这意味着 JavaScript 会进行各种各样的隐式类型转换,你最好小心,否则它会让你吃亏。在强类型语言中,如果你尝试将数字和字符串相加(`2 + "3"`),或者对布尔值和数组进行算术运算(`true + ([] - false)`),编译器会认为你疯了。然而,JavaScript 会愉快地继续进行,在后台将数字转换为字符串,将布尔值和空数组转换为数字,并将几乎任何东西转换为布尔值。

弱类型会产生各种你可能意想不到的后果,但有两个重要的后果你应该知道。

首先,JavaScript 有一个 `==` 运算符。你可能会期望它像其他语言中的 `==` 运算符一样工作,但遗憾的是,它并非如此。如果两个操作数类型不同,JavaScript 会尝试通过它喜欢的任何转换来统一它们的类型,然后检查相等性。所有这些表达式在 JavaScript 中都返回 true,这与你合理期望的相反

123 == "123"
true == 1
false == "0"
[] == ""
{} == "[object Object]"

幸运的是,JavaScript 有严格相等运算符 `===` 和 `!==`,如果两个操作数的类型不同,它们总是返回 false。你应该几乎总是使用它们而不是 `==` 和 `!=` 运算符。

其次,JavaScript 总是会将 `if` 语句中的表达式转换为布尔值。以下值会在 `if` 条件中被评估为 `false`。这些被称为“假值”(falsy values)。

false
null
undefined
0
""
NaN

其他所有值都评估为 true,或者称为“真值”(truthy values)。

数字

JavaScript 只有一个数字类型。它等同于你熟悉的 `double` 类型。请记住这一点,因为整数除法可能不会按你预期的方式工作。还要注意,在 JavaScript 中,位移和按位操作的开销很大,因为在执行位运算之前数字必须转换为整数,然后再转换回双精度浮点数。

数组

JavaScript 中只有一个列表类型,JavaScript 称之为数组。这种单一数据类型在 JavaScript 中被用于你可能在其他语言中使用列表、数组、向量、集合或元组的地方。数组的长度可以动态改变,并且数组中的元素不必是相同的类型。

基本上,你可以像这样声明一个数组

var array1 = []; //empty array
var array2 = [1, "hello", null, ["bye", 0]]; //array with stuff in it

这很简单,但我建议你阅读一下 JavaScript 中的 数组函数,以熟悉可用的功能以及如何使用它们。

对象

对象是 JavaScript 的最佳特性之一,因为它们的通用性。JavaScript 中的对象就像关联数组或字典。左边是字符串键或名称,右边可以是任何 JavaScript 值,包括函数和其他对象。

在 Java/C# 中,你经常使用类。在 JavaScript 中,你可以更简洁地使用对象来实现相同的目的。

这是一个虚构的 Person 类

class Person
{
    public string name = "John";
    public int age = 25;
    public string occupation = "programmer";
}

在 JavaScript 中,你可以用对象字面量完成相同的任务

var person = {
  name: "John",
  age: 25,
  occupation: "programmer"
};

但假设你想实现一个带有构造函数、私有字段和方法的完整类,变得更高级。

class Person
{
    private string name;
    private int age;
    private string occupation;

    public Person(string name, int age, string occupation)
    {
        this.name = name;
        this.age = age;
        this.occupation = occupation;
    }

    public string GetDescription()
    {
        return name + " is a " + age + " year old person that earns money as a " + occupation + ".";
    }
}

现在你可以创建一个新的人,并使用类似这样的代码获取他的描述

Person John = new Person("John", 25, "programmer");
string biographyOfJohn = John.GetDescription();

JavaScript 没有类。JavaScript 有一种我认为更直观、更简洁的东西:对象。让我们通过将我们的 Person 类移植到 JavaScript 来观察对象是如何工作的。

function Person(name, age, occupation) {
    return {
      name: name,
      age: age,
      occupation: occupation,
      getDescription: function() {
        return name + " is a " + age + " year old person that earns money as a " + occupation + ".";
      }
    };
}

现在,当然,你可以调用 Person 函数,它返回一个对象,你可以用它来获取某个人的描述。

var John = Person("John", 25, "programmer");
var biographyOfJohn = John.getDescription();

函数

与 C# 和 Java 不同,JavaScript 中的函数是第一类值:你可以将它们赋值给变量,将它们作为参数传递给函数,让它们从函数返回,并将它们作为数组元素或对象的值。这是一个很棒的功能。

解释为什么这很棒超出了本文的范围,但它确实很棒,如果你不熟悉这个概念,我强烈建议阅读关于 JavaScript 中的 函数式编程,或者甚至关于 函数式编程范式本身

总之,JavaScript 中的函数语法在很大程度上是相当直接的。你有两个选择,

function name(arguments) {
  //function body
  return returnValue;
}

var name = function(arguments) {
  //function body
  return returnValue;
}

唯一的区别是第一个会自动“提升”(hoist)到作用域的顶部,允许你在声明它之前在代码中调用它。(阅读更多关于 JavaScript 中的提升)第二个只能在你声明它之后才能调用它。

范围

JavaScript 只有函数作用域,而不是 C#/Java 中的块作用域。如果你写出像这样的代码,它会让你吃亏

var x = 45;
if(condition) {
  var x = 35;
}

在块级作用域中,你会期望 x 是局部于其块的,并且不会影响外部的 x。在 JavaScript 中,两者都被视为同一作用域,因此对内部 x 的赋值会改变外部的 x。

幸运的是,像 JSHint 这样的代码质量工具可以发现这类问题并向你发出警告。

意外的全局作用域

在声明变量时,请务必使用 `var` 关键字。如果你不这样做,JavaScript 会自动假定它是全局的。这很糟糕:它可能导致难以调试的细微错误。

var globalVar = "global variable";
otherGlobalVar = "other global variable";
 
function doStuff() {
  alsoGlobal = "unfortunately, this is also a global variable";
}

此示例中声明的所有变量都是全局的。`globalVar` 还可以,尽管你通常应该避免过度使用全局变量。`otherGlobalVar` 通常被认为是坏习惯,而 `alsoGlobal` 则非常糟糕,很可能导致 bug。

再次强调,请确保使用像 JSHint 这样的工具,它会为你捕获此类错误。

DOM

这本身并不是关于 JavaScript 语言的,而是关于它用于操作网页的文档对象模型 (DOM) 库:它很糟糕。不同的浏览器对工作方式不一致,它非常冗长,我不建议浪费时间在它上面。为了你自己好,请使用像 jQuery 这样的 DOM 抽象框架。

就这些,各位!

我相信你还会遇到 JavaScript 中其他令人惊讶的方面,但我希望我已经涵盖了最令人诟病的部分。如果你打算继续学习 JavaScript,这里有一些我推荐的链接

© . All rights reserved.