JavaScript中的类型(一)———— 类型
引入
类型:对语言引擎和开发人员来说,类型是值的内部特征,它定义了值的行为,以使其区别于其他值。
JavaScript是一门弱类型(偏向容忍隐式类型转换)语言,同时也是一门动态类型(编译的时候就知道每个变量的类型)语言。
没有严格的类型的限制,使得JavaScript成为了一门特别灵活的语言,但是也带了一些麻烦,例如强制类型转换。
内置类型
JavaScript中暂时有7种内置类型:
- 空值(null)
- 未定义(undefined)
- 布尔值(boolean)
- 数字(number)
- 字符串(string)
- 对象(object)
- 符号(symbol)
通常,我们可以通过typeof操作符来查看值的类型,它会返回类型的字符串值,但是并不是这七种类型和它们的字符串值并不是一一对应。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//下面六种类型均有同名的字符串值与之对应,'==='会返回true。
typeof undefined === 'undefined'
typeof false === 'boolsean'
typeof 3 === 'number'
typeof '3' === 'string'
typeof {number: 3} === 'object'
typeof Symbol() === 'symbol'
//对于null类型,存在一个bug(从JavaScript诞生就一直存在,以后估计也会存在),对null进行typeof操作,返回的值居然是'object';
typeof null === 'object' //true
typeof null === 'null' //false
//我们需要使用复合条件来检测null值的类型:
const a = null;
//对于&&操作,如果运算符左边的值为真,则该表达式直接返回右边的值,例如 'true && 3'表达式的结果为3。
(!a && typeof a === "object"); //true
函数
函数是’object’的一个子类型,函数是一个“可调用对象”,它有一个内部属性[[Call]],该属性使其可以被调用。但是,对函数进行typeof操作时,返回的值可不是’object’。1
2typeof function(){} === 'object' //false
typeof function(){} === 'function' //true
没错,就是’function’。看起来function就是另外一种内置类型,但是它不是。:)
值和类型
变量没有类型,值才有类型
JavaScript中的变量是没有类型的,只有值才具有类型,变量可以随时持有任何类型的值。前面提到过,JavaScript是一门动态弱类型的语言。例如:变量a持有的值为42,那么这个值的类型为number,并且无法直接更改42的类型(number),但可以通过强制转换将这42转换为’42’,此时值拥有的类型就为字符串(string)。
undefined and undeclared
变量未持有值时为undefined,此时typeof返回’undefined’。
已在作用域中声明但未赋值的比变量是undefined的,未在作用域声明过的变量是undeclared的。1
2
3let a;
a; //undefined
b; //ReferenceError:b is not defined,报错
每次看到“not defined”,总以为等价于”undefined”,真让人糟心,还有更糟心的:)1
2
3let a;
typeof a; //undefined
typeof b; //undefined
一向被认为比较安全的操作符typeof也会这样闹着玩:)
注:推荐一本必看的书《JavaScript语言精粹》,作者是Douglas Crockford(大神)。
安全防范机制
并不是没有办法让开发人员解决’undeclared’问题,有时候通过typeof的安全防范机制(阻止报错)来检查undeclared变量不失为一个方法。
当一个窗口加载多个.js文件时,这些.js文件中的代码会共享一个全局作用域,如果有重名的变量名,那么就太可怕了……减少全局变量是编写可维护的代码的基础,所有东西都应该被封装到模块或者私有/独立的命名空间里。
例如:调试模式下,在debug.js声明了一个DEBUG的全局变量,把它作为调试模式的开关,在输出调试信息时我们会先判断DEBUG是否已经被声明。可是如何在程序中检查全局变量DEBUG且不会出现ReferenceError错误呢?typeof的安全防范机制就成了我们的帮手1
2
3
4
5
6
7
8//这样做是要挨Error的
if (DEBUG) {
console.log("调试开始啰");
}
//这样就比较安全了
if (typeof DEBUG !== "undefined") {
console.log("调试开始啰")
}
不仅对于自定义的变量有用,对于内建的API也有帮助。甚至不一定要求作用域为全局,也可以用于检查我们的变量是否在宿主作用域声明过。
还有一个方法,就是检查所有全局变量是否是全局对象的属性,浏览器的全局对象是window,服务器端的全局对象为global。上述的例子我们可以这样操作:1
2
3if (window.DEBUG) {
//...
}
访问不存在的对象属性与undeclared变量不同,是不会报错的。
关于
参考文献:
《你不知道的JavaScript(中卷)》
《JavaScript语言精粹》