本文深入解析JavaScript的六种数据类型,重点探讨加减运算中的隐式转换规则及==与===的区别。详细阐述包装对象机制及其性能影响,并对比typeof、instanceof、Object.prototype.toString等五种类型检测方法,帮助开发者准确掌握JS类型系统。
JavaScript六种数据类型概述
一、六种数据类型(弱类型数据)
1. 基本数据类型(共5种):Undefined、Null、Boolean、Number、String
2. 引用数据类型(共1种):Object,例如 Function、Date、Array 等
在JavaScript中定义变量时无需指定类型。例如定义变量 var num = 16,随后将字符串赋值给该变量 num = "this is a string" 也是合法的。这是否意味着JavaScript定义变量时完全无需关注类型?接下来我们将通过具体案例进行分析。
(1). 16 + 16 的运算结果很简单,为 32
(2). 若将第一个 16 用双引号包裹视为字符串,"16" + 16 的运算结果是否仍为 32?并非如此。此时执行的是字符串拼接操作,结果为包含字符 "1616" 的字符串。
(3). 继续观察,若同样是字符串与数字,将运算符由 + 变为 -,即 "16" - 16,其运算结果是多少?答案为 0。此处执行的是数字运算,因此结果为数值 0。
小结:虽然JavaScript定义变量时无需指定数据类型,但在涉及运算符等运算时,背后往往存在复杂的隐式转换逻辑。
隐式转换规则:运算符与相等性判断
二、隐式转换(+ 和 -,== 和 ===)
1. 基于上述案例举例说明:
"16" + 6 // "166" (字符串拼接)
"16" - 6 // 10 (数字运算)
我们可以利用(+/-)规则进行类型转换。
例如:若要将变量 num 转换为数字类型,一个简单的方法是执行 num - 0。若想将 num 转换为 String 类型,只需执行 num + "" 即可。
2. == 和 ===
(一)、相等运算符(==)
1. 若一个值是 null,另一个是 undefined,则它们相等。
2. 若一个值是数字,另一个是字符串,则先将字符串转换为数字,再使用转换后的值进行比较。
3. 若其中一个值是 true,则将其转换为 1 再进行比较。若其中一个值是 false,则将其转换为 0 再进行比较。
4. 若一个值是对象,另一个值是数字或字符串,则将对象转换为原始值后再进行比较。对象通过 toString() 方法或 valueOf() 方法转换为原始值。JavaScript核心的内置类首先尝试使用 valueOf(),再尝试使用 toString(),除了日期类,日期类只使用 toString() 转换。那些不是JavaScript语言核心中的对象则通过各自的实现中定义的方法转换为原始值。
5. 其他不同类型之间的比较均不相等。
例如:
null == undefined, //true
“16” == 16, //true
0 == false, //true
[1,2] == [1,2], //false
new Object() == new Object(), //false
new Object().toString() == new Object().toString(), //true
new String("aaa") == "aaa", //true
new String("11") == 11, //true
(二)、严格等于(===)
1. 首先判断它们的类型,如果两个值类型不相同,则它们不相等,直接返回 false。
// “16” === 16, //false
2. 如果两个值都是 null 或者都是 undefined,则它们才相等。
// null === null, //true, undefined=== undefined, //true undefined === null //false
3. 如果两个值都是布尔值 true 或 false,则它们相等。
4. 如果其中一个值是 NaN,或者两个值都是 NaN,则它们不相等。NaN 和其他任何值都是不相等的,包括它本身!!!通过 x!==x 来判断 x 是否为 NaN,只有在 x 为 NaN 的时候,这个表达式的值才为 true。
5. 如果两个值为数字,且数值相等,则它们相等。如果一个为 0,另一个为 -0,则它们同样相等。
6. 如果两个值为字符串,且所含的对应位上的 16 位数完全相等,则它们相等。如果它们的长度或内容不同,则它们不等。两个字符串可能含义完全一样且所显示出的字符也一样,但具有不同编码的 16 位值。JavaScript 并不对 Unicode 进行标准化的转换,因此像这样的字符串通过 "===" 和 "==" 运算符的比较结果也不相等。
7. 如果两个引用值指向同一个对象、数组或函数,则它们是相等的。如果指向不同的对象,则它们是不等的。尽管两个对象具有完全一样的属性。
包装对象机制与性能影响
三、包装对象
定义:在JavaScript中,“一切皆对象”,数组和函数本质上都是对象,就连三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”。
通俗来讲:字符串,数值,布尔值,这些基本类型看其它人都有对象,非常不爽,所以自己也搞了个对象,名字叫包装。
1. 一般来说,只有对象是可以对属性进行读写操作的。但是你是否发现,平时用得很多的字符串方法和属性,都是直接通过“.”操作符访问的。例如:
console.log("hello world".length);
console.log("this a string".indexOf("a"));
2. 其实,在我们调用这些方法和属性时,JS内部已经隐式地帮我们创建了一个包装对象了,以上的实际情形应该是这样的:
console.log(new String("hello world").length);
console.log(new String("this a string").indexOf("a"));
3. 但是两者还是有区别的,
区别1:
浏览器自己隐式创建的包装对象和你显式创建的包装对象不严格相等。简单来说,虽然表面JS对亲生的与领养的一样,但实际上,亲生的不等于领养的。
var a1 = "test",
a2=new String("test");
console.log(a1 == a2);//true
console.log(a1 === a2);//false
区别2:
隐式创建的包装对象,在使用完后之后就会被抛弃了。简单来说就是,一个大大的负心汉,上完我,就抛弃,还重新去找其他对象。
拿我们开头的那道题来说:
test.a = "hello";
这里隐式创建了一个包装对象,所以这里赋值不会报错。
console.log(test.a);
这里之前的包装对象已经被抛弃了,但是使用了"."运算符,所以又创建一个新的包装对象,但是这个对象的属性 a 并没有赋值,所以属性 a 的值是 undefined。
说到实际运用中,有的浏览器性能不是很好,比如说低版本IE,当频繁处理字符串时,效率会很低。所以很多时候,我们还不如直接显式地创建包装对象,防止浏览器过多地创建隐式的包装对象,提升性能。
//不推荐使用种方法
var example = "this is a example";
//推荐使用这种方法,提升性能。
var example2 = new String("this is a example");
JavaScript类型检测方法详解
四、类型检测
在js中检测类型的方法有很多,比如 typeof、instanceof、Object.prototype.toString、constructor、duck type
1. typeof 运算符,有两种方式 typeof + 检测目标 或者 typeof (检测目标),该方法适用于基本数据类型和函数的检测(遇到null失效),返回的结果都是字符串
typeof 100 “number”
typeof true “boolean”
typeof("hello") "String"
typeof Function “Function”
typeof undefined “undefined”
typeof null “Object” 历史遗留的问题,为了兼容
typeof NaN “number” 判断NaN的方法可以用isNaN(NaN)来判断,如果返回true就是NaN
typeof new Object() “Object”
typeof [1,2] "Object"
大家可能注意到了,typeof在检测对象类型的时候是无法区分的,此时我们就要用第二种方法instanceof
2.instanceof运算符(适用于自定义对象或者原生对象数据类型的检测),基于原型链的类型检测
比如判断一个对象是不是数组: [1,2] instanceof Array 返回 true
obj instanceof Object :它期望左操作数obj 是一个对象,如果是一个数字1,2,3或者true,false时就会直接返回false。
它期望右操作数是一个函数对象或者是函数构造器,如果不是就会抛出Typeerror异常
instanceof 工作的一个大概的原理就是它会判断左操作数这样一个对象的原型链上是否有右边这个构造函数的prototype属性
我们举一个例子:
function Person(){}
function Student(){}
Student.prototype = new Person();
Student.protype.constructor = Student
var a = new Person();
var b = new Student();
-----------------------------------------
a instanceof Person // true
b instanceof Student //true
a instanceof Student //false
b instanceof Person //true
注意:不同window之间和跨iframe之间是不可以使用instanceof的,虽然看着是同一个对象但是在不同window之间和跨iframe之间是不同的对象,每个iframe下都有自己的一套原型链,跨frame实例化的对象彼此是不共享原型链,因此会返回false
3.Object.prototype.toString 函数方法
Object.prototype.toString.call()或者Object.prototype.toString.apply()
例如:
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(function(){}) //"[object Function]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(null) //"[object Null]"
注意:1.解决了跨iframe 失效的问题
2.IE 6,7,8下 Object.prototype.toString.call(null或undefined) //"[object Object]"
4.constructor方法
function A(){};
function B(){};
A.prototype = new B();
var aObj = new A();
--------------------------------
aObj.constructor === B; //true;
aObj.constructor === A; //false;
注意:类继承时会出现问题
5.duck type(鸭子类型)
比如检测数组时,我们可以检测他有没有push()方法等等
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!