将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. spring boot 启动慢的原因

    停留在Spring logo那里差不多4分钟 SpringBoot启动慢的原因应该是某些应用占用了spring config server默认的端口8888,然后SpringCloud进程有些引用了s ...

  2. python week08 并发编程之多进程--实践部分

    一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...

  3. css各属性浏览器的兼容情况

  4. 如何在 Rails 中搭配 Turbolinks 使用 Vue

    [Rails] Vue-outlet for Turbolinks 在踩了 Rails + Turbolinks + Vue 的許多坑後,整理  的作法並和大家分享. Initialize the A ...

  5. [python xml 学习篇][0]

    tree = ET.parse("Result.xml")root = tree.getroot()print type(root)print root.tag # 得到root ...

  6. 《人月神话》读书笔记(2)-week3

    为了确保团队中的每个人都能保持系统概念上的完整性,关于项目的书面规格说明是必不可少的.手册要描绘用户可见的一切,但不应支配实现的过程.光有规格说明也是不够的,会议也是必要的.书中提到的周例会会迅捷地给 ...

  7. poj 2195 最小费用最大流模板

    /*Source Code Problem: 2195 User: HEU_daoguang Memory: 1172K Time: 94MS Language: G++ Result: Accept ...

  8. java异常处理及400,404,500错误处理

        java代码中经常碰到各种需要处理异常的时候,比如什么IOException  SQLException  NullPointException等等,在开发web项目中,遇到异常,我现在做的就 ...

  9. Codeforces Round #345 (Div. 2)——B. Beautiful Paintings(贪心求上升序列个数)

    B. Beautiful Paintings time limit per test 1 second memory limit per test 256 megabytes input standa ...

  10. 刷题总结——date(ssoj)

    题目: 题目背景 SOURCE:NOIP2015-SHY-9 题目描述 小Y和小Z好不容易有机会相见啦,可是邪恶的小H却不想让他们相见.现在有一些城市,城市之间有双向路径相连,有路径相连的城市之间可以 ...