资讯专栏INFORMATION COLUMN

[译]Understanding javascript's 'undefined

galaxy_robot / 2959人阅读

摘要:一个表示编译器检测到一个无效的引用值。在实际情况中,往往是在获取一个未被赋值的引用时被抛出。任何一个函数上下文都有一个被称为活动对象的变量对象。没有找到的话,就会认为引用名没有基础值并抛出的错误。下没有下的属性仅存在于被启动的情况下。

和其他语言相比,javascript中的对于undefined的理解还是有点让人困惑的。特别是试着理解ReferenceErrors错误("x is not defined")以及在编码过程中如何去避免这些错误总让人感到比较困惑。

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

undefined是什么?

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

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

undefined(值)是一个原始值,是未定义类型的基础值。任何未被赋值的属性值都可以假设为undefined。无返回值或者返回值为空的函数最后执行得到的值都未undefined。未设定的函数参数值也为undefined.

    var a;
    typeof a; // undefined
    
    window.b;
    typeof window.b; // undefined
    
    var c = (function() {})();
    typeof c; // undefined
    
    var d = (function(e) {return e})();
    typeof d; // undefined

undefined(变量)是初始值为undefined的全局属性,既然它是个全局属性,那么我们也可以通过变量来获取它。例如我在这篇文章里面像这样将它作为一个变量来获取它。

    typeof undefined; // undefined
    
    var f = 2;
    f = undefined;  //重新将变量f赋值为变量undefined
    typeof f; // undefined

在ECMA3当中,它的值可以重新被赋值:

    undefined = "washing machine";
    typeof undefined;  // string
    
    f = undefined;
    typeof f; // string
    f; // "washing machine"

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

And then there"s null?

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

So what"s a RefernceError?

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

在实际情况中,ReferenceError往往是在Javascript获取一个未被赋值的引用时被抛出。
注意下在不同浏览器中,ReferenceError现在的不同的语法错误提示信息。正如我们看到的,在不同浏览器中,错误信息并非特别的清楚明了。

    alert(foo);
    
    //FF/Chrome: foo is not defined
    //IE: foo is undefined
    //Safari: can"t find variable foo
Still not clear ..."unresolvable reference"?

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

如果这个引用是一个属性,那么基础值及这个引用分别位于点号的两侧:

    window.foo; // base value = window, reference name = foo;
    a.b; //base value = a, reference name = b;
    myObj["create"]; //base value = myObj, reference name = create;
    //Safari, Chrome, IE8+ only
    Object.defineProperty(window, "foo", {value: "hello"});
    //base value = window. reference name = foo;

对于引用变量来说,基础值是当前执行上下文的变量对象。全局上线文的变量对象就是全局对象自己(浏览器当中是window)。任何一个函数上下文都有一个被称为活动对象的变量对象。(这个活动对象取决于调用这个函数的执行context)

    var foo;    //base value = window, reference name = foo;
    function a() {
        var b;  // base value = ActivationObject, reference name = b;
    }

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

因此,如果点号前面的值为undefined,那么这个属性引用就是unresolved。下面的这个例子就会抛出一个ReferenceError的错误,但是这并非是因为TypeError在此之前就被抛出。这是因为一个属性的基础值是属于CheckObjectCoercible,当试着将一个Undefined类型的转化为一个Object,那么这种情况下会抛出TypeError;

    var foo;    
    foo.bar;    //TypeError (base value, foo, is undefined)
    bar.baz;    //ReferenceError (bar is unresolvable)
    undefined.foo   //TypeError (base value is undefined)

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

当引用被定义的时候既不是属性值也不是变量的时候将会抛出一个ReferenceError:

    foo;   //ReferenceError

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

But isn"t foo just a undeclared variable?

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

What about implicit globals?

未通过var关键字声明的标识符将会默认当做全局变量而被创建,但这只会在这些标识符被赋值的情况下才会生效。

    function a() {
        alert(foo);     // ReferenceError
        bar = [1, 2, 3]; // no error, bar is global
    }
    a();
    bar;    // [1, 2, 3]

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

When do I need to code against ReferenceErrors?

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

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

最后的结果就是,下面的代码在浏览器中运行时还是有可能会抛出ReferenceError的错误。

    console.log(new Date());
How do I code against variables that may not exist?

一种方式就是去通过使用typeof关键字去嗅探一个unresolvable reference,避免抛出ReferenceError错误:

    if (typeof console != "undefined") {
        console.log(new Date());
    }

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

幸运的是还有另外一种方式:我们已经知道基础值被定义了,但是属性未被定义是不会抛出ReferenceErrorconsole是全局对象的属性值,因此我们可以这样做:

    window.console && console.log(new Date());

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

原文链接

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88087.html

相关文章

  • [] RabbitMQ tutorials (3) ---- 'Pub/Sub&#039

    摘要:生产者只能把消息发到交换器。是否要追加到一个特殊的队列是否要追加到许多的队列或者丢掉这条消息这些规则被定义为交换类型。有一点很关键,向不存在的交换器发布消息是被禁止的。如果仍然没有队列绑定交换器,消息会丢失。 发布与订阅 (Publish/Subscribe) 在之前的章节中,我们创建了工作队列,之前的工作队列的假设是每个任务只被分发到一个worker。在这一节中,我们会做一些完全不一...

    zzir 评论0 收藏0
  • JS '严格模式'

    摘要:一篇简单的探索,这个严格模式出来很早了,很多人都有写过,而且官方文档很详细。 原文链接:乖小鬼的简书 为什么想到写这么一篇文章呢,来源在于回答一个 SG上面的问题。那么问题是这样子的。 var a = 2; function foo(){ console.log(this.a);} foo(); 以上代码,执行的结果是什么?? 如果你回答是2,那么对了多少? 只能说对了一半,为什么...

    sunnyxd 评论0 收藏0
  • JS 中为啥 ['1', '7', '11

    摘要:如果我们把非布尔值作为条件呢打开控制台并运行上述代码,会打印说明条件为真值。在中,真值指的是在布尔值上下文中转换后的值为真的值。两个能够建立元素间一一对应的集合称为互相对等集合。 showImg(https://segmentfault.com/img/bVbtSvt?w=720&h=360); 为了保证可读性,本文采用音译而非直译。 Javascript 一直是神奇的语言。 不相信我...

    yuanzhanghu 评论0 收藏0
  • 简单易懂的ECMA规范导读1 that's this

    摘要:本文不是标准的中文翻译,也不是的入门教程,本文虽然以的常见问题切入,但并不适合想要快速了解这些问题的人才是快速了解问题的正解。尽量以英文原版为基础,为了流畅,可能会使用某些名词的中文翻译,但会将匹配的英文名词以此种样式中出现一次以避免误解。 简单易懂的ECMA规范导读1 序 最近混SF,恰巧又逢工作方面有了NodeJS的机会,迫切地有教别人怎么写JS的需求, 我发现JS这个东西其实...

    yintaolaowanzi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<