原始值和引用值
ECMAScript变量可以包含两种不同类型的数据:原始值和引用值,原始值就是最简单的数据:Undefined,Null,Boolean,Number,String,Symbol,保存原始值的变量是按值访问的,因为我们操作的就是存储在变量中的实际值。
引用值是保存在内存中的对象,JavaScript不允许直接访问内存位置,操作对象时,实际上操作的对该对象的引用,而非实际的对象本身,因此,保存引用值的变量是按引用访问的。
区别
动态属性
引用值可以随时添加,修改删除属性。
而原始值不能有属性,原始值的初始化只使用原始字面量形式,如果使用new关键字则JavaScript会创建一个Object类型的实例,
1 | let name1="Nicholas"; |
复制值
通过变量把一个原始值赋值到另一个变量时,原始值会被复制得到新变量的位置。
1 | let num1=5; |
num1和num2相互独立,num2是num1的副本
当把引用值从一个变量赋值给另一个变量时,存储在变量中的值也会被复制到新变量所在位置,区别在于:这里复制的值时一个指针,它指向存储在堆内存中的对象。两个变量实际上指向同一个对象。
1 | let obj1=new Object(); |
传递参数
ECMAScript中所有函数的参数都是按值传递,这意味着函数外的值会被复制到函数内部参数中,就像一个变量赋值到另一个变量一样,如果是原始值,那么就跟原始值变量的复制一样,如果是引用值就和引用值的变量复制一样。
1 | function addTen(num){//count的值被复制到参数num,函数内部num+10但不会影响函数外部的原始变量count |
如果是对象:
1 | function setName(obj){ |
typeof
typeof用来判断一个变量是否为原始类型,即判断一个变量是否为字符串,数字,布尔值或undefined的最好方式,值为对象null,那么typeof返回Object
typeof虽然对原始值很有用,但是对引用值用处不大,我们通常关心一个值是不是对象,而是想知道它是什么类型的对象。
instanceof
如果变量是给定引用类型的实例,则instanceof操作符返回true
1 | console.log(person instanceof Object);//变量person是Object吗 |
按照定义所有引用值都是Object的实例,因此通过instanceof检测的任何引用值和Object构造函数都会返回true。如果,使用instanceof检测原始值,则始终会返回false,因为原始值不是对象。
对象标识和相等判定
ES6之前:
1 | console.log(+0===-0);//true |
ES6中的object.is()与===很像,但同时考虑了上述边界情形,这个方法接收两个参数:
1 | console.log(Object.is(true,1));//false |
检查超过两个值是否相等
1 | function recursiveEqual(x,...rest){ |
constructor:
constructor两个作用,
- 判断数据的类型
- 对象实例通过prototype对象上的constructor访问它的构造函数,因此如果改变prototype对象,constructor就不能用来判断数据类型
1 | console.log((2).constructor === Number); // true |
1 | function Fn(){}; |
Object.prototype.toString.call()
Object.prototype.toString.call()使用Object对象的原型方法toString来判断数据类型
1 | var a = Object.prototype.toString; |
注意:同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是因为toString是Object的原型方法,而Array,function等类型作为Object的实例,都重写了toString()方法,不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString()方法,(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。