你不知道的JavaScript(中卷)1.3  值和类型_你不知道的JavaScript(中卷)1.3  值和类型试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > 你不知道的JavaScript(中卷) > 1.3  值和类型

你不知道的JavaScript(中卷)——1.3  值和类型

JavaScript 中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。 换个角度来理解就是,JavaScript 不做“类型强制”;也就是说,语言引擎不要求变量总是持有与其初始值同类型的值。一个变量可以现在被赋值为字符串类型值,随后又被赋值为数字类型值。 42 的类型为number,并且无法更改。而"42" 的类型为string。数字42 可以通过强制类型转换(coercion)为字符串"42"(参见第4 章)。 在对变量执行typeof 操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型,因为JavaScript 中的变量没有类型。 var a = 42; typeof a; // "number" a = true; typeof a; // "boolean" typeof 运算符总是会返回一个字符串: typeof typeof 42; // "string" typeof 42 首先返回字符串"number",然后typeof "number" 返回"string"。 1.3.1  undefined 和undeclared 变量在未持有值的时候为undefined。此时typeof 返回"undefined": var a; typeof a; // "undefined" var b = 42; var c; // later b = c; typeof b; // "undefined" typeof c; // "undefined" 大多数开发者倾向于将undefined 等同于undeclared(未声明),但在JavaScript 中它们完全是两回事。 已在作用域中声明但还没有赋值的变量,是undefined 的。相反,还没有在作用域中声明过的变量,是undeclared 的。 例如: var a; a; // undefined b; // ReferenceError: b is not defined 浏览器对这类情况的处理很让人抓狂。上例中,“b is not defined”容易让人误以为是“b is undefined”。这里再强调一遍,“undefined”和“is not defined”是两码事。此时如果浏览器报错成“b is not found”或者“b is not declared”会更准确。 更让人抓狂的是typeof 处理undeclared 变量的方式。例如: var a; typeof a; // "undefined" typeof b; // "undefined" 对于undeclared(或者not defined)变量,typeof 照样返回"undefined"。请注意虽然b 是一个undeclared 变量,但typeof b 并没有报错。这是因为typeof 有一个特殊的安全防范机制。 此时typeof 如果能返回undeclared(而非undefined)的话,情况会好很多。 1.3.2  typeof Undeclared 该安全防范机制对在浏览器中运行的JavaScript 代码来说还是很有帮助的,因为多个脚本文件会在共享的全局命名空间中加载变量。 很多开发人员认为全局命名空间中不应该有变量存在,所有东西都应该被封装到模块和私有/ 独立的命名空间中。理论上这样没错,却不切实际。然而这仍不失为一个值得为之努力奋斗的目标。好在ES6 中加入了对模块的支持,这使我们又向目标迈近了一步。 举个简单的例子,在程序中使用全局变量DEBUG 作为“调试模式”的开关。在输出调试信息到控制台之前,我们会检查DEBUG 变量是否已被声明。顶层的全局变量声明var DEBUG = true 只在debug.js 文件中才有,而该文件只在开发和测试时才被加载到浏览器,在生产环境中不予加载。 问题是如何在程序中检查全局变量DEBUG 才不会出现ReferenceError 错误。这时typeof 的安全防范机制就成了我们的好帮手: // 这样会抛出错误 if (DEBUG) { console.log( "Debugging is starting" ); } // 这样是安全的 if (typeof DEBUG !== "undefined") { console.log( "Debugging is starting" ); } 这不仅对用户定义的变量(比如DEBUG)有用,对内建的API 也有帮助: if (typeof atob === "undefined") { atob = function() { /*..*/ }; } 如果要为某个缺失的功能写polyfill(即衬垫代码或者补充代码,用来补充当前运行环境中缺失的功能),一般不会用var atob 来声明变量atob。如果在if 语句中使用var atob,声明会被提升(hoisted,参见《你不知道的JavaScript(上卷)》1 中的“作用域和闭包”部分)到作用域(即当前脚本或函数的作用域)的最顶层,即使if 条件不成立也是如此(因为atob 全局变量已经存在)。在有些浏览器中,对于一些特殊的内建全局变量(通常称为“宿主对象”,host object),这样的重复声明会报错。去掉var 则可以防止声明被提升。 还有一种不用通过typeof 的安全防范机制的方法,就是检查所有全局变量是否是全局对象的属性,浏览器中的全局对象是window。所以前面的例子也可以这样来实现: if (window.DEBUG) { // .. } if (!window.atob) { // .. } 注1:此书已由人民邮电出版社出版。——编者注 与undeclared 变量不同,访问不存在的对象属性(甚至是在全局对象window 上)不会产生ReferenceError 错误。 一些开发人员不喜欢通过window 来访问全局对象,尤其当代码需要运行在多种JavaScript 环境中时(不仅仅是浏览器,还有服务器端,如node.js 等),因为此时全局对象并非总是window。 从技术角度来说,typeof 的安全防范机制对于非全局变量也很管用,虽然这种情况并不多见,也有一些开发人员不大愿意这样做。如果想让别人在他们的程序或模块中复制粘贴你的代码,就需要检查你用到的变量是否已经在宿主程序中定义过: function doSomethingCool() { var helper = (typeof FeatureXYZ !== "undefined") ? FeatureXYZ : function() { /*.. default feature ..*/ }; var val = helper(); // .. } 其他模块和程序引入doSomethingCool() 时,doSomethingCool() 会检查FeatureXYZ 变量是否已经在宿主程序中定义过;如果是,就用现成的,否则就自己定义: // 一个立即执行函数表达式(IIFE,参见《你不知道的JavaScript(上卷)》 “作用域和闭包” // 部分的3.3.2节) (function(){ function FeatureXYZ() { /*.. my XYZ feature ..*/ } // 包含doSomethingCool(..) function doSomethingCool() { var helper = (typeof FeatureXYZ !== "undefined") ? FeatureXYZ : function() { /*.. default feature ..*/ }; var val = helper(); // .. } doSomethingCool(); })(); 这里,FeatureXYZ 并不是一个全局变量,但我们还是可以使用typeof 的安全防范机制来做检查,因为这里没有全局对象可用(像前面提到的window.___)。 还有一些人喜欢使用“依赖注入”(dependency injection)设计模式,就是将依赖通过参数显式地传递到函数中,如: function doSomethingCool(FeatureXYZ) { var helper = FeatureXYZ || function() { /*.. default feature ..*/ }; var val = helper(); // .. } 上述种种选择和方法各有利弊。好在typeof 的安全防范机制为我们提供了更多选择。

展开全文


推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《你不知道的JavaScript(中卷)》其他试读目录

• 1.1  类型
• 1.2  内置类型
• 1.3  值和类型 [当前]
• 1.4  小结
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  •