UDN-企业互联网技术人气社区

板块导航

浏览  : 761
回复  : 2

[原生js] Understanding JavaScript's 'undefined'

[复制链接]
独领风骚的头像 楼主
发表于 2017-2-4 10:39:53 | 显示全部楼层 |阅读模式
  和其他语言相比, JavaScript 中的对于 undefined 的理解还是有点让人困惑的。特别是试着理解 ReferenceErrors 错误("x is not defined")以及在编码过程中如何去避免这些错误总让人感到比较困惑。

  这篇文章是我整理的关于这个知识点的内容。如果你对于 JavaScript 中的变量以及属性还不是很熟悉的话,你可以看看我之前写的[文章]()

  undefined是什么?

  在 JavaScript 中,存在着 Undefined(基本类型) , undefined(值) ,以及 defedined(变量) 。

  Undefined(基本类型)是js内置的基本类型之一( String , Number , Boolean , Null , Object )

  undefined(值)是一个原始值,是未定义类型的基础值。任何未被赋值的属性值都可以假设为 undefined 。无返回值或者返回值为空的函数最后执行得到的值都未 undefined 。未设定的函数参数值也为 undefined .
  1. var a;
  2.     typeof a; // undefined
  3.    
  4.     window.b;
  5.     typeof window.b; // undefined
  6.    
  7.     var c = (function() {})();
  8.     typeof c; // undefined
  9.    
  10.     var d = (function(e) {return e})();
  11.     typeof d; // undefined
复制代码

  undefined(变量)是初始值为 undefined 的全局属性,既然它是个全局属性,那么我们也可以通过变量来获取它。例如我在这篇文章里面像这样将它作为一个变量来获取它。
  1. typeof undefined; // undefined
  2.    
  3.     var f = 2;
  4.     f = undefined;  //重新将变量f赋值为变量undefined
  5.     typeof f; // undefined
复制代码

  在ECMA3当中,它的值可以重新被赋值:
  1. undefined = 'washing machine';
  2.     typeof undefined;  // string
  3.    
  4.     f = undefined;
  5.     typeof f; // string
  6.     f; // 'washing machine'
复制代码

  不用说,将undefined重新赋值是一个比较糟糕的用法。事实上再ECMA5当中这种做法也是不被允许的。

  And then there's null?

  大致上我们了解这两者之间的区别,但是还是需要重新复述一遍: undefined 和 null 相比, undefined 是一个原始值,它表示一个缺省值。 undefined 和 null 之间唯一相似的地方就是它们都能被通过类型转换成 false 。

  So what's a RefernceError?

  一个 ReferenceError 表示编译器检测到一个无效的引用值。

  在实际情况中, ReferenceError 往往是在 JavaScript 获取一个未被赋值的引用时被抛出。

  注意下在不同浏览器中, ReferenceError 现在的不同的语法错误提示信息。正如我们看到的,在不同浏览器中,错误信息并非特别的清楚明了。
  1. alert(foo);
  2.    
  3.     //FF/Chrome: foo is not defined
  4.     //IE: foo is undefined
  5.     //Safari: can't find variable foo
复制代码

  Still not clear ..."unresolvable reference"?

  在ECMA标准中,一个引用值包含一个引用名称及一个基本值。

  如果这个引用是一个属性,那么基础值及这个引用分别位于点号的两侧:
  1. window.foo; // base value = window, reference name = foo;
  2.     a.b; //base value = a, reference name = b;
  3.     myObj['create']; //base value = myObj, reference name = create;
  4.     //Safari, Chrome, IE8+ only
  5.     Object.defineProperty(window, 'foo', {value: 'hello'});
  6.     //base value = window. reference name = foo;
复制代码

  对于引用变量来说,基础值是当前执行上下文的 变量对象 。全局上线文的变量对象就是全局对象自己(浏览器当中是 window )。任何一个函数上下文都有一个被称为活动对象的变量对象。(这个活动对象取决于调用这个函数的执行 context )
  1. var foo;    //base value = window, reference name = foo;
  2.     function a() {
  3.         var b;  // base value = <code>ActivationObject</code>, reference name = b;
  4.     }
复制代码

  如果一个引用的基础值是 undefined 的话,那么这个引用就被认为 unresolvable

  因此,如果点号前面的值为 undefined ,那么这个属性引用就是 unresolved 。下面的这个例子就会抛出一个 ReferenceError 的错误,但是这并非是因为 TypeError 在此之前就被抛出。这是因为一个属性的基础值是属于 CheckObjectCoercible ,当试着将一个 Undefined 类型的转化为一个 Object ,那么这种情况下会抛出 TypeError ;
  1. var foo;   
  2.     foo.bar;    //TypeError (base value, foo, is undefined)
  3.     bar.baz;    //ReferenceError (bar is unresolvable)
  4.     undefined.foo   //TypeError (base value is undefined)
复制代码

  如果使用 var 关键字,那么将会确保变量对象始终有基础值,那么就不会出现引用变量 unresolvable 的情况。

  当引用被定义的时候既不是属性值也不是变量的时候将会抛出一个 ReferenceError :
  1. foo;   //ReferenceError
复制代码

  JavaScript 检测到引用名 foo 没有明确的基础值,因此就会去寻找属性名为 foo 的变量对象。没有找到的话,就会认为引用名 foo 没有基础值并抛出 ReferenceError 的错误。

  But isn't foo just a undeclared variable?

  技术上来说不是。尽管有时候我们觉得 undeclared variable 是有利用我们去排查 bug 。但是事实上如果一个变量未被声明的话也就不是一个变量。

  What about implicit globals?

  未通过 var 关键字声明的标识符将会默认当做全局变量而被创建,但这只会在这些标识符被赋值的情况下才会生效。
  1. function a() {
  2.         alert(foo);     // ReferenceError
  3.         bar = [1, 2, 3]; // no error, bar is global
  4.     }
  5.     a();
  6.     bar;    // [1, 2, 3]
复制代码

  这确实让人有点困惑。如果 JavaScript 检测到 unresolvable 的引用就直接抛出 ReferenceErrors 就更好了。(事实上在严格模式下 JavaScript 确实是这样做的)

  When do I need to code against ReferenceErrors?

  如果你的代码写的很合理。我们发现在典型的用法中只有一种方式会遇到 unresolvable reference : 当使用属性或者变量的句法不正确的时候。在大多数情况下,如果你能确保声明变量的时候使用 var 关键字时即可避免这种情况。在代码运行过程中唯一可能会遇到的情况就是引用的变量仅仅存在于部分浏览器或者第三方的代码当中。

  一个比较好的例子就是 console .在 webkit 浏览器中, console 是内置的, console 这个属性可以在任何地方获取到。 Firefox 中 console 属性取决于 Firebug 是否被安装以及被打开使用。 IE7 下没有 console , IE8 下的 console 属性仅存在于 IE Developer Tools 被启动的情况下。 Opera 明显是有 console 属性的,但是我从来没用使用过。

  最后的结果就是,下面的代码在浏览器中运行时还是有可能会抛出 ReferenceError 的错误。
  1. console.log(new Date());
复制代码

  How do I code against variables that may not exist?

  一种方式就是去通过使用 typeof 关键字去嗅探一个 unresolvable reference ,避免抛出 ReferenceError 错误:
  1. if (typeof console != 'undefined') {
  2.         console.log(new Date());
  3.     }
复制代码

  然而这种方法对我来说太啰嗦了,更不用说合理了。我是比较反对使用 typeof 去进行类型检测的。

  幸运的是还有另外一种方式:我们已经知道基础值被定义了,但是属性未被定义是不会抛出 ReferenceError 。 console 是全局对象的属性值,因此我们可以这样做:
  1. window.console && console.log(new Date());
复制代码

  事实上在全局环境下仅仅只需要检测变量是否存在(函数中也存在着执行上下文,你可以决定哪些变量可以存在于你的函数当中)。因此理论上至少你可以避免使用 typeof 来消除 ReferenceError 的情况。

相关帖子

发表于 2017-2-4 10:40:23 | 显示全部楼层
LZ是闲人,天天发帖,坚定完毕
使用道具 举报

回复

发表于 2017-2-17 19:32:12 | 显示全部楼层
js框架越来越火了, 不过本人还是喜欢原生的好, 自己写类库, 自己来封装,用着方便。。。。
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部