新手入门 JavaScript 必知必会






4.79/5 (44投票s)
本文总结了 JavaScript 中一些重要的要点和令人意外的怪癖,这些内容对于有经验的开发者,特别是来自 C# 或 Java 背景的开发者来说,是应该了解的。
新手入门 JavaScript 必知必会
与你可以随意选择语言和框架的服务器端或桌面开发不同,在客户端开发中,你基本上被 JavaScript 所束缚。有一些语言可以编译成 JavaScript,如果你真的非常不喜欢 JavaScript,你还有一些其他选项,比如 Script#、WebSharper、Dart、CoffeeScript、ClojureScript、Opa 等等。话虽如此,不管你喜不喜欢,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,这里有一些我推荐的链接