Effective JavaScript Item 54 将undefined视为"没有值"
将undefined视为"没有值"
JavaScript中的undefined是一个特殊的值:当JavaScript没有提供详细的值时。它就会产生undefined。
比方:
- 未被赋值的变量的初始值就是undefined
- 訪问对象中不存在的属性会得到undefined
- 没有返回值的函数。undefined会作为其返回值
- 函数的參数没有提供时。它的值就是undefined
// 情形1
var x;
x; // undefined // 情形2
var obj = {};
obj.x; // undefined // 情形3
function f() {
return;
}
function g() { } f(); // undefined
g(); // undefined // 情形4
function f(x) {
return x;
}
f(); // undefined
将undefined视为不论什么详细值的缺失是JavaScript语言的一种约定。
所以,将它作为其他用途使用就是一种具有风险的行为。比方,一个库中的highlight方法用来改变元素的背景颜色:
element.highlight(); // use the default color
element.highlight("yellow"); // use a custom color
假设我们想让highlight方法具备返回随机颜色的功能,我们或许会尝试使用undefined作为这样的情况下须要传入的參数来和其它情况差别开:
element.highlight(undefined); // use a random color
可是,这样做是有风险的。比方。我们可能会向该方法中传入一个对象的属性。假设该属性没有值时。highlight方法就会返回一个随机的颜色,可是这样的情况下,用户期望的结果应该是为该元素使用默认的颜色。
var config = JSON.parse(preferences);
// ...
element.highlight(config.highlightColor); // may be random
除了使用undefined之外。有些开发者可能会选择相同比較特殊的null作为參数传入来进行区分:
element.highlight(null);
可是。这种代码的可读性比較差。用户第一眼看上去会猜想此方法是要移除element的背景颜色,而不是八竿子打不着的返回随机颜色。
一个更好的API应该是这种,通过传入字符串来表名意图:
element.highlight("random"); // 或者通过配置对象。关于配置对象能够參考Item 55
element.highlight({ random: true });
另外一个须要注意undefined的地方是拥有可选參数的函数。尽管能够通过arguments对象(关于此对象,能够參考Item 51)对实际传入的參数进行推断,可是对參数进行undefined推断能够让API更加健壮。比方。一个Server对象也许会接受host名作为參数:
var s1 = new Server(80, "example.com");
var s2 = new Server(80); // defaults to "localhost" function Server(port, hostname) {
if (arguments.length < 2) {
hostname = "localhost";
}
hostname = String(hostname);
// ...
}
以上代码使用arguments的length值作为推断根据。来给hostname參数一个默认值。可是,假设hostname被传入了undefined,就会导致默认值不会生效:
// config.hostname为undefined时,就跳过了以上的检查
var s3 = new Server(80, config.hostname); // 更好的办法是显式地对undefined进行检查
function Server(port, hostname) {
if (hostname === undefined) {
hostname = "localhost";
}
hostname = String(hostname);
// ...
}
一种替代方案是进行真值推断(參见Item 3):
function Server(port, hostname) {
hostname = String(hostname || "localhost");
// ...
}
根据是undefined在做真值推断时会返回false,因此默认值localhost会生效。
可是须要注意在某些情况下使用真值推断也是不安全的。
当一个函数可以接受空的字符串作为合法參数时。进行真值推断就会将传入的空字符串替换为默认值。类似的,假设一个函数可以接受数字0(或者特殊的NaN)作为合法參数,真值推断也会将它替换成默认值。
比方,以下的API用来通过传入元素的宽度和高度进行创建。
假设没有传入。则使用默认值:
var c1 = new Element(0, 0); // width: 0, height: 0
var c2 = new Element(); // width: 320, height: 240 function Element(width, height) {
this.width = width || 320; // wrong test
this.height = height || 240; // wrong test
// ...
} var c1 = new Element(0, 0); c1.width; // 320
c1.height; // 240
当我们传入0时,真值推断会将它替换成默认值。然而这并非我们想要的行为。更好的方式是显式对undefined进行推断:
function Element(width, height) {
this.width = width === undefined ? 320 : width;
this.height = height === undefined ? 240 : height;
// ...
} var c1 = new Element(0, 0); c1.width; // 0
c1.height; // 0 var c2 = new Element();
c2.width; // 320
c2.height; // 240
总结
- 不要使用undefined来表达除了缺失特定值外的不论什么其它意义。
- 在须要表达特殊情况时。不要使用undefined或者null。
而是使用更具表达性的字符串或者对象。
- 在函数中显式地对參数进行undefined检查,而不要依赖于诸如arguments.length等检查方法。
- 对于可以接受真值推断返回false的特殊值(如0,NaN,null,""),不要使用真值推断。
Effective JavaScript Item 54 将undefined视为"没有值"的更多相关文章
- Effective JavaScript Item 55 接受配置对象作为函数參数
接受配置对象作为函数參数 尽管保持函数接受的參数的顺序非常重要,可是当函数可以接受的參数达到一定数量时.也会让用户非常头疼: var alert = new Alert(100, 75, 300, 2 ...
- Effective JavaScript Item 31 优先使用Object.getPrototypeOf,而不是__proto__
本系列作为Effective JavaScript的读书笔记. 在ES5中引入了Object.getPrototypeOf作为获取对象原型对象的标准API.可是在非常多运行环境中.也提供了一个特殊的_ ...
- Effective JavaScript Item 21 使用apply方法调用函数以传入可变參数列表
本系列作为Effective JavaScript的读书笔记. 以下是一个拥有可变參数列表的方法的典型样例: average(1, 2, 3); // 2 average(1); // 1 avera ...
- Effective JavaScript Item 46 优先使用数组而不是Object类型来表示有顺序的集合
本系列作为Effective JavaScript的读书笔记. ECMAScript标准并没有规定对JavaScript的Object类型中的属性的存储顺序. 可是在使用for..in循环对Objec ...
- Effective JavaScript Item 37 认识this的隐式指向
本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔.所以在实现CSV Reader时,须要支持不同的分隔符.那么,非常自然的一种实现就是将分隔符作为 ...
- Effective JavaScript Item 10 避免使用with
本系列作为Effective JavaScript的读书笔记. Item 9:避免使用withkeyword 重点: 设计withkeyword本来是为了让代码变简洁,可是却起到了相反的效果.比方: ...
- Effective JavaScript Item 39 绝不要重用父类型中的属性名
本系列作为Effective JavaScript的读书笔记. 假设须要向Item 38中的Actor对象加入一个ID信息: function Actor(scene, x, y) { this.sc ...
- Effective JavaScript Item 22 使用arguments来创建接受可变參数列表的函数
本系列作为Effective JavaScript的读书笔记. 在Item 21中,介绍了结合apply方法实现的可变參数列表函数average,它实际上仅仅声明了一个数组作为參数,可是利用apply ...
- Effective JavaScript Item 40 避免继承标准类型
本系列作为Effective JavaScript的读书笔记. ECMAScript标准库不大.可是提供了一些重要的类型如Array,Function和Date.在一些场合下.你或许会考虑继承当中的某 ...
随机推荐
- luogu3370 【模板】字符串哈希
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> ...
- LiveScript 操作符
The LiveScript Book The LiveScript Book 操作符 数字 标准的数学操作符: 1.1 + 2 # => 32.3 - 4 # => -13.6 ...
- ICM Technex 2018 and Codeforces Round #463 (Div. 1 + Div. 2, combined)
靠这把上了蓝 A. Palindromic Supersequence time limit per test 2 seconds memory limit per test 256 megabyte ...
- JS数组的下标如果是字符串的排序
var test = []; test['0'] = 0; test['1'] = 1; test['2'] = 2; 这样一个数组的排序方式是字符为‘1’的数组元素排第一,为‘0’的排在最后
- 二进制<1>
Matrix67:位运算简介及实用技巧(一) 基础篇 什么是位运算? 程序中的所有数在计算机内存中都是以二进制的形式储存的.位运算说穿了,就是直接对整数在内存中的二进制位进行操作.比如,and运 ...
- Django notes III: Dynamic filtering
EXTRACTED from the Django document It's a common need to filter down the objects given in a list pag ...
- BZOJ1879 [Sdoi2009]Bill的挑战 【状压dp】
题目 输入格式 本题包含多组数据. 第一行:一个整数T,表示数据的个数. 对于每组数据: 第一行:两个整数,N和K(含义如题目表述). 接下来N行:每行一个字符串. T ≤ 5,M ≤ 15,字符串长 ...
- Eclipse + Apache Axis2 发布RESTful WebService(二)配置开发环境
1. 下载axis2相关软件地址:http://axis.apache.org/axis2/java/core/download.html 2. 安装插件:将axis2-eclipse-codegen ...
- [TJOI2018] Xor 异或 (可持久化Trie,树链剖分)
题目描述 现在有一颗以 1 为根节点的由 n 个节点组成的树,树上每个节点上都有一个权值 \(v_i\).现在有 Q 次操作,操作如下: 1 x y :查询节点 x 的子树中与 y 异或结果的最大值. ...
- bzoj 3143 [Hnoi2013]游走 期望dp+高斯消元
[Hnoi2013]游走 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3394 Solved: 1493[Submit][Status][Disc ...