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方法。  

 数据检测,原文地址

posted @ 2017-06-22 16:33  呆萌的蚊子  阅读(750)  评论(0编辑  收藏  举报