《编写可维护的JavaScript》读书笔记之编程实践-避免“空

2019-01-07  本文已影响3人  游学者灬墨槿

避免“空比较”

在 JavaScript 中,我们常会看到这种代码:变量与 null 的比较,用来判断变量是否被赋予一个合理的值。比如:

var Controller = {
    process : function(items) {
        // 不好的写法
        if(items !== null) {
            items.sort();
            items.forEach(function(item) {
                // 执行一些逻辑
            });
        }
    }
};

检测原始值

如果你希望一个值是字符串、数字、布尔值或 undefined,最佳选择是使用 typeof 运算符。typeof 运算符会返回一个表示值的类型的字符串。

typeof 语法:

typeof variable
/*
 * 尽管这是合法的 JavaScript 语法,这种用法让 typeof
 * 看起来像一个函数而非运算符。因此,更推荐无括号的写法
 */
typeof(variable)

检测 4 种原始值的做法:

// 检测字符串
if(typeof name === "string") {
    anotherName = name.substring(3);
}

// 检测数字
if(typeof count === "number") {
    unpdateCount(count);
}

// 检测布尔值
if(typeof found === "boolean" && found) {
    message("Found!");
}

// 检测 undefined
if(typeof MyApp === "undefined") {
    MyApp = {
        // 其他的代码
    };
}

注意:

typeof 运算符用于一个未声明的变量也不会报错。未定义的变量和值为 undefined 的变量通过 typeof 都将返回 "undefined"。

关于 null:

// 如果你需要检测 null,则使用这种方法
var element = document.getElementById("my-div");
if(element !== null) {
    element.className = "found";
}
/*
 * 如果 DOM 元素不存在,则获得的节点的值为 null。这个方法要么返回一个节点,要么返回 null。
 * 由于这时 null 是可预见的一种输出,则可以使用 !== 来检测返回结果。

检测引用值

引用值也称作对象(object),在 JavaScript 中除了原始值之外的值都是引用。

常见的内置引用类型:

对引用值的检测:

var arr = [1, 2],
    obj = {name: "clvsit"},
    date = Date(),
    reg = RegExp(),
    err = Error();
console.log(typeof arr === "object"); // true
console.log(typeof obj === "object"); // true
console.log(typeof date === "object"); // false
console.log(typeof reg === "object"); // true
console.log(typeof err === "object"); // true

从上述示例可以发现,typeof date === "object" 返回 false,为什么会这样?

console.log(typeof date); // 'string'
console.log(date.toString()); // "Sat Jan 05 2019 15:06:28 GMT+0800 (中国标准时间)"
console.log(date); // "Sat Jan 05 2019 15:06:28 GMT+0800 (中国标准时间)"

实践可知,在使用 Date 对象时会自动调用 Date 对象的 toString() 方法,因此 typeof date 得到的结果为 ‘string’。

instanceof:

value instanceof constructor
// 检测日期
if(value instanceof Date) {
    console.log(value.getFullYear());
}

// 检测正则表达式
if(value instanceof ReqExp) {
    if(value.test(anotherValue)) {
        console.log("Matches");
    }
}

// 检测 Error
if(value instanceof Error) {
    throw value;
}
  1. instanceof 不仅检测构造这个对象的构造器,还检测原型链。而原型链包含了很多信息,包括定义对象所采用的继承模式。比如,默认情况下,每个对象都继承来自 Object,因此每个对象的 value instanceof Object 都会返回 true(使用 value instanceof Object 来判断对象是否属于某个特定类型的做法并非最佳)。
var now = new Date();

console.log(now instanceof Object);  // true
console.log(now instanceof Date);    // true
  1. instanceof 运算符也可以检测自定义的类型(最好的做法)。
function Person(name) {
    this.name = name;
}

var me = new Person("Nicholas");

console.log(me instanceof Object);  // true
console.log(me instanceof Person);  // true

存在的限制:

// true
frameAPersonInstance instanceof frameAPerson;

// false
frameAPersonInstance instanceof frameBPerson;

检测函数

从技术上讲,JavaScript 中的函数是引用类型,同样存在 Function 构造函数,每个函数都是其实例。

function myFunc() {}

// 不好的写法
console.log(myFunc instanceof Function);  // true
  1. instanceof 不能跨帧(frame)使用,因为每个帧都有各自的 Function 构造函数。
  2. typeof 运算符也是可以用于函数的,返回 "function"。检测函数最好的方法是使用 typeof,因为它可以跨帧使用。
function myFunc() {}

// 好的写法
console.log(typeof myFunc === "function");  // true
  1. typeof 检测函数的限制:在 IE 8 和更早版本的 IE 浏览器中,使用 typeof 来检测 DOM 节点(比如 document.getElementById())中的函数都返回 "object" 而不是 "function"。之所以出现这种现象是因为浏览器对 DOM 的实现有差异。简言之,早期的 IE 并没有将 DOM 实现为内置的 JavaScript 方法,导致内置 typeof 运算符将这些函数识别为对象。
  2. 在 IE 8 及早期版本,开发者往往通过 in 运算符来检测 DOM 的方法。
// 检测 DOM 方法
if("querySelectorAll" in document) {
    images = document.querySelectorAll("img");
}
  1. 尽管使用 in 检测 DOM 方法不是最理想的方法,但如果想在 IE 8 及更早浏览器中检测 DOM 方法是否存在,这是最安全的做法。在其他所有情形中,typeof 运算符是检测 JavaScript 函数的最佳选择。

检测数组

JavaScript 最古老的跨域问题之一就是在帧之间来回传递数组。因为每个帧都有各自的 Array 构造函数,因此一个帧中的实例在另外一个帧里面不会被识别。

  1. 鸭式辨型(duck typing)
// 采用鸭式辨型的方法检测数组
function isArray(value) {
    return typeof value.sort === "function";
}
/*
 * 缺陷:数组是唯一包含 sort() 方法的对象。
 * 如果传入 isArray() 的参数是一个包含 sort() 
 * 方法的对象,也会返回 true。
 */
var obj = {
    sort: function () {
    
        // ...
    }
};
isArray(obj) // true; 
  1. Kangax 方案
function isArray(value) {
    return Object.prototype.toString.call(value) === "[object Array]";
}
function isArray(value) {
    if(typeof Array.isArray === "function") {
        return Array.isArray(value);
    } else {
        return Object.prototype.toString.call(value) === "[object Array]";
    }
}

检测属性

另外一种用到 null(以及 undefined)的场景是当检测一个属性是否在对象中存在时。

// 不好的写法:检测假值
if(object[propertyName]) {
    // 一些代码
}

// 不好的写法:和 null 相比较
if(object[propertyName] !== null) {
    // 一些代码
}

// 不好的写法:和 undefined 比较
if(object[propertyName] !== undefined) {
    // 一些代码
}
var object = {
    count : 0,
    related : null
}

// 好的写法
if("count" in object) {
    // 这里的代码会执行
}

// 不好的写法
if(object["count"]) {
    // 这里的代码不会执行
}

// 好的写法
if("related" in object) {
    // 这里的代码会执行
}

// 不好的写法:检测是否为 null
if(object["related"] !== null) {
    // 这里代码不会执行
}

hasOwnProperty():

// 对于所有非 DOM 对象来说,这是好的写法
if(object.hasOwnProperty("related")) {
    // 执行这里的代码
}

// 如果你不确定是否为 DOM 对象,则这样来写
if("hasOwnProperty" in object && object.hasOwnProperty("related")) {
    // 执行这里的代码
}
上一篇 下一篇

猜你喜欢

热点阅读