将undefined视为"没有值"

JavaScript中的undefined是一个特殊的值:当JavaScript没有提供详细的值时。它就会产生undefined。

比方:

  1. 未被赋值的变量的初始值就是undefined
  2. 訪问对象中不存在的属性会得到undefined
  3. 没有返回值的函数。undefined会作为其返回值
  4. 函数的參数没有提供时。它的值就是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

总结

  1. 不要使用undefined来表达除了缺失特定值外的不论什么其它意义。
  2. 在须要表达特殊情况时。不要使用undefined或者null。

    而是使用更具表达性的字符串或者对象。

  3. 在函数中显式地对參数进行undefined检查,而不要依赖于诸如arguments.length等检查方法。

  4. 对于可以接受真值推断返回false的特殊值(如0,NaN,null,""),不要使用真值推断。

Effective JavaScript Item 54 将undefined视为&quot;没有值&quot;的更多相关文章

  1. Effective JavaScript Item 55 接受配置对象作为函数參数

    接受配置对象作为函数參数 尽管保持函数接受的參数的顺序非常重要,可是当函数可以接受的參数达到一定数量时.也会让用户非常头疼: var alert = new Alert(100, 75, 300, 2 ...

  2. Effective JavaScript Item 31 优先使用Object.getPrototypeOf,而不是__proto__

    本系列作为Effective JavaScript的读书笔记. 在ES5中引入了Object.getPrototypeOf作为获取对象原型对象的标准API.可是在非常多运行环境中.也提供了一个特殊的_ ...

  3. Effective JavaScript Item 21 使用apply方法调用函数以传入可变參数列表

    本系列作为Effective JavaScript的读书笔记. 以下是一个拥有可变參数列表的方法的典型样例: average(1, 2, 3); // 2 average(1); // 1 avera ...

  4. Effective JavaScript Item 46 优先使用数组而不是Object类型来表示有顺序的集合

    本系列作为Effective JavaScript的读书笔记. ECMAScript标准并没有规定对JavaScript的Object类型中的属性的存储顺序. 可是在使用for..in循环对Objec ...

  5. Effective JavaScript Item 37 认识this的隐式指向

    本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔.所以在实现CSV Reader时,须要支持不同的分隔符.那么,非常自然的一种实现就是将分隔符作为 ...

  6. Effective JavaScript Item 10 避免使用with

    本系列作为Effective JavaScript的读书笔记. Item 9:避免使用withkeyword 重点: 设计withkeyword本来是为了让代码变简洁,可是却起到了相反的效果.比方: ...

  7. Effective JavaScript Item 39 绝不要重用父类型中的属性名

    本系列作为Effective JavaScript的读书笔记. 假设须要向Item 38中的Actor对象加入一个ID信息: function Actor(scene, x, y) { this.sc ...

  8. Effective JavaScript Item 22 使用arguments来创建接受可变參数列表的函数

    本系列作为Effective JavaScript的读书笔记. 在Item 21中,介绍了结合apply方法实现的可变參数列表函数average,它实际上仅仅声明了一个数组作为參数,可是利用apply ...

  9. Effective JavaScript Item 40 避免继承标准类型

    本系列作为Effective JavaScript的读书笔记. ECMAScript标准库不大.可是提供了一些重要的类型如Array,Function和Date.在一些场合下.你或许会考虑继承当中的某 ...

随机推荐

  1. 命令行客户端操作pg数据库常用操作

    登录 # su - postgres -c "psql" 或者 $psql -U user_name -d database_name -h serverhost psql (10 ...

  2. Leetcode 419.甲板上的战舰

    甲板上的战舰 给定一个二维的甲板, 请计算其中有多少艘战舰. 战舰用 'X'表示,空位用 '.'表示. 你需要遵守以下规则: 给你一个有效的甲板,仅由战舰或者空位组成. 战舰只能水平或者垂直放置.换句 ...

  3. 紫书第三章训练1 D - Crossword Answers

    A crossword puzzle consists of a rectangular grid of black and white squares and two lists of defini ...

  4. fedora安装rails缺少js runtime和cannot load such file -- sqlite3/sqlite3_native解决办法

    装完rails后创建应用程序: rails new demo 进入创建的demo文件夹 cd demo 检查安装环境 rake about 这时出现错误 Could not find a JavaSc ...

  5. Unity3D - UGUI的手动搭建

    了解UGUI组件的搭建方式,有助于搭建我们自己的UI界面. Text 文本 text 是UGUI中的基本控件,在Hierarchyi面板创建一个空物体 - 给这个空物体添加一个Text组件即可实现与t ...

  6. C遇到的编译错误整理

    1: Permission denied collect2.exe: error: ld returned exit status c:/mingw/bin/../lib/gcc/mingw32/6. ...

  7. 【bzoj1965】 [Ahoi2005]SHUFFLE 洗牌 欧拉定理

    题目描述 为了表彰小联为Samuel星球的探险所做出的贡献,小联被邀请参加Samuel星球近距离载人探险活动. 由于Samuel星球相当遥远,科学家们要在飞船中度过相当长的一段时间,小联提议用扑克牌打 ...

  8. ACM程序设计选修课——Problem E:(ds:图)公路村村通(Prim)

    问题 E: (ds:图)公路村村通 时间限制: 1 Sec  内存限制: 128 MB 提交: 9  解决: 5 题目描述 现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本, ...

  9. cssText批量修改样式

    cssText所有浏览器都支持. cssText 的使用 obj.style.cssText = " width:200px;position:absolute;left:100px;&qu ...

  10. 乘法逆元__C++

    在开始之前我们先介绍3个定理: 1.乘法逆元(在维基百科中也叫倒数,当然是 mod p后的,其实就是倒数不是吗?): 如果ax≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p ...