第2章 词法结构
一、字符集
*JavaScript程序是用Unicode字符集编写的。
*JavaScript严格区分大小写。
*JavaScript注释:(1)“//”用于单行注释;(2)"/* .... */"用于多行注释。
*JavaScript使用分号将语句之间分隔;如果各条语句单独成一行,通常可以省略分号。但下面几种情况需要注意:
a = 3; //这里的分号是可以省略的 b = 4; a = 3; b = 4; //这里第一个分号不能省略
如果编写时候未用分号,则JavaScript会自动填补分号,但只在缺少了分号无法正确解析代码的时候。
var y = x + f (a+b).toString() //对于以上代码解析自动添加分号后如下 var y = x + f(a+b).toString(); //已经不是原意了
x ++ y //解析自动添加后 x; ++y;
所以,添加分号是很好的编写习惯。
第3章 类型、值和变量
*JavaScript的数据类型分为两类:原始类型(primitive type)和对象类型;其中原始类型包括数字、字符串、布尔值、null(空)、undefined(未定义);JavaScript的对象是“命名值”的无序集合,是隶属于该对象(Obeject)的属性(property)的集合,每个属性都由“名/值对”(这里的值可以是原始类型数据,也又可以是对象类型数据);JavaScript还定义了数组(array)和函数等特殊对象。
*JavaScript解释器有自己的内存管理机制,可以自动对内存进行垃圾回收(garbage collection),当程序中不再有任何引用指向一个对象时,解释器就会自动回收该对象所占据的内存空间。
*从另外一个角度,JavaScript数据类型又可以分为可变类型和不可变类型。可变类型的值是可以修改的,如数组、对象。而数字、布尔值、null和undefined属于不可变类型。
一、数字
*JavaScript不区分整数和浮点数,所有的数字都是用浮点数来表示。当一个数字直接出现在程序中,则称为数字直接量(numeric literal)。
*JavaScript可以自由地进行数据类型转换
*JavaScript中定义了Math对象,其属性和方法定义了很多算术运算:
//求2的53次幂Math.pow(2,53)//四舍五入,输出1.0 Math.round(.6) //向上求整,输出1.0 Math.ceil(.6) //向下求整,输出0.0 Math.floor(.6) //返回最大值 Math.max(x,y,z) //返回最小值 Math.min(x,y,z) //生成一个0和1之间的伪随机数 Math.random() //3的平方根 Math.sqrt(3) //e的三次幂 Math.exp(3)
在JavaScript中算术运算的溢出情况不会报错,无穷大用Infinity表示,负无穷大用-Infinity表示。
换,其变量是无类型的,在声明后可以被赋予任何类型的值。
*日期和时间:
var then= new Date(2011,0,1) ; //2011年1月1日 var later=new Date(2011,0,1,17,10,30); var now=new Date(); //当前日期时间 later.getFullYear();//输出2011 later.getFullMonth;//输出0,月份从0开始 later.getFullDate();//输出1 later.getDay();//输出5,星期五;0表示周日。 later.getHours();//输出时间
二、字符串
文本字符串是由一组16位值组成的不可变的有序序列。字符串的下标索引从0开始。两个字符串的了解可以通过加号“+”运算符
msg="hello, "+"world";//输出"hello, world"
字符串的主要属性和方法:
var s="hello, world" s.length //输出12 s.charAt(0)//输出h s.charAt(s.length-1)//输出d s.substring(1,4) //输出第2-4个字符 s.slice(-3)//输出最后三个字符 s.indexOf("l")//输出2,字符l首次出现的位置 s.lastIndexOf("l")//输出10,字符最后一次出现的位置 s.indexOf("l",3)//输出3,字符在位置3及之后首次出现的位置 s.split(", ")//["hello","world"],分割成子串 s.replace("h","H")//替换,输出"Hello, world" s.toUpperCase()//输出"HELLO, WORLD"
在JavaScript中字符串是固定不变的,上面的replace()和toUpperCase()方法实际返回的是新创建的字符串。
三、布尔值
*布尔值只有两个值:true和false。通常比较语句的返回值都是布尔值。如"a==4"用来监测a的值是否等于4。
*任意JavaScript的值都可以转换为布尔值。undefined/null/0/-0/NaN/""(空字符串)会被转换成false。其他所有值,包括对象(数组)都会被转换成true。
*布尔运算:逻辑与&&/逻辑或||/布尔非NOT。
四、全局对象
当JavaScript解释器启动时(或者任何Web浏览器加载新页面的时候),将创建一个新的全局对象,并给它一组定义的初始属性:全局属性如undefined、Infinity和NaN;全局函数,如IsNaN()、parsInIt()等;构造函数如Date()、RegExp()等;全局对象如Math等。
在客户端JavaScript中,在其表示的浏览器窗口中的所有JavaScript代码中,Window对象充当了全局对象,这个对象有一个属性window引用其自身,可以代替this来引用全局对象。如果代码又声明了一个全局变量,那这个全局变量则是全局对象的一个属性。
五、包装对象
var s="hello world"; var word=s.substring(s.indexOf(" ")+1,s.length);
这里s是字符串,而非对象,但还是可以有属性,这里是因为当引用了字符串的属性时JavaScript会自动调用new String()的方法转换成对象。同理,在对数字和布尔值类似场景时也会通过Number()和Boolean()构造函数创建对象。
需要注意的是,一旦属性引用结束,这个新创建的对象就会销毁。
var s="test"; //创建一个字符串 s.len=4; //给其设置一个属性 var t=s.len; //创建变量t
在上述代码中,最终t的值是undefined。在第二行代码后,临时创建的第二行代码的包装对象具备了len这个属性(注意不是本身自带的length属性!)已经被销毁,在第三行继续创建一个新的包装对象,这个对象是不具备len属性的。
对于基于原始值创建的包装对象,“==”是成立的,但全等“===”不成立。通过“typeof”运算符可以看出类型的不同。
六、不可变的原始值和可变的对象引用
JavaScript中的原始值(数字、布尔值、字符串、null、undefined)与对象有着根本区别,即他们是不可改变的,任何方法都无法更改一个原始值。对于undefined、null、数字和布尔值,这点很好理解。但对于字符串会显得不够明显,总会认为可以将其视为数组似的对象,从而改变其中字符值。实际上,JavaScript禁止如此。字符串的方法修改的字符串实际返回的是一个修改后新的字符串。
var s="hello"; s.toUppercase(); //这里返回"HELLO",临时创建一个新对象存储 s; //输出"hello",并未改变
原始值的比较就是值的比较,只有在其值相等时才相等;其中对于两个单独的字符串,当且仅当它们长度相等并且每个对应索引的字符都相等时,才相等。
而对象的比较并非值的比较,即便两个对象包含同样的属性和对应相同的值,也不相等;对于两个单独的数组也是如此。只有当两个对象引用的是同一个基对象时,它们才相等。
var o={x:1}, p={x:1}; //创建了两个单独的对象 o===p; //输出false;两个单独的对象永不相等 var a=[],b=[]; a===b; //输出false;数组也与上述一样 //引用对象的情况 var a=[]; //创建一个数组 var b=a; //创建一个数组,引用a var c=a; //创建一个数组,引用a c[0]=1; //通过c来修改引用 a[0]; //输出1 b[0]; //输出1 a===b; //true c===a; //true b===c; //true
七、变量声明
JavaScript中在使用一个变量之前应当先声明:
var i; var a,b,c; //也可以同行一次声明多个变量 var m=1,s="hello",t; var n=10; //声明创建一个基本数值类型的变量n n="love"; //也可以灵活转变其数据类型
八、变量作用域
一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域;在函数内声明的变量只在函数体内有效,是局部变量;函数的参数也是局部变量。
在函数体内部,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,则全局变量在函数内会被局部变量覆盖。
var scope="global"; //声明一个全局变量 function checkscope() { var scope="local"; //函数体内的局部变量 return scope; } checkscope(); //返回“local”
需要注意,如果给一个未声明的变量赋值,JavaScript实际会给全局对象创建一个同名属性,使其看起来像一个全局变量。因此,虽然在全局作用域内可以不声明创建一个全局对象,但在声明局部变量时必须使用var语句:
scope="global"; //创建一个全局变量 function checkscope2() { scope="local"; //这里未使用“var”,则解释器认为是上述的全局变量 myscope="local"; //未使用"var",则创建了一个全局变量 return [scope,myscope]; //返回两个值 } checkscope2(); //返回["local","loacl"] scope; //返回"local" myscope; //返回"local"
局部作用域嵌套的情况:
var scope="global scope"; //全局变量 function checkscope(){ var scope="loacl scope"; //局部变量 function nested() { var scope="nested scope"; //嵌套作用域内的局部变量 return scope; //返回当前作用域内的值 } return nested(); } checkscope();
*在类似C语言等编程语言中,花括号内的每一段代码都具有自己的作用域,在其内声明的变量对外是不可见的,这是块级作用域(block scope);但在JavaScript中使用的是函数作用域(function scope):变量在声明它的函数体以及这个函数体内嵌套的任意函数体内都是可见的。所以在一个函数体内,不管在什么位置声明的变量,甚至在它被声明之前的语句也可以使用它(不会提示报错),但只有在它被声明以及赋值的位置之后才可以正确的使用它被赋予的值。因此,一般情况下,较好的习惯是在函数体中把要声明的局部变量放在函数体顶部。
var scope="global"; function f() { //输出“undefined”,可见的是后面声明的局部变量,而非全局变量 //但因为这个局部变量还没有赋值 console.log(scope); //局部变量在这里赋值,但在函数体内任何位置都可见 var scope="local"; console.log(scope); //输出“local” }
*对于全局变量的隐式和显示声明的区别就是,显式使用var语句声明的全局变量是不可删除的,而隐式声明的可以删除。
var a=1; //声明一个不可删除的全局变量 b=2; //创建全局对象一个可以删除的属性 this.c=3; //同上,可以删除 delete a; //返回false delete b; //返回true delete c; //返回true