一、说明
Javascript是动态语言,由浏览器执行,是解析性语言。
二、介绍 (大部分从JavaScript高级程序设计(第2版)摘录)
五种基本数据类型
基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。基本类型有五种,Undefined、Null、Boolean、Number和String。还有一种复杂数据类型——Object(本质上是由一组无序的名值对组成)
可以使用使用typeof操作符来获得数据类型:
l "undefined"——如果这个值未定义
l "boolean"——如果这个值是布尔值
l "string"——如果这个值是字符串
l "number"——如果这个值是数值
l "object"——如果这个值是对象或null
l "function"——如果这个值是函数
代码:
1 var a; 2 var b = 1; 3 var c = 'c'; 4 var d = {}; 5 var e = null; 6 var f = true; 7 var g=function(){ 8 9 }; 10 function show() { 11 var aType = 'a:' + typeof a; 12 var bType = 'b:' + typeof b; 13 var cType = 'c:' + typeof c; 14 var dType = 'd:' + typeof d; 15 var eType = 'e:' + typeof e; 16 var fType = 'f:' + typeof f; 17 var gType = 'g:' + typeof g; 18 19 alert(aType + "\n" + bType + "\n" + cType + "\n" + dType + "\n" + eType + "\n" + fType+"\n"+gType); 20 }
函数
函数使用function关键字来声明,基本语法:
function functionName(arg0,arg1){
statements
}
注意下面几点:
l 如果遇到return ; 函数会停止执行并返回undefined值
l 所有参数传递都是值,不可能通过引用传递参数
l 参数在内部是用一个数组来表示的。函数接受到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。函数体内可以通过arguments对象来访问这个参数。arguments对象只是与数组类似(并不是Array实例)。
l 没有传递值的命名参数将自动被赋予undefined值
l 如果定义了两个名字相同的函数,则后面的会覆盖前面的。
l 除了什么时候可以通过变量访问函数这一点区别外,函数声明与函数表达式的语法其实是等价的。
l 要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号。
l 函数有两个特殊的对象:arguments和this。arguments还有一个名叫callee的属性(指针),指向拥有这个arguments对象的函数。this引用的是函数据以执行操作的对象。
l 每个函数都包含两个属性:length和prototype。length表示函数希望接受的命名参数的个数。
l 每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
l 每个函数都有一个非标准的caller属性,该属性指向调用当前函数的函数。(只建议将该属性用于调试目的)。
l 函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
l arguments.callee是一个指向正在执行的函数的指针
l JavaScript中没有私有成员的概念,所有对象属性都是公有的。不过,在任何函数中定义的变量,都可以认为是私有变量,因此不能在函数的外部访问这些变量。但有两种方式创建特权方法
l 函数f1内部定义了另外一个函数f2,当f1执行完毕后,当f2仍存在,则其活动对象也不会被销毁,因为f2的作用域仍然在引用这个活动对象,f1的执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中,直到f2被销毁后,f1的活动对象才会被销毁。
构造函数
注意下面几点:
l 构造函数在不返回值的情况下,默认会返回新对象实例。
l 寄生构造函数模式:返回的对象与构造函数或者与构造函数的原型属性之前没有关系:也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。
l 组合继承=原型链+构造函数(会用到call或apply),是最常用的继承模式
l 寄生组合式继承是引用类型最理想的继承范式,YUI的YAHOO.lang.extend()方法采用了寄生组合继承,从而让这种模式首次出现在了一个应用非常广泛的javascript库中
引用类型
引用类型值是指那些保存在堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象。
基本类型和引用类型在内存中保存情况如下图:
注意下面几点:
l 访问对象属性时一般都使用点表示法,不过也可以使用方括号表示法来访问对象属性,但属性必须以字符串的形式放在方括号中。
l Array数组,每一项可以保存任何类型的数据,它的大小是可以动态调整的。
l 使用索引设置数组的值时,如果超过了数组现有项数,数组会自动增加到该索引值加1的长度
l 数组的length属性不是只读的。
l 数组有栈方法和队列方法(IE对JavaScript的实现中存在一个偏差,其unshift方法总是返回undefined而不是数组的新长度)
l 引用类型与基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在与一行代码的执行瞬间,然后立即被销毁。
l 对基本包装类型的实例调用typeof会返回"object",而所有基本包装类型的对象都会被转换为布尔值true。
执行环境及作用域
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。
某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(如果还能在其他环境中被访问到,则不销毁)。
环境的机制
存在环境栈,可把环境推入或弹出。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链。它的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。全局环境的变量对象始终都是作用域链中的最后一个对象。
标识符解析是沿着作用域链一级一级地搜索标识符的过程。
延长作用域链
try-catch语句的catch块和with语句可以延长作用域链,即当执行流进入其中一个语句时,在作用域链的前端临时增加一个变量对象,作用域链得到加长,该变量对象会在代码执行后被已移除。(作用域增加的临时变量对象都只是只读的,所以在它们中定义的变量,成了函数执行环境的一部分)
注意下面几点:
l JavaScript中没有块级作用域
l 变量在未经声明的情况下被初始化,那么该变量会被自动添加到全局环境中。
l 局部变量会在它们离开执行环境时自动被解除引用(解除的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收)
l 垃圾收集策略:标记清除、引用计数(不常使用,除了IE,如果有循环引用会出现问题,需要手动断开,即赋值为null,不然无法垃圾收集),不建议手动执行垃圾收集
页面注意下面几点:
l 在使用<script>嵌入JavaScript代码时,不能在代码的任何地方出现“</script>”字符串,会产生错误。因为按照解析嵌入式代码的规则,当浏览器遇到字符串“</script>"时,就会认为那是结束的标签。而通过把这个字符串分隔为两部分可以解决这个问题。例如alert("<scr"+"ipt"/>);
l 解析器对<script>元素内部的所有代码求值完毕以前,页面中的 其余内容都不会被浏览器加载或显示。
l 浏览器会按照<script>元素在页面最后能够出现的先后顺序对它们依次进行解析。
l javascript代码放在<head>和<body>的区别
l 变量定义没有使用var,会看作全局变量
l 大量使用with语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用with语句。
l switch可以使用任何数据类型,每个case的值不一定是常量,可以是变量,甚至是表达式。switch语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串“10”不等于数值10)
三、理解
关于引用类型
在java和C++中都采用了引用这个概念,但是二者的行为并不一样。java的引用其实更象c++中的指针,而非c++中的引用
引用其实是特殊的"指针",它只是没有指针运算(a++,a- -这些),这样会更安全。所以声明定义一个引用时,它都会在栈中建立一个空间。
var a={};
var b=a;
a和b都代表同一个对象,而不是b拥有a的一个副本。
a存放了一个对象的内存地址,然后把地址赋值给了b,所以他们都指向同一个对象。注意的是,a和b都拥有自己的栈空间。
关于string特殊的基本类型(希望有人可解答我疑问)
Javascript中的基本类型都存放在栈中,并且所占空间大小固定,可是string大小并不固定,它为什么也是基本类型?。下面是我对此的理解:
l 字符串值存放在栈中:
上面所说空间固定,指的是当已经存放了值后,值的大小已经确定了,无法改变。而并不是指string基本类型在存放值之前空间大小已经固定,所以可能出现有a,b两个string类型的值,它们在栈中的空间大小却不一样
赋值操作时:
var a="a";
var b=a;
此时栈中有a和b两块空间,存放的值都是"a",注意它们在不同的栈空间中。
l 字符串值存放在堆中:
上面所说空间固定,是指基本类型在存放值之前已经确定空间大小了。则string类型必定是一个引用,它存放指向堆空间的一个地址。可为什么不把它归于基本类型呢?理由如下:
由于效率的原因,我们希望JS只复制对字符串的引用,而不是字符串的内容。但是另一方面,字符串在许多方面都和基本类型的表现相似,而字符串是不可变的这一事实(即没法改变一个字符串值的内容),因此可以将字符串看成行为与基本类型相似的不可变引用类型。
关于关键字this
在 Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象。一般在编译期确定下来,或称为编译期绑定。而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的。
我的理解是——this表示的是当前环境的变量对象。
匿名函数是全局性的理由是,它不存在除全局环境之外的任何环境中,也就是说没有引用可以访问得到它。这时的this是全局环境的window对象。
是否为匿名函数,主要看是否有引用指向它。
代码:
/* * 是否为匿名函数,主要看是否有引用指向它 */ var ob={}; ob.a=function () { (function() {//匿名函数 alert("a this:" + this); })(); } ob.b=function(){ alert("b this:" + this); } ob.c=function(){ return function(name){ alert(name+" this:" + this); }; } ob.d=ob.c();//ob.c()返回一个匿名函数,但是ob.d指向了它,则它不再是匿名函数,this执行ob function show() { ob.a(); ob.b(); ob.c()('c');//ob.c()得到一个匿名函数,可是却没有指针指向它,所以执行后this是window ob.d('d'); }
参考:JavaScript高级程序设计(第2版)