前言
JavaScript 是一门简单易用的语言,应用广泛,同时它的语言机制又十分复杂和微妙,即使经验丰富的开发人员也需要用心学习才能真正掌握。
类型
一般描述某某类型,是对变量的值的类型描述,而不是变量,变量是没有类型的
javascript有七种内置类型:1
2
3
4
5
6
7空值(null)
未定义(undefined)
布尔值(boolean)
数字(number)
字符串(string)
对象(object)
符号(symbol, ES6 中新增)
使用typeof 就可以查看到,但有意思的null是object,无法一一对应。1
2
3
4
5
6
7typeof undefined === 'undefined'
typeof true === 'boolean'
typeof 1 === 'number'
typeof "41" === 'string'
typeof {} === 'object'
typeof Symbol() === 'symbol'
typeof null === 'object'
除了object,其他都是基础类型,而使用instanceof对基础类型校验类型,都是object,除非使用String等包装对象,就可以正确的校验出对应的类型,而对象类型都可以instanceof校验,因为它校验的是construct指向类型。
typeof、instanceof、Object.prototype.toString
- typeof
一般使用来检查基本类型,当然也有一些意外,如null为object,这是规范的bug,但牵连过多,所以无法修复,还有function,由于内部的[[call]]原因,当typeof检查时会认为它是可调用对象,所以会返回function字符串,其他的对象统一都返回object,而宿主对象也不一样,但这个很少用,不过有一个很特别就是document.all返回undefined。
对基础类型进行手动的包装对象,typeof只能认为它是对象类型,而且js引擎对这部分优化了,会自动包装,所以最好不要手动包装。1
2
3
4typeof document.all === 'undefined'
typeof 宿主对象(由JS环境提供) === Implementation-dependent
typeof null === 'object'
typeof new String("xxx") === 'object'
- instanceof
instanceof是检测对象的原型链上构造器constructor对象是否等于要检查对象。
而原型的constructor是很容易被修改的,比如prototype直接赋予对象,那么constructor就等于对象的constructor1
2
3function Car(){}
Car.prototype = {}
// Car的constructor被修改为Object
对象继承了父级对象,那么使用instanceof检查时要检查对象为父级constructor也是为true,因为它会查找原型链上的所有的constructor。1
2
3
4
5
6
7
8
9function Parent(){}
Parent.prototype.get = function(){
return "父级";
}
function Car(){}
Car.prototype = new Parent();
Car.prototype.constructor = Car;
let car = new Car();
console.log(car instanceof Parent); // true
- Object.prototype.toString
js的一句很吊的话,“一切皆为对象”,但有时从各种方面又可以体验出这句话的意思,如所有的类型都有一个顶级对象Object,而他们都继承了它的,这方面和java很像,因为java在强类型语言,所以一切变量都要定义好类型,但他们都可以向上转型为Object,他们的顶级类型也为Object。
而在一个对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用,就会自动调用toString方法,返回”[object type]”,type是对象的类型,所以可以利用这点对类型进行检查,但要使用到function的call重新定义上下文的this,那么就可以访问到对象里[[class]]属性,从而知道类型,而且这个检查类型的方式是比较全面的,但对自定义的实例化对象是无法检查到的,都统一返回[object Object],所以对这方面使用instanceof检查比较好。
1 | let a = "xx"; |
所以综上所述,Object.prototype.toString对检查类型方面是比较完善正确的,缺点就是在自定义原型的实例化对象上的检查。
语句
语句相当于句子,表达式相当于短语,运算符相当于标点符号和连接符。
语句都是有结果返回的。
如在控制台输入一个语句,可以查看到一个返回结果。
赋值表达式的副作用,就是变量被赋予某个值。1
2
3
4
5
6var b;
var a = 10;
if(a && (b = typeof a)){
}
// b就被赋值,而且也进行了校验
代码块:1
2[] + {}; // [object object]
{} + []; // 0
第一行[]强制转换为””, “”+{},被认为{}空对象
第二行{}被当作空的代码块,而代码块不需要分号,+[]被强制转换为数字0,如+”” === 0
主要看运算符在谁前面
1 | var a = false,b = true, c = false, d = true, e = false, w = false, q = false, x = true, z = true; |
&& 优先于 || 优先于 ?:
优先级,=优先于”,”,用()包着可以先优先执行1
2var a = 10;
var c = (++a,a)
无论情况finally都会执行,除了finally报错,那么try的值就不会给返回。1
2
3
4
5try{
return 10;
}finally{
console.log("finally");
}
switch case比较的是和===类似,所以计较严格,类型不一样也无法匹配。1
2
3
4
5
6
7
8
9switch(a){
case 10:....
}
switch(true){
case a == '10':....
}
// 使用&& || 要小心,因为它返回的是值,不是布尔值