第1章:JavaScript精华(The Good Parts)
一、导读
1.1 本章学习目标
第1章是《JavaScript: The Good Parts》的开篇,Douglas Crockford 以独到的视角审视了 JavaScript 这门语言。JavaScript 被很多人误解和低估,Crockford 认为这门语言中隐藏着一些优秀的设计特性,但被大量的"糟粕"所掩盖。本章的目标是帮助你建立对 JavaScript 精华部分的认知。
本章的核心学习目标包括:
- 理解 JavaScript 的设计哲学:认识 JavaScript 中哪些是精华,哪些是糟粕。
- 掌握 JavaScript 的核心优势:函数是一等公民、基于原型的继承、对象字面量。
- 理解 JavaScript 的子集选择:为什么只使用精华部分能写出更好的代码。
- 认识 JavaScript 的历史包袱:了解哪些特性应该避免使用。
- 建立正确的 JavaScript 编程心态:不被偏见左右,客观评价这门语言。
1.2 为什么第一章如此重要
JavaScript 是世界上最被误解的语言。很多人对它的认知停留在浏览器脚本、表单验证的层面,忽视了它作为一门真正强大的编程语言的实力。Crockford 在本书开篇就指出,JavaScript 包含了大量优秀的设计,但这些设计被糟糕的特性所掩盖。
理解 JavaScript 的精华,不仅能让你写出更好的代码,还能让你理解为什么 JavaScript 能在短短十几年间从浏览器脚本发展为全栈语言、移动开发语言甚至系统编程语言。
1.3 核心问题引导
JavaScript 中哪些设计是精华?为什么它们如此优秀?
JavaScript 中哪些设计是糟粕?为什么应该避免它们?
为什么说 JavaScript 是"被误解的语言"?
函数作为一等公民意味着什么?
基于原型的继承与基于类的继承有什么本质区别?
二、核心概念详解
2.1 JavaScript 的精华
Crockford 认为 JavaScript 的精华包括:
2.1.1 函数是一等公民
函数在 JavaScript 中是对象,可以被赋值给变量、作为参数传递、作为返回值。这是 JavaScript 最强大的特性之一。
// 函数是对象
var add = function(a, b) {
return a + b;
};
// 函数可以作为参数
function apply(fn, x, y) {
return fn(x, y);
}
apply(add, 3, 4); // 7
// 函数可以拥有属性
add.name = "Addition";
add.version = "1.0";2.1.2 基于原型的继承
JavaScript 的继承机制不同于传统的基于类的语言。它使用原型链来实现对象之间的继承,这种机制更加灵活和强大。
// 原型继承
var animal = {
type: "animal",
say: function() {
return "I am a " + this.type;
}
};
var dog = Object.create(animal);
dog.type = "dog";
dog.bark = function() {
return "Woof!";
};
dog.say(); // "I am a dog"
dog.bark(); // "Woof!"2.1.3 对象字面量
JavaScript 的对象字面量是创建对象最简洁、最优雅的方式。它比 Java 的类定义更加直观。
// 对象字面量
var point = {
x: 10,
y: 20,
distance: function(other) {
var dx = this.x - other.x;
var dy = this.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
};2.1.4 数组字面量
JavaScript 的数组是真正的关联数组,不是传统意义上的连续内存块。
// 数组字面量
var colors = ["red", "green", "blue"];
colors.length; // 3
// 数组也是对象
colors.name = "primary colors";
colors.length; // 仍然是 32.2 JavaScript 的糟粕
Crockford 同时指出了 JavaScript 中的糟粕:
2.2.1 全局变量
JavaScript 的全局变量是语言设计中最严重的缺陷之一。所有未声明的变量都会成为全局变量,这导致了命名冲突和安全问题。
// 糟粕:隐式全局变量
function foo() {
value = "hello"; // 没有 var/let/const,成为全局变量
}
foo();
console.log(value); // "hello" —— 意外泄露到全局2.2.2 with 语句
with 语句会改变作用域链,使得代码难以理解和优化。
// 糟粕:with 语句
with (obj) {
a = b; // a 和 b 到底来自哪里?
}2.2.3 eval
eval 函数执行字符串形式的 JavaScript 代码,带来安全和性能问题。
// 糟粕:eval
eval("var x = 42"); // 安全风险,性能问题2.2.4 == 和 !=
宽松相等运算符 == 会进行类型强制转换,导致令人困惑的结果。
// 糟粕:宽松相等
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
null == undefined // true
// 正确做法:使用严格相等 ===
'' === '0' // false
0 === '' // false
0 === '0' // false2.3 JavaScript 的语法基础
2.3.1 数字
JavaScript 只有一种数字类型——64位浮点数。没有整数类型,这导致了精度问题。
// 数字
var x = 42;
var y = 3.14;
var z = 0xff; // 十六进制
// 精度问题
0.1 + 0.2 === 0.3 // false!
0.1 + 0.2 // 0.30000000000000004
// 特殊值
NaN // Not a Number
Infinity // 无穷大
-Infinity // 负无穷大
// NaN 的特性
NaN === NaN // false
typeof NaN // "number"
isNaN(NaN) // true2.3.2 字符串
JavaScript 的字符串是不可变的字符序列。
// 字符串
var s = "Hello, World!";
s.length; // 13
// 字符串方法
s.toUpperCase(); // "HELLO, WORLD!"
s.charAt(0); // "H"
s.indexOf("World"); // 7
s.slice(0, 5); // "Hello"
s.replace("World", "JavaScript"); // "Hello, JavaScript!"2.3.3 标识符
JavaScript 的标识符规则比大多数语言宽松,允许 Unicode 字母、数字、$ 和 _。
// 合法标识符
var _private = 1;
var $element = 2;
var café = 3; // Unicode 标识符
var 中文 = 4; // 也是合法的三、实战案例
3.1 函数式编程实战
// 利用函数是一等公民的特性
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
function double(x) { return x * 2; }
function increment(x) { return x + 1; }
var doubleThenIncrement = compose(increment, double);
doubleThenIncrement(5); // 11
// 闭包实现私有状态
function makeCounter() {
var count = 0;
return {
increment: function() { count += 1; return count; },
decrement: function() { count -= 1; return count; },
getCount: function() { return count; }
};
}
var counter = makeCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2
// count 变量对外部不可见3.2 对象字面量实战
// 使用对象字面量实现模块模式
var Calculator = (function() {
var history = [];
return {
add: function(a, b) {
var result = a + b;
history.push(a + " + " + b + " = " + result);
return result;
},
subtract: function(a, b) {
var result = a - b;
history.push(a + " - " + b + " = " + result);
return result;
},
getHistory: function() {
return history.slice(); // 返回副本
},
clearHistory: function() {
history = [];
}
};
})();
Calculator.add(3, 4); // 7
Calculator.subtract(10, 3); // 7
Calculator.getHistory(); // ["3 + 4 = 7", "10 - 3 = 7"]四、本章小结
第1章揭示了 JavaScript 语言的精华与糟粕:
精华:函数是一等公民、基于原型的继承、对象字面量、动态对象。
糟粕:全局变量、with 语句、eval、宽松相等。
核心思想:只使用 JavaScript 的精华部分,避免糟粕,可以写出更好的代码。
JavaScript 是一门被严重误解的语言。当你理解了它的精华,你会发现它比大多数语言更加灵活和强大。
五、思考与练习
思考题
为什么说函数是一等公民?这给 JavaScript 带来了哪些能力?
基于原型的继承与基于类的继承有什么本质区别?
全局变量为什么是 JavaScript 最严重的缺陷?
=== 和 == 有什么区别?为什么应该总是使用 ===?
闭包是什么?它如何实现数据隐藏?
编程练习
实现 compose 函数:支持任意数量的函数组合。
实现模块模式:使用闭包实现一个私有的栈数据结构。
实现原型继承:创建一个动物层级结构(Animal → Dog → Puppy)。