{"action":"opened","issue":{"url":"https://api.github.com/repos/yogee412/Star/issues/2","labels_url":"https://api.github.com/repos/yogee412/Star/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/yogee412/Star/issues/2/comments","events_url":"https://api.github.com/repos/yogee412/Star/issues/2/events","html_url":"https://github.com/yogee412/Star/issues/2","id":105060723,"number":2,"title":"【转】探索JavaScript中Null和Undefined的坑","user":{"login":"yogee412","id":12248555,"avatar_url":"https://avatars.githubusercontent.com/u/12248555?v=3","gravatar_id":"","url":"https://api.github.com/users/yogee412","html_url":"https://github.com/yogee412","followers_url":"https://api.github.com/users/yogee412/followers","following_url":"https://api.github.com/users/yogee412/following{/other_user}","gists_url":"https://api.github.com/users/yogee412/gists{/gist_id}","starred_url":"https://api.github.com/users/yogee412/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yogee412/subscriptions","organizations_url":"https://api.github.com/users/yogee412/orgs","repos_url":"https://api.github.com/users/yogee412/repos","events_url":"https://api.github.com/users/yogee412/events{/privacy}","received_events_url":"https://api.github.com/users/yogee412/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-09-06T01:27:33Z","updated_at":"2015-09-06T01:27:33Z","closed_at":null,"body":"\r\n\r\n当讨论JavaScript中的原始数据类型时，大多数人都知道从String、Number到Boolean的基本知识。这些原始类型相当简单，行为符合常识。但是，本文将更多关注独特的原始数据类型Null和Undefined，是什么让它们如此相似，却又似是而非。\r\n\r\nAD：\r\n\r\n当讨论JavaScript中的原始数据类型时，大多数人都知道从String、Number到Boolean的基本知识。这些原始类型相当简单，行为符合常识。但是，本文将更多关注独特的原始数据类型Null和Undefined，是什么让它们如此相似，却又似是而非。\r\n\r\n理解Null和Undefined\r\n\r\n在JavaScript中，null是字面量同时也是语言中的关键字，用来表示无法识别的对象值。换句话说，这用来表示“无值（no value）”。虽然相似，undefined实际上代表了不存在的值（non-existence of a value）。都是完全不可变的，没有属性和方法，也不能给其属性赋值。事实上,试图访问或定义一个属性将会引发一个类型错误（TypeError）。正 如他们的名字暗示的那样，他们是完全无效的值。\r\n\r\n没有值代表的布尔值是false，这意味着他们在条件上下文中会被被计算为false，如if语句。使用相等操作符(= =)比较这两个值和其他false值,他们并不等于除了自己：\r\n\r\n    null == 0; // false \r\n    undefined == \"\"; // false \r\n    null == false; // false \r\n    undefined == false; // false \r\n    null == undefined; // true \r\n\r\n尽管如此，和其他相似之处，但null和undefined并不是等价的。每个作为其独特的类型的唯一成员,undefined是Undefined类型和null是Null类型。使用全等操作符（===）比较这两个值，这要求类型和值都相等，下面证明这一点：\r\n\r\n    undefined === null; // false \r\n\r\n这是一个重要的区别，服务于不同的目的和理由。区分这两个值，你可以认为undefined代表一个意想不到的没有值而null作为预期没有值的代表。\r\n\r\n产生Undefined\r\n\r\n有许多的方法产生一个undefined值的代码。它通常遇到当试图访问一个不存在的值时。在这种情况下，在JavaScript这种动态的弱类型语言中，只会默认返回一个undefined值，而不是上升为一个错误。\r\n\r\n任何声明变量时没有提供一个初始值，都会有一个为undefined的默认值：\r\n\r\n    var foo; // 默认值为 undefined \r\n\r\n当试图访问一个不存在的对象属性或数组项时，返回一个undefined值：\r\n\r\n    var array = [1, 2, 3]; \r\n    var foo = array.foo; // foo 属性不存在, 返回 undefined \r\n    var item = array[5]; // 数组中没有索引为5的项，返回 undefined \r\n\r\n如果省略了函数的返回语句,返回undefined：\r\n\r\n    var value = (function(){})(); // 返回 undefined \r\n\r\n函数调用时未提供的值结果将为undefined参数值：\r\n\r\n    (function(undefined){ \r\n        // 参数是 undefined \r\n    })(); \r\n\r\nvoid操作符也可以返回一个undefined值。像Underscore的库使用它作为一个防御式的类型检查，因为它是不可变的，可以在任何上下文依赖返回undefined：\r\n\r\n    function isUndefined(obj){ \r\n        return obj === void 0; \r\n    } \r\n\r\n最后，undefined是一个预定义的全局变量(不像null关键字)初始化为undefined值：\r\n\r\n    'undefined' in window; // true \r\n\r\nECMAScript 5中，这个变量是只读的，以前并非如此。\r\n\r\nNull的用例\r\n\r\nnull的用例是使他与众不同的主要方面，因为不像undefined，null被认为是更有用。这正是为什么typeof操作符作用于null值 时返回“object”。最初的理由是，现在仍然是，通常用作一个空引用一个空对象的预期,就像一个占位符。typeof的这种行为已经被确认为一个错 误，虽然提出了修正，出于后兼容的目的，这一点已经保持不变。\r\n\r\n这就是为什么JavaScript环境从来没有设置一个值为null；它必须以编程方式完成。正如文档MDN所说：\r\n\r\n在API中，null是经常检索对象的地方可以预期，但没有相关的对象。\r\n\r\n这适用于DOM，它是独立于语言的，不属于ECMAScript规范的范围。因为它是一个外部API，试图获取一个不存在的元素返回一个null值，而不是undefined。\r\n\r\n一般来说,如果你需要给一个变量或属性指定一个不变值，将它传递给一个函数，或者从一个函数返回null，null几乎总是最好的选择。简而言之，JavaScript使用undefined并且程序员应该使用null。\r\n\r\nnull的另一个可行的用例，也被认为是良好的实践是一个显式指定变量为无效(object= null)当一个引用不再是必需的。通过分配null值，有效地清除引用，并假设对象没有引用其他代码，指定垃圾收集，确保回收内存。\r\n\r\n深入挖掘\r\n\r\n使null和undefined像黑洞的不只是他们的行为，而是在他们在JavaScript环境的内部的处理方式。他们似乎通常并不具有同样的关联特征与其他原生或内置对象。\r\n\r\n在ES5中，Object.prototype.toString方法，已经成为实际的类型检查标准，这在null和undefined中被证明是一致的：\r\n\r\n    Object.prototype.toString.call(null); // [object Null] \r\n    Object.prototype.toString.call(undefined); // [object Undefined] \r\n\r\n然而，Object.prototype.toString方法实际上并不是检索null的内部[[Class]]属性或undefined的公开构造函数。根据文档，以下步骤发生在被调用过程中：\r\n\r\n    如果值是undefined，返回“[object Undefined]”。\r\n    如果这个值为null，则返回“[object Null]”。\r\n    让O作为调用ToObject同时传递this值作为参数的结果值。\r\n    让class是O的内部属性[[Class]]的值。\r\n    返回的结果连接三个字符串“[object ”，class，和“]”的结果的字符串值。\r\n\r\n该方法执行一个简单的字符串返回值，如果它检测到null或undefined和其他对象统一的功能。在整个规范中这是很常见的，因为当遇到null和undefined值时大多数方法包含一个简单的捕捉并返回。事实上，没有迹象表明他们包含与任何原生对象相关联的内部属性。就好像他们不是对象。我很想知道如果一个JavaScript的原生环境内部实际存在的显式方案会怎样？也许有人更熟悉一个可以参与的实现。\r\n\r\n结论\r\n\r\n无论这些原生对象多么不寻常，理解null和undefined之间的差异，和他们在JavaScript的语言基础中截然不同的角色。它可能不能使你的应用程序有所突破，但是一般来说，它仅被证明在开发和调试中长期有益。\r\n\r\n本文为翻译文章，原文为“Exploring the Abyss of Null and Undefined in JavaScript”，本文较难以理解，可参照原文便于理解。\r\n\r\n原文链接：http://blog.jobbole.com/54828/"}}