javascript变量的引用类型值
JavaScript变量可以用来保存俩种类型的值:基本类型和引用类型值
前言
JS变量可以用来保存两种类型的值:基本类型值和引用类型值。基本类型的值源自一下5种基本数据类型:Underfined、Null、Boolean、Number和String。
基本类型值和引用类型值具有以下特点:
-
基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
-
从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
-
引用类型的值是对象,保存在堆内存中;
-
包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
-
从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;
-
确定一个值是哪种基本类型可以使用Typeof操作符,而确定一个值是哪种引用类型可以使用instanceof操作符。
所有变量(包括基本类型和引用类型)都存在于一个执行环境(也称为作用域)当中,这个执行环境决定了变量的生命周期,以及那一部分代码可以访问其中的变量。
一下是关于执行环境的几点总结:
-
执行环境有全局执行环境(也称为全局环境)和函数执行环境之分;
-
每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;
-
函数的局部黄金不仅有权访问函数作用域中的变量,而且有权访问其包含(父)环境,乃至全局环境;
-
全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;
-
变量的执行环境有助于确定应该合适释放内存。
上述是来自JavaScript高级程序设计,先扫盲基本类型和引用类型值,接下来开始说说引用类型值
对于理解和使用引用类型值,我认为下面倆句话很关键
-
包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
-
从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象
先看一段简单的js
var o = { color:'red' }; var a = o; a.color = 'black'; console.log(o.color) // black
为什么上边的 o.color会变成black能,砸门都没有对它进行操作!!它之所以改变时因为我们有一步将它赋值给了a,这个a就变成了引用类型的值再连起上面俩句话,就能理解引用类型的值和o.color变成black的原因了!
备注:之所以写这个,是因为最近再写vue的学习项目时,用到了组件通信,父组件给子组件通信还好,但子组件给父组件通信就比较麻烦了,所以我就想到引用类型的值的特点(牵一发而动全身)+vue深处数据监听来实现子组件给父组件通信,一试还不错!但水平有限不能彻底搞懂这种方法可以吗?所以记下来,以便以后看到解决!
既然开始写了就吧引用类型的值的检测也总结记一下吧!
1、常用的数据检测 typeof
typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型。
typeof ''; // string 有效 typeof 1; // number 有效 typeof true; //boolean 有效 typeof undefined; //undefined 有效 typeof null; //object 无效 typeof [] ; //object 无效 typeof new Function(); // function 有效 typeof new Date(); //object 无效 typeof new RegExp(); //object 无效
typeof 可以对JS基础数据类型做出准确的判断,而对于引用类型返回的基本上都是object,这是因为因为所有对象的原型链最终都指向了Object,所以直接pass;
2、instanceof (不完美)
instanceof 是用来判断 A 是否为 B 的实例对,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。
注意的是:instanceof检测的是原型
instanceof (A,B) = { var L = A.__proto__; var R = B.prototype; if(L === R) { //A的内部属性__proto__指向B的原型对象 return true; } return false; }
该代码模拟instanceof内部执行过程从上述过程可以看出,当 A 的 __proto__ 指向 B 的 prototype 时,就认为A就是B的实例!
但是还有问题就是,虽然 instanceof 能够判断出 [] 是Array的实例,但它认为 [] 也是Object的实例,如下
[] instanceof Array; //true {} instanceof Object;//true new Date() instanceof Date;//true function Person(){}; new Person() instanceof Person; [] instanceof Object; //true new Date() instanceof Object;//true new Person instanceof Object;//true
这是因为:[]、Array、Object就形成了如下图所示的一条原型链:
从原型链可以看出,[] 的 __proto__ 直接指向Array.prototype, 间接指向Object.prototype, 所以按照 instanceof 的判断规则,[] 就是Object的实例。当然,类似的new Date()、new Person() 也会形成这样一条原型链,因此,instanceof 只能用来判断两个对象是否属于原型链的关系, 而不能获取对象的具体类型。
3、constructor(属性返回对创建此对象的数组函数的引用。)也有问题
当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。如下所示:
function F () { //定义函数 } console.log('---F.prototype') console.log(F.prototype) var f=new F(); console.log('---f.constructor === F') console.log(f.constructor === F)
上面代码会在控制台输出以下内容
当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。
当执行 var f = new F() 时,F被当成了构造函数,f是F的实例对象,此时F原型上的constructor传递到了f上,因此f.constructor === F;
可以看出,JS在函数F的原型上定义了constructor,当F被当作构造函数用来创建对象时,创建的新对象就被标记为了“F” 类型,使得新对象有名有姓,可以追溯。
同理,JS中的数据类型也遵守这个规则:
细节问题:
- null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过typeof来判断。
- JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object
为什么变成了Object?
prototype被重新赋值的是一个{}, {}是new Object()的字面量,因此new Object()会将Object原型上的constructor传递给{},也就是Object本身。
因此,为了规范,在重写对象原型时一般都需要重新给constructor赋值,以保证实例对象的类型不被改写。
4、Object.prototype.toString (完美)√
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。
function typeofToString(obj) {
var str = Object.prototype.toString.call(obj);
var substr = str.match(/\s(\S*)]/);
substr = str.match(/\s(\S*)]/);
return substr[1];
}
console.log(typeofToString('')); //string
console.log(typeofToString(1)); //Number
console.log(typeofToString(true)); //Boolean
console.log(typeofToString(undefined)); //Undefined
console.log(typeofToString(null)); //Null
var obj=new Function();
console.log(typeofToString(obj)); //Function
var obj=new Date();
console.log(typeofToString(obj)); //Date
console.log(typeofToString([])); //Array
var obj=new RegExp();
console.log(typeofToString(obj)); //RegExp
var obj=new Error();
console.log(typeofToString(obj)); //Error
console.log(typeofToString(document)); //HTMLDocument
console.log(typeofToString(window)); //Window
需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。
数据检测,原文地址