ECMAScript变量分析

  ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。

  • 基本类型值指的是简单的数据段,而它包括五种基本数据类型分别是:Undefined、Null、Boolean、Number和String,这五种基本数据类型时按值访问的,因为操作保存在变量中实际的值。
  • 引用类型的值是保存在内存中对象,不能像其他语言一样,可以直接访问内存中的位置,也就是说不能直接操作对象的内存空间,而在操作时,实际上是在操作对象的引用而不是实际的对象。所以,引用类型的值是按引用访问的。

一、变量的动态属性

  基本类型值和引用类型值定义的方式是类似的:创建一个变量并为该变量赋值。

var person = "String";//定义一个基本类型值的变量
var person = new Object();//定义一个引用类型值的变量

  但是,要为其不同类型执行的操作是否也类似呢?

  我们为基本类型添加属性或方法时候:

var name = "xiaoming";
name.height = 180;
console.log(name.height);//undefined

  我们为基本类型变量name定义一个height属性,并赋值,而接下来在次调用访问这个属性的时候,发现属性不见了。

  我们来试试引用类型值的添加属性或方法:

var person = new Object();
person.name = "xiaohong";
console.log(person.name);//"xiaoming"

  我们构造了一个person的实例,然后为该实例添加了一个name的属性,并为其赋值上"xiaoming"字符串,最后通过console.log函数输出这个属性。所以,很明显在使用引用类型的值添加属性和方法的时候,可以成功访问到属性的值。

二、复制变量值

基本类型变量的复制过程:

var a1 = 2;
var a2 = a1;

在变量对象上创建一个新值,然后把该值复制到新变量分配的位置上,图解如下所示:

引用类型值的复制

  同样也会将存储在变量对象中的值复制一份放到为新变量分配空间中,但不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制结束后,前后两个变量实际上将引用同一个对象。所以说,改变其中一个变量,就会影响另一个变量。

var obj1 = new Object();
var obj2 = obj1;
obj1.name = "xiaoming";
console.log(obj1.name); //"xiaoming"
console.log(obj2.name); //"xiaoming"

  首先,变量obj1保存了一个对象的新实例。然后,这个值被复制到了obj2中,也就是说,这两个obj1和obj2都指向同一个对象。这样obj1添加了name属性后,就可以通过obj2来访问这个属性,因为这两个变量都引用了同一个对象。

三、传递参数

  ECMAScript中所以函数的参数都是按值传递的。其实,就是遵循上面复制的规则,从一个变量复制到另一个变量中,而基本类型值和引用类型值各自由各自的复制特点。

  在向参数传递基本类型的值时,被传递的参数值会被复制给一个局部变量(即命名参数)。

function add(num) {
    num += 10;
    return num;
}

var count = 20;
var result = add(count);
console.log(count);    //20
console.log(result);//30

 全局变量count复制它的值20,给了参数num(局部变量)。而在函数内部,num被加上10,但这并不会影响外部的count变量,这个变量互不相干,它们有相同的值而已。

那么,如果使用对象也就是引用类型值传递呢,好像不好理解!!!

function myName(obj) {
    obj.name = "xiaoming";
}

var person = new Object();
myName(person);
console.log(person.name);//"xiaoming"

  上面我们创建了一个对象,并将其保存在了变量person中。然后,这个变量被传递到myName()函数中就复制给了obj。那么,可以说obj和person引用都是同一个对象。也就是说,即使这个变量是按值传递的,obj也会按引用来访问同一个对象。在这里可以执行添加属性,在外部也可有所反映。那是因为person指向的对象在堆内存中只有一个,而且全局对象。

然而在函数内部修改对象,在全局作用域会反映出来吗?

function myName(obj) {
    obj.name = "xiaoming";
    obj = new Object();
    obj.name = "huage";
}

var person = new Object();
myName(person);
console.log(person.name);//"xiaoming"

  我们在函数内部重新定义了一个obj对象和修改属性,但输出的还是最先那个值,这说明了即使函数内部修改了参数值,但原始的引用仍然保持不变,在重写的对象中,其实引用的是一个局部变量,而这个局部对象会在函数执行完毕后立即被销毁。

四、类型检测

  在检测某个值的对象的时候我们会用typeof,但如果你要检测它是什么类型的对象呢?

  在这里ECMAScript提供了instanceof操作符:

result = variable instanceof constructor
//      (某个对象)           (Object、Array....)

如果该变量是给定的引用类型的实例,使用instanceof操作符就可以返回true。

var person = new Object();
var grounp = {
    a:1,
    b:2,
    c:3
};
console.log(person instanceof Object);//"true"
console.log(grounp instanceof Object);//"true"

还可以检测Array、RegExp等。

小总结:

  1. 基本类型值在内存中占据固定大小的空间,因此被保持在栈内存中;
  2. 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本;
  3. 引用类型的值是对象,保持在堆内存中;
  4. 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;
  5. 在一个变量向另一个引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象;
  6. 基本数据类型可以用typeof操作符检测,而引用类型可以用instanceof操作符。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果您喜欢或者有所启发,欢迎添加收藏,一起加油学习啊。

猜你喜欢

转载自www.cnblogs.com/goodfeeling/p/9075937.html