最近在看JQuery之父的《精通Javascript》,里面提到了Javascript是基于一个很庞大,很精妙的引用系统。
1. 对象可以包含一系列的属性,这些属性不过是到其他对象(比如字符串,数字,数组等)的引用。如果多个变量指向的是同一对象,那么该对象的内容 修改,这些变量也会跟着相应改变。比如其后举的例子中显示,多个变量指向一个Object对象,给这个对象的prototype属性添加共有方法,那么指向这个对象的多个变量也跟着改变了。同样的,如果多个变量指向的是一个数组,那么这个数组的内容的改变(比如添加,删除,修改元素)也会对所有指向它的变量有影响。
2. 必须记住:引用指向的只能是具体的对象,而不是另一个引用。不像perl语言中允许多个引用层。JS中变量的赋值的结果是沿着引用链一直上溯到那个对象。其后的代码显示:实际对象已经改变了,但原来指向它的引用仍然保持指向旧的对象。注意:在执行字符串连接操作时,结果总会是一个新的字符串对象,而非原字符串的修改版本。我自己写了一个简单代码,证明,对字符串的内容修改,也会产生一个新的字符串对象,而非原先的字符串的修改版本。代码如下:
var temp ="abc" var test = temp temp = "d" alert(temp) alert(test)
为了试验第一条笔记中的对象可以包含一系列的属性,这些属性不过是到其他对象(比如字符串,数字,数组等)的引用 这句话。我把字符串,修改为数字。结果显示 对数字的内容修改,也会产生一个新的数字对象,而非原先的数字的修改版本。代码如下:
var temp =1 var test = temp temp = 2 alert(temp)//弹出2 alert(test)//弹出1
这里发生了引用的重定向,也就证明这句话确实是正确的,在JS中任何变量都是对一个实际对象的引用。以下是我个人的观点,我们知道JS中任何对象都是继承自Object的!数字类型是这样,字符串是这样,数组是这样,Function是这样。这么想就容易理解了:JS中声明的任何对象都是Object的实例!自然,把一个Object实例赋值给一个变量的话,是让这个变量指向这个Object的实例。顺其自然,对这个实例的内容的修改会影响全部指向它的变量!但是如果发生了指针重定向了,那么新的指针理所当然的指向新对象( 数字类型是这样,字符串是这样,数组是这样,Function是这样 )了。而原先指向这个实例的指针还是指向这个实例。上面说的我个人的观点中,需要提醒一下的是对象的实例化方式。看如下代码:
var num = 123;//等同于这么写var num = new Number(123);或者var num = new Number("123")这样是严格的按照JS的语法构建对象。 //说明:没有按照类实例化的写法,只是JS提供的方便的写法,毕竟声明一个数字,字符串,数组,布尔值的操作太频繁了,每次都要严格的写这么一大串文字太累人了,而且要考虑到下载方面的影响。 var str = "test";//等同于var str = new String(test);或者var str = new String("test");
知道了JS原来这么定义数字,字符串,布尔值,数组,大家现在肯定能理解为什么说"Everything is Object"。现在回头重新品味下上面的2条笔记吧。
3. 讨论到利用JS来模拟 强类型语言的函数重载的功能。提到函数重载,我们需要使这个函数具备2个重要功能:判断参数个数的能力和判断参数类型的能力。这里需要使用到arguments对象(这是一个伪数组)和typeof运算符(用于提供一个字符串 名称,用于表达变量内容的类型)以及所有JS对象都带有的一个属性constructor(typeof运算符有弊端,对于Object,Array和自定义对象类型是无法区分的,全部返回字符串"object"。而constructor属性引用的是原本用来构造该对象的函数,返回的结果必然是个对象 。这里,大家就能想到了。调用对象的constructor属性能提供严格的对象类型检查!请记住一个写法:var test = obj.constructor();这里是运行obj这个对象的构造函数,实例化一个和obj相同的对象给test。)。看如下的对比表格:
4. 在JS中,作用域默认 是 由函数划分的,而不是由块(block)划分的。请区别于JAVA或者C#中的语句块(while,for,if等)。如下代码:
var foo = "test"; if(true){ var foo = "new"; } alert(foo);//弹出new function test(){ var foo = "old";//设置foo,不过这个设置的作用域默认是这个函数体内。如果我写成this.foo = "old",或者foo="old"那么就是指定了这个变量的作用域为window,则改变全局变量foo的值。 } test(); alert(foo);//弹出new
可以看到,变量都在全局作用域里。基于浏览器的JS的一个有趣的特性是,所有属于全局作用域的变量其实都是window对象的属性。
5. 闭包(closure)意味着内层的函数可以引用存在于包含它的函数内的变量,即使外层函数的执行已经终止。 配合上第四条笔记上提到的函数维护的作用域的强大功能,闭包的良好使用可以使我们的编程变得非常清晰。这里需要说明的是尽量要避免函数对全局变量的直接调用,否则会使我们写的代码乱成一锅粥!
6. 关于对象:对象一直是JS的基础。 事实上,这门语言里所以的东西都是对象(我之前已经详细解释过了)。这门语言的大部分功能都是基于这一点的。从基本的层次上说,对象是一系列属性的集合,和其他语言的散列表结构类似。这里需要提醒下,请参考我之前的一篇博文,里面提到对象其实只有一种运算符,就是点运算符(比如[].join() [].length等)。
提到对象,肯定要考虑到对象的类型。之所以,我们称JS是弱类型语言,因为它是一门动态类型语言,需要在运行时才能真正的判别对象的类型,那么类型检查就理所当然的重要了。
7.关于作用域(scope):在js中,作用域是由函数划分的,而不是由块(block)划分(比如while,if和for语句块中)的。其后,便举了一个例子,很好的解释了JS的这一特性:
//设置全局变量foo var foo = "test"; //在if块中 if(true){ //将foo置为"new test"。注意:现在还在全局作用域中! var foo = "new test"; } //如我们所见,现在foo等于"new test"了 alert(foo == "new test"); //创建一个会修改变量foo的新函数 function test(){ var foo = "old test"; } //现在调用时,foo只在函数作用域内起作用 test(); //这里确认了foo还是等于"new test" alert(foo == "new test");
值得注意的是,基于浏览器的JS有一个有趣的特性,所有属于全局作用域的变量其实都是window对象的属性。