《编写高质量 JavaScript 代码的 68 个有效方法》读书笔记

最后更新:
阅读次数:

数字和字符串

  • JavaScript 中所有的数字都是双精度浮点数
// 小心浮点数的精度陷阱

0.1 +
0.2(
// 0.30000000000000004
0.1 + 0.2
) +
0.3; // 0.6000000000000001
0.1 + (0.2 + 0.3); // 0.6
  • 一个 JavaScript 字符串的元素是一个 16 位的代码单元

代码点: Unicode 编码为世界上所有的文字系统的每个字符单位分配了一个唯一的整数,该整数介于 0 和 1114111 之间,在 Unicode 术语中一个该整数被称为一个代码点(code point)。

代码单元: 最初,人们认为 Unicode 最多需要 2 的 16 次方个代码点,所以产生了 UCS-2 编码,其为 16 位编码的原始标准。由于每个代码点可以容纳一个 16 位的数字,所以简单的方法就是将代码点与其编码元素一对一地映射起来,这被称为一个代码单元(code unit)。

由于错误估算了代码点的容量范围,UCS-2 变得过时,其替代者 UTF-16 通过代理对来表示附加的代码点。一个代理对是一对 16 位的代码单元,这对代码单元共同编码一个等于大于 2 的 16 次方的代码点。

在 Unicode 扩大规模时,JavaScript 已经采用了 16 位的的字符串元素,字符串属性和方法都是基于代码单元层级,而不是代码点层级,所以一个 JavaScript 字符串的元素是一个 16 位的代码单元。

类似地,JavaScript 的正则表达式也工作于代码单元层级。

unicode

toString() VS valueOf()

  • 对象也可以被强制转换为原始值,优先使用 valueOf() 进行转换,其次使用 toString() 进行转换
let a = "a";
let obj1 = {
toString: function() {
return "b";
}
};
let obj2 = {
valueOf: function() {
return "c";
}
};
let obj3 = {
toString: function() {
return "b";
},
valueOf: function() {
return "c";
}
};

console.log(a + obj1); // ab
console.log(a + obj2); // ac
console.log(a + obj3); // ac
  • Object.prototype.toString(): 返回一个表示该对象的字符串。如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象类型。
Math.toString(); // "[object Math]"

// 因为在 String 中覆盖掉了 Object 中的 toString() 方法
String.toString(); // "function String() { [native code] }"
Object.prototype.toString.call(String); // "[object Function]"

let str = "abcd";
str.toString(); // "abcd"
Object.prototype.toString.call(str); // "[object String]"
  • Object.prototype.valueOf(): 返回指定对象的原始值。

JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)

其他

  • this 变量的隐式绑定问题

每个函数都有一个 this 变量的隐式绑定,该 this 变量的绑定值是在调用该函数时确定地。

this 变量的作用域总是由其最近的封闭函数所确定地。

  • 在类数组对象上复用通用的数组方法
// 在控制端下打印指定的 DOM 节点
let title = document.querySelectorAll("h3");

Array.prototype.forEach.call(title, function(e) {
console.log(e);
});

库和 API 设计

  • 利用测试 undefined 的方式提供参数默认值
function Person(name, age) {
this.name = name === undefined ? "default_name" : name;
this.age = age === undefined ? "default_age" : age;
}

let p1 = new Person();
console.log(p1); // Person {name: "default_name", age: "default_age"}

let p2 = new Person("percy", 22);
console.log(p2); // Person {name: "percy", age: 22}
  • 接收一个选项对象,而不是一堆参数
// bad way
let window1 = new Window(300, 100, "#666", "#FFF");

// good way
let window2 = new Window({
width: 300,
height: 100,
background: "#666",
color: "#FFF"
});

接收一个选项对象的实现逻辑是:先在内部定义一个全部的有默认值的默认选项对象,然后用 extend() 方法将传入的选项对象对默认选项对象进行相同键名覆盖即可(注意 extend() 方法有 undefined 值检测)

注意: 下面实现接收选项对象的代码并不完善,因为没有对传入的选项对象的属性进行类型检测。所以在实际应用中,一定要进行类型检测,防止出错。

function Window(options) {
let default_options = {
width: 300,
height: 100,
background: "#FFF",
color: "#000",
position: "center",
isAnimation: false
};
extend(default_options, options);
console.log(options);
console.log(default_options);

// 其他操作。。。
}

let window3 = new Window({
height: 200,
color: "#FFF",
isAnimation: true
});

function extend(target, source) {
if (source) {
for (let key in source) {
let val = source[key];

if (typeof val !== "undefined") {
target[key] = val;
}
}
}
return target;
}