ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。 基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。
5 种基本数据类型: Undefined、 Null、 Boolean、 Number 和 String。这 5 种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象。与其他语言不同, JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。
动态的属性
对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。
对象不被销毁,设置的属性和方法,将一直存在。
不能给基本类型的值设置属性和方法。
复制变量值
如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。这两个变量可以参与任何操作而不会相互影响。(复制变量的值)
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量。(复制的是引用指针)
传递参数
ECMAScript 中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
访问变量有按值和按引用两种方式,而参数只能按值传递。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者用ECMAScript 的概念来说,就是 arguments 对象中的一个元素)。
1
2
3
4
5
6
7
8
|
function addTen(num){
num +=10;
return num;
} var count = 20;
var result = addTen(count);
alert(count); //20
alert(result); //30
|
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。传给函数的是数值的一个引用,函数中对其属性的修改外部可见,但用新引用覆盖其则在外部不可见。
1
2
3
4
5
6
|
function setName(obj){
obj.name = "staven" ;
} var person = new Object();
setName(person); alert(person.name); //staven |
普通赋值
1
2
3
4
|
var a = 1;
var b = a; //赋的是a的复制值
b ++; alert(a); //"1" b的修改不影响a
|
对象赋值
1
2
3
4
|
var a = [1];
var b = a; //赋的是a的引用
b[0] ++; alert(a); //"2" b的修改对a也有效
|
参数传值传递:传给函数的是数值的一个复制,函数中对其的修改外部不可见
1
2
3
4
5
6
7
8
9
10
11
12
|
var a = 1;
var b = 2;
function change(a,b) {
var c = a;
a = b; //用新引用覆盖
b = c;
alert(a); //"2"
alert(b); //"1"
} change(a,b); alert(a); //"1"
alert(b); //"2"
|
传址的传递:传给函数的是数值的一个引用,函数中对其属性的修改外部可见,但用新引用覆盖其则在外部不可见
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var a = [1, 2, 3];
var b = [5, 6];
function change(a,b) {
a[0] = 4; //对其属性的修改外部可见
var c = a;
a = b; //用新引用覆盖
b = c;
alert(a); //"5,6"
alert(b); //"4,2,3"
} change(a,b); alert(a); //"4,2,3"
alert(b); //"5,6"
|
a,b是change函数中的变量,在调用函数时传递了a,b的引用赋给了这两个变量,但是并不能改变全局中的a,b。因为用新引用覆盖在外部不可见,因为函数只是拿到了引用 并没有权力更改引用。
1
2
3
4
5
6
7
8
9
10
11
|
var a = [1, 2, 3];
var b = [5, 6];
function change() {
var c = a;
a[0] = 4;
a = b;
b = c;
}; change(); alert(a); //"5,6"
alert(b); //"4,2,3"
|
又得提到js的块级作用域了,这个放某些语言里定然是要报未定义错误的,因为js没有块级作用域,所以它在change里找不到变量a,b就会自觉的到上层去找,所以这里的a,b是全局变量的引用。
传值的比较比较的是数值 而传址的比较比较的是引用。
参数传递角度理解闭包
1
2
3
4
5
6
7
8
|
var add_handlers = function (nodes) {
var i;
for (i = 0, l = nodes.length; i < l; i ++) {
nodes[i].onclick = function (e) {
alert(i); // 当然这里的结果必然是每次alert的都是节点总数。。。。
}
}
}; |
此时i是父级函数作用域的变量的引用。给每个节点设置onclick事件的时候将i的引用传递给了alert,当我点击节点触发onclick事件的时候,i的值已经变成了节点总数。
1
2
3
4
5
6
7
8
9
10
11
|
var add_handlers = function (nodes) {
var i;
for (i = 0, l = nodes.length; i < l; i ++) {
nodes[i].onclick = function (i) {
return function (){
alert(i); } }(i);
}
}; |
这样修改后之所以正确是因为此时传进去的是i的值的复制。
检测类型
typeof 操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具。如果变量的值是一个对象或 null,则 typeof 操作符都会返回"object"。
如果变量是给定引用类型的实例,那么instanceof 操作符就会返回 true。
1
2
3
4
5
6
7
8
|
function setName(obj){
obj.name = "staven" ;
obj = new Object();
obj.name = "Bob" ;
} var person = new Object();
setName(person); alert(person.name); //staven
|