将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. selenium 自动化测试 Chrome 大于 63 版本 不能重定向问题解决办法

    Chrome 一些信息: Chrome 63 以后,浏览器默认屏蔽了重定向 Chrome 63 版本,设置了禁止更新,有些情况还是会更新到最新版本 解决过程: 在博客上查到  selenium 里 给 ...

  2. 一个JS判断客户端是否已安装某个字体(Only IE)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. HDU——1215七夕节(因数和)

    七夕节 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submis ...

  4. BZOJ2285 [SDOI2011]保密 【01分数规划 + 网络流】

    题目 现在,保密成为一个很重要也很困难的问题.如果没有做好,后果是严重的.比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了. 当然,对保密最需求的当然是军方,其次才是像那个人.为了应付 ...

  5. BZOJ4556 [Tjoi2016&Heoi2016]字符串 【后缀数组 + 主席树 + 二分 + ST表】

    题目 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职 ...

  6. matlab 中的删除文件

    Matlab中有两种删除文件的方式: 一种是删除文件     delete()函数      //可以使用help  delete命令查询delete()函数的使用方法 delete('p1.jpg' ...

  7. [USACO15FEB]Superbull (最小生成树)

    题目链接 Solution 基本上就是个板子. 因为 \(n\) 很小,只有 \(2000\),所以直接暴力建图,然后跑最小生成树就好了. Code #include<bits/stdc++.h ...

  8. C#函数多返回值的方法

    C#以前都是不支持多返回值,当需要返回多个值的时候,就会感觉比较麻烦,通常的做法有 1.采用ref,out 关键字返回 2.定义类或者结构体 返回对象 C# 6.0出来了新的语法 Tuple 支付返回 ...

  9. 【CF1016A】Death Note(签到)

    题意:无限页的书,每页可以写m个名字,给你一个长度为n的序列,序列为你每天要写的名字数,输出你每天要翻的页数. n<=2e5,m,a[i]<=1e9 思路: #include<cst ...

  10. Java面试题—字符串

    如果明天你要参加一场面试,面试官有个题目是:说说String/StringBuffer/StringBuilder它们之间的区别?你会怎么回答呢?(脑补一下答案)今天,小编就来和大家详细聊聊字符串St ...