javascript select 二级联动Father(“0”,“根级”)这个用法是什么意思求详解

JavaScript高级程序设计_甜梦文库
JavaScript高级程序设计
第二章 ECMAScript 基础http://www.jb51.net/w3school/js/pro_js_operators_comma.htm2.1 语法? ? ? ? ?区分大小写 变量不区分类型, var 定义任何类型,5+6=11 每条语句结尾可以省略分号 注释与 C,C++,java,php 相同 代码段要封闭,{代码块}2.2 变量var 变量 1,变量 2,变量 3;(多个变量) var 变量名称=变量值; 变量命名原则:变量名称不能和系统保留关键字相同 变量类型: 字符型(string):必须括在英文双引号或单引号之间 整型(Integer):直接书写结果 浮点型(float):直接书写结果 布尔型(Boolean):直接书写结果,但只有 true 和 false 两种结果 在使用变量之前可以不声明:解释程序会将其创建为全局变量并初始化指定的值。若不注意很危险。 命名方式: 匈牙利命名法,但这种命名法对于跨平台移植简直是灾难。这种命名方法是由 Microsoft 程序员查尔斯? 西蒙尼(Charles Simonyi) 提出的。其主要思想是“在变量 和函数名中加入前缀以增进人们对程序的理解”。匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个 单词或多个单词组合,该单词要指明变量的用途。例如:lpszStr, 表示指向一个以'\0'结尾的字符串(sz)的长指针(lp)变量。 骆驼(Camel)Java 这样的平台下使用得当相多。骆驼命名法,正如它的名称所表示的那样,指的是混合使用大小写字母来构成标识符的名字。其中第一个单 词首字母小写,余下的单词首字母大写。例如:printEmployeePaychecks() 帕斯卡(Pascal)命名法与骆驼命名法类似。帕斯卡命名法则是第一个单词首字母大写。例如:DisplayInfo()和 UserName 都是采用了帕斯卡命名法。 事实上,很多程序设计者在实际命名时会将骆驼命名法和帕斯卡结合使用,例如变量名采用骆驼命名法,而函数采用帕斯卡命名法。 下划线命名法。下划线法是随着 C 语言的出现流行起来的,在 UNIX/LIUNX 这样的环境,以及 GNU 代码中使用非常普遍。2.3 关键字关键字 break case catch continue default in typeof 保留字 abstract double goto native static boolean enum implements package super byte export import private synchronized char extends int protected throws class final interface public transient const float long short volatile else finally for function if try new return switch this throw do var void while with delete instanceof2.4 原始值和引用值原始值(primitive value):是固定而简单的值,是存放在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置. 引用值则是比较大的对象,存放在堆(heap)中的对象 所有引用类型都集成自 Object. 如果一个值是引用类型的,那么它的存储空间将从堆中分配.由于引用值的大小会改变,所以不能把它放在栈中,否则会降低查询速度.相反,存放变量的栈空间中 的值是该对象存储在堆中的地址.地址的大小是固定的,所以把它存放在栈中对变量性能无任何负面影响.2.5 原始类型原始类型(primitive type)有以下 5 种类型:Undefined,Null,Boolean,Number,String(别的语言都为引用类型) 我们可以使用 typeof 来判断一个是否在某个类型的范围内.2.5.1typeof 运算符如果变量是 Undefined 类型的 如果变量是 Boolean 类型的 如果变量是 Number 类型的 如果变量是 String 类型的 如果变量是一种引用类型或者 Null 类型的&undefined& &boolean& &number& &string& &object&注意: 1.返回值为字符串类型.alert(typeof(1))//number2.和原始类型比,还差了个 null,这个比较特殊,使用 typeof(null),返回的是&object&,我们将 null 理解成是 object 的占位符.2.5.2Undefined 类型Undefined 类型只有一个值,就是 undefined.当声明的变量未初始化时,该变量的默认值是 undefined.注意:值 undefined 并不等同于未定义的值.但 typeof 运算符并不能真正区分这两种值.看以下代码:代码:var undefinedV//未设置值时赋予undefined类型字面量 document.write(typeof(undefinedValue)+&&br/&&); document.write(typeof(undefinedValue2));查看效果:页面输出都为 undefined undefined 此处,我们看到,两个结果都为 undefined,即使第 undefinedValue2 没有被声明,依然可以得到输出.注意:1.对未声明的变量使用其他操作符,都会引起错误,因为其他运算符只能运行于已声明的变量上. 2.如果函数没有明确的返回值,则函数的返回值也是 undefined 总结:变量声明为赋值就使用和根本就没声明的变量都为 undefined,前者不报错输出为 undefined 后者报错2.5.3Null 类型Null 类型的值也只有一个,就是 null.值 undefined 实际上是从值 null 派生过来的,因此 ECMAScript 把它们定义为相等的. 执行下列代码,返回的 true: alert(undefined==null);注意: 尽管两个值相等,但是它们的含义却不相等.undefined 是声明了变量,但未定义值.取英文原意。 null 则表示尚未存在的对象.如果函数或方法返回的是对象,当找不到这个对象时,返回的通常是 null.2.5.4Boolean 类型Boolean 对象有两个值:true 和 false.2.5.5Number 类型ECMA-262 中定义的最特殊的类型是 Number 类型.这种类型既可以表示 32 位的整数,还可以表示 64 位的浮点数. 整数可以表示为 8 进制,10 进制和 16 进制的字面量.其中: 8 进制要求以 0 开头,其后的数字可以是任何的八进制数字(0-7); 16 进制要求以 0x 开头其后的数字可以是任何的十六进制数字(0-f),其中字母可以大写,可以小写,无限制.注意: 尽管所有的整数都可以表示为八进制或者十六进制,但所有的数字运算结果返回的都是十进制结果.对于非常大或者非常小的数字,我们可以使用科学计数法来表示浮点值.采用科学计数法,可以把一个数表示为以下形式:数字 e 数字见例子:3.14e2=3.14*100=314//输出为 10 进制 也可以用科学技术法表示很小的数字,例如: 314-e2=3.14//报错 e2 未定义 Number 类型还有几个特殊值: Number.MAX_VALUE 和 Number.MIN_VALUE,分别表示最大值和最小值. Number.POSITIVE_INFINITY(无穷大):当生成的值大于 Number.MAX_VALUE 时,就会被赋予 Number.POSITIVE_INIFITY Number.NEGATIVE_INFINITY(无穷小):当生成的值小于 Number.MIN_VALUE 时,就会被赋予 Number.NEGATIVE_INFINITY 还有一个特殊值,就是 NaN,意思就是 not a number.一般函数转换时产生 自身不相等 alert(NaN == NaN)//FALSE, 故推荐用 isNaN()函数判断 alert(isNaN('blue'))//false注意:Number 类型在真正进行运算之前,实际上存储的是字符串.2.5.6String 类型String 类型是唯一没有固定大小的原始类型. String 类型有以下几个字符量变量:\n \t \b \r \f \\换行 制表符 空格 回车 换页 反斜杠 \' \& \0nnn \xnn \xnnnn单引号 双引号 八进制 nnn,n 表示 0-7 任意数字 十六进制 nn,n 表示 0-f 十六进制 nnnn,4 位表示的 Unicode 字符2.6 转换 2.6.1 转换成字符串ECMAScript 的 Boolean 值、数字和字符串的原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法。例如,要获得字符串的长度,可以 采用下面的代码: var sColor = &red&; alert(sColor.length); //输出 &3& 尽管 &red& 是原始类型的字符串,它仍然具有属性 length,用于存放字符串的大小。 总而言之,3 种主要的原始类型 Boolean 值、数字和字符串都有 toString() 方法,可以把它们的值转换成字符串。 Boolean 类型的 toString() 方法只是输出 &true& 或 &false&,结果由变量的值决定: var bFound = alert(bFound.toString()); //输出 &false& Number 类型的 toString() 方法比较特殊,它有两种模式,即默认模式和基模式。采用默认模式,toString() 方法只是用相应的字符串输出数字值(无论是 整数、浮点数还是科学计数法) ,如下所示: var iNum1 = 10; var iNum2 = 10.0; var iNum3 = 3.14e2;//科学计数法 alert(iNum1.toString()); //输出 &10& alert(iNum2.toString()); //输出 &10& alert(iNum3.toString()); //输出 &314& 注释:在默认模式中,无论最初采用什么表示法声明数字,Number 类型的 toString() 方法返回的都是数字的十进制表示。因此,以八进制或十六进制字面 量形式声明的数字输出的都是十进制形式的。 采用 Number 类型的 toString() 方法的基模式,可以用不同的基输出数字,例如二进制的基是 2,八进制的基是 8,十六进制的基是 16。 基只是要转换成的基数的另一种加法而已,它是 toString() 方法的参数: var iNum = 10; alert(iNum1.toString(2)); //输出 &1010& alert(iNum1.toString(8)); //输出 &12& alert(iNum1.toString(16)); //输出 &A& 在前面的示例中,以 3 种不同的形式输出了数字 10,即二进制形式、八进制形式和十六进制形式。HTML 采用十六进制表示每种颜色,在 HTML 中处理 数字时这种功能非常有用。 注释:对数字调用 toString(10) 与调用 toString() 相同,它们返回的都是该数字的十进制形式。2.6.2转换成数字ECMAScript 提供了两种把非数字的原始值转换成数字的方法,即 parseInt() 和 parseFloat()。前者把值转换成整数,后者把值转换成浮点数。只有对 Strin g 类型调用这些方法,它们才能正确运行;对其他类型返回的都是 NaN。1.parseInt()在判断字符串是否是数字值前,parseInt() 和 parseFloat() 都会仔细分析该字符串。 parseInt() 方法首先查看位置 0 处的字符, 判断它是否是个有效数字; 如果不是, 该方法将返回 NaN, 不再继续执行其他操作。 但如果该字符是有效数字, 该方法将查看位置 1 处的字符,进行同样的测试。这一过程将持续到发现非有效数字的字符为止,此时 parseInt() 将把该字符之前的字符串转换成数字。 例如,如果要把字符串 &12345red& 转换成整数,那么 parseInt() 将返回 12345,因为当它检查到字符 r 时,就会停止检测过程。字符串中包含的数字 字面量会被正确转换为数字,比如 &0xA& 会被正确转换为数字 10。不过,字符串 &22.5& 将被转换成 22,因为对于整数来说,小数点是无效字符。示例如下: var iNum1 = parseInt(&12345red&); //返回 12345 var iNum1 = parseInt(&0xA&); //返回 10 var iNum1 = parseInt(&56.9&); //返回 56 var iNum1 = parseInt(&red&); //返回 NaN parseInt() 方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。基是由 parseInt() 方法的第二个参数指定的,当然, 对二进制、八进制、十六进制甚至十进制(默认模式),都可以调用 parseInt() 方法: var iNum1 = parseInt(&10&, 2); //返回 2 var iNum2 = parseInt(&10&, 8); //返回 8 var iNum1 = parseInt(&AF&, 16); //返回 175 var iNum3 = parseInt(&10&, 10); //返回 10 如果十进制数包含前导 0,那么最好采用基数 10,这样才不会意外地得到八进制的值。例如: var iNum1 = parseInt(&010&); //返回 8 var iNum2 = parseInt(&010&, 8); //返回 8 var iNum3 = parseInt(&010&, 10); //返回 10 var iNum4 = parseInt(&010&); //返回 8 在这段代码中,两行代码都把字符 &010& 解析成一个数字。第一行代码把这个字符串看作八进制的值,解析它的方式与第二行代码(声明基数为 8)相同。 最后一行代码声明基数为 10,所以 iNum3 最后等于 10。2.parseFloat()parseFloat() 方法与 parseInt() 方法的处理方式相似,从位置 0 开始查看每个字符,直到找到第一个非有效的字符为止,然后把该字符之前的字符串转换 成整数。 不过,对于这个方法来说,第一个出现的小数点是有效字符。如果有两个小数点,第二个小数点将被看作无效的。parseFloat() 会把这个小数点之前的字符 转换成数字。这意味着字符串 &11.22.33& 将被解析成 11.22。使用 parseFloat() 方法的另一不同之处在于,字符串必须以十进制形式表示浮点数,而不是用八 进制或十六进制。 该方法会忽略前导 0, 所以八进制数 0102 将被解析为 102。 对于十六进制数 0xA, 该方法将返回 NaN, 因为在浮点数中, x 不是有效字符。 此外,parseFloat() 方法也没有基模式。下面是使用 parseFloat() 方法的一些示例: var fNum1 = parseFloat(&12345red&); //返回 12345 var fNum2 = parseFloat(&0xA&); //返回 NaN var fNum3 = parseFloat(&11.2&); //返回 11.2 var fNum4 = parseFloat(&11.22.33&); //返回 11.22 var fNum5 = parseFloat(&0102&); //返回 102 var fNum1 = parseFloat(&red&); //返回 NaN2.6.3强制类型转换您还可以使用强制类型转换(type casting)来处理转换值的类型。使用强制类型转换可以访问特定的值,即使它是另一种类型的。(注:cast 有“铸造”之意, 很贴合“强制转换”的意思。) ECMAScript 中可用的 3 种强制类型转换如下: Boolean(value) - 把给定的值转换成 Boolean 型; Number(value) - 把给定的值转换成数字(可以是整数或浮点数); String(value) - 把给定的值转换成字符串; 用这三个函数之一转换值,将创建一个新值,存放由原始值直接转换成的值。这会造成意想不到的后果。1.Boolean() 函数当要转换的值是至少有一个字符的字符串、非 0 数字或对象时,Boolean() 函数将返回 true。如果该值是空字符串、数字 0、undefined 或 null,它将 返回 false。 可以用下面的代码测试 Boolean 型的强制类型转换: var b1 = Boolean(&&); //false - 空字符串 var b2 = Boolean(&hello&); //true - 非空字符串 var b1 = Boolean(50); //true - 非零数字 var b1 = Boolean(null); //false - null var b1 = Boolean(0); //false - 零 var b1 = Boolean(new object()); //true - 对象 2.Number() 函数Number() 函数的强制类型转换与 parseInt() 和 parseFloat() 方法的处理方式相似,只是它转换的是整个值,而不是部分值。 还记得吗,parseInt() 和 parseFloat() 方法只转换第一个无效字符之前的字符串,因此 &1.2.3& 将分别被转换为 &1& 和 &1.2&。用 Number() 进行强制类型 转换,&1.2.3& 将返回 NaN,因为整个字符串值不能转换成数字。如果字符串值能被完整地转换,Number() 将判断是调用 parseInt() 方法还是 parseFloat() 方法。下表说明了对不同的值调用 Number() 方法会发生的情况: 用法 结果Number(false) 0 Number(true) 1 Number(undefined) NaN Number(null) 0 Number(&1.2&) 1.2 Number(&12&) 12 Number(&1.2.3&) NaN Number(new object()) NaN Number(50) 503.String() 函数最后一种强制类型转换方法 String() 是最简单的,因为它可把任何值转换成字符串。 要执行这种强制类型转换, 只需要调用作为参数传递进来的值的 toString() 方法, 即把 12 转换成 &12&, 把 true 转换成 &true&, 把 false 转换成 &false&, 以此类推。 强制转换成字符串和调用 toString() 方法的唯一不同之处在于,对 null 和 undefined 值强制类型转换可以生成字符串而不引发错误: var s1 = String(null); //&null& var oNull = var s2 = oNull.toString(); //会引发错误 在处理 ECMAScript 这样的弱类型语言时,强制类型转换非常有用,不过应该确保使用值的正确。2.7 引用类型var o = new Object();//括号不是必须的2.7.1Object 类Object 类具有以下属性: Constructor:对创建对象的函数的引用(指针),对于 Object 类,该指针指向原始的 object()函数. Prototype:对该对象的对象原型的引用.它默认返回的是 Object 对象的一个实例. Object 类还具有以下方法: HasOwnProperty(property):判断某个对象是否具备某个属性.必须用字符串指定该属性。如:o.HasOwnProperty(&name&) IsPrototypeOf(object):判断该对象是否存在于另一个对象的原型.是则返回 true,否则返回 false. 注: 使用方法:object1.isPrototypeOf(object2);object1 是一个对象的实例,object2 是另一个对象的实例,如果 object2 的原型链包含 object1,那么就返回 true. 原型链可以用来在同一个对象类型的不同实例之间共享功能. PropertyIsEnumerable(property):判断给定的属性是否可以使用 for..in 语句进行枚举. ToString():返回对象的原始字符串表示. ValueOf():返回最适合该对象的原始值,对于许多类,该方法的返回值都和 ToString()方法结果相同. 例子:&script type=&text/javascript&& function Person(name,age){ this.name= this.age= } Person.prototype.getAge=function(){ return this. } var p = new Person(&ww&,20); document.write(p.hasOwnProperty(&age&)+&&br/&&); function Student(name,age,banji){ Person.call(this,name,age); this.banji= } Student.prototype = new Person(); Student.prototype.getBanji=function(){ return this. } var s = new Student(&w&,1,1); document.write(Person.prototype.isPrototypeOf(s)); &/script&效果: true true2.7.2Boolean 类Boolean 类是 Boolean 原始类型的引用类型.要创建 Boolean 对象,只需要传递 boolean 值作为参数即可. Boolean 对象会覆盖 object 类的 valueOf()和 ToString()方法.返回原始值,即:true 或 false 查看如下代码,结果该返回什么呢? var falseObject = new Boolean(&false&); var result = falseObject && document.write(result); 答案是结果返回 true. 在这段代码中,用 false 值创建了 Boolean 对象,然后这个值与原始值 true 进行 and 操作.在 Boolean 运算中,false 和 true 进行与操作的结果是 false. 不过在这行代码中,计算的是 falseObject,而不是它的值 false. 在 Boolean 表达式中,所有对象都会转换为 true.所以,在进行 Boolean 运算中,falseObject 的值为 true.true and true,结果当然也是为 true.javascript 中尽量用原 始型以免错误。 看下面的代码: var falseObject = new Boolean(&false&); document.write(falseObject+&&br/&&);var trueObject = new Boolean(&true&); document.write(trueObject); 结果: true true2.7.3Number 类Number 类是 Number 原始值的引用类型.要得到 Number 原始值,只需要使用 Number 类的 valueOf()方法即可. javascript 中尽量用原始型以免错误。 Number 有几个方法: 1.toFixed()返回的是具有指定位数小数的数字的字符串 代码: var nn = new Number(99); document.write(nn.toFixed(2)); 效果: 99.00 2.toExponential()返回的是用科学计数法表示的数字的字符串表示. 代码: var nn = new Number(99); document.write(nn.toExponential(2)); 效果: 9.90e+12.7.4String 类下标从 0 开始到 length-1,汉字(双字节)也算一个字符。&啊 a 的&.length//output 3函数按功能分类String 查找类函数? ? ? ? ? ?charAt() 函数 -- 返回指定位置(n)的字符 charCodeAt() 函数 -- 返回指定位置(n)字符的 Unicode 编码 indexOf() 函数 -- 返回 substring 在 string 中的位置,未找到返回-1 lastIndexOf() 函数 -- 返回 substring 在 string 中最后匹配(出现)的位置,从后往前匹配String 操作类函数 concat() 函数 -- 合并多个字符串,并返回合并的结果 ,一般用+号 replace(regexp/substr,newstr)--用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串说明,详细见正则表达式 字符串 stringObject 的 replace() 方法执行的是查找并替换的操作。它将在 stringObject 中查找与 regexp 相匹配的子字符串,然后用 replacement 来替换这些子串。如果 regexp 具有全局标志 g,那么 replace() 方法将替换所有匹配的子串。否则,它只替换第一个匹配子串。newstr可以是字符串,也可以是函数。如果它是字符串,那么每个匹配都将由字符串替换。但是 newstr中的$ 字符具有特定的含义。 ? ?slice(startPos, endPos) 函数 -- 返回被截取的字符串 ,一般用 substring split(separator,howmany) 函数 -- 将字符串分割为字符串数组,并返回此数组 参数 separator howmany 描述 必需。字符串或正则表达式,从该参数指定的地方分割 stringObject。 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个 参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。?substr(start,length) 函数 -- 返回从 start 下标开始的指定数目的字符 参数 描述 start必需。要抽取的子串的起始下标。必须是数值。如果是负数,那么该参数声明从字符串的尾部开 始算起的位置。也就是说,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推。length可选。子串中的字符数。必须是数值。如果省略了该参数,那么返回从 stringObject 的开始位置 到结尾的字串。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?substring(start,stop) 函数 -- 返回提取字符串中两个指定的索引号之间的字符,[半开半闭)String 转换类函数 fromCharCode() 函数 -- 返回多个 Unicode 代码组成的字符串String 比较类函数 localeCompare() 函数 -- 使用本地规则比较字符串 string 与 string2,并返回比较结果String 大小写转换函数 toLocaleLowerCase() 函数 -- 返回本地小写字符串 toLocaleUpperCase() 函数 -- 返回本地大写字符串 toLowerCase() 函数 -- 返回小写字符串 toUpperCase() 函数 -- 返回大写字符串HTML 标签类函数 anchor() 函数 -- 返回 HTML a 标签中 name 属性为 str 的锚 big() 函数 -- 返回 HTML big 标签定义的大字体 blink() 函数 -- 返回使用 HTML blink 标签定义的闪烁字符串 bold() 函数 -- 返回使用 HTML b 标签定义的粗体字符串 fixed() 函数 -- 返回使用 HTML tt 标签定义的单间距字符串 fontcolor() 函数 -- 返回使用 HTML font 标签中 color 属性定义的带有颜色的字符串 fontsize() 函数 -- 返回使用 HTML font 标签中 size 属性定义的指定尺寸的字符串 italics() 函数 -- 返回使用 HTML i 标签定义的斜体字符串 link() 函数 -- 返回使用 HTML a 标签定义的链接 small() 函数 -- 返回使用 HTML small 标签定义的小字体的字符串 strike() 函数 -- 返回使用 HTML strike 标签定义删除线样式的字符串 sub() 函数 -- 返回使用 HTML sub 标签定义的下标字符串 sup() 函数 -- 返回使用 HTML sup 标签定义的上标字符串 在本例中,我们会为文本添加一个锚: &script type=&text/javascript&& var txt=&Hello world!& document.write(txt.anchor(&myanchor&)) &/script& 上面的代码将输出为纯粹的 HTML: &a name=&myanchor&&Hello world!&/a&2.7.5instanceof 运算符typeof(a); //无法识别引用类型对象,引用类型都返回object,故此引入instanceof (b instanceof String)2.8 运算符 2.8.1delete delete 运算符删除对以前定义的对象属性或方法的引用。例如:一元运算符var o = new O o.name = &David&; alert(o.name); delete o. alert(o.name); //输出 &undefined& //输出 &David&在这个例子中,删除了 name 属性,意味着强制解除对它的引用,将其设置为 undefined(即创建的未初始化的变量的值)。 delete 运算符不能删除开发者未定义的属性和方法。例如,下面的代码将引发错误:delete o.toS即使 toString 是有效的方法名,这行代码也会引发错误,因为 toString() 方法是原始的 ECMAScript 方法,不是开发者定义的。 void void 运算符对任何值返回 undefined。该运算符通常用于避免输出不应该输出的值,例如,从 HTML 的 &a& 元素调用 JavaScript 函数时。要正确做到这 一点,函数不能返回有效值,否则浏览器将清空页面,只显示函数的结果。例如:&a href=&javascript:window.open('about:blank')&&Click me&/a&如果把这行代码放入 HTML 页面,点击其中的链接,即可看到屏幕上显示 &[object]&。TIY 这是因为 window.open() 方法返回了新打开的窗口的引用。然后该对象将被转换成要显示的字符串。 要避免这种效果,可以用 void 运算符调用 window.open() 函数:&a href=&javascript:void(window.open('about:blank'))&&Click me&/a& 这使 window.open() 调用返回 undefined,它不是有效值,不会显示在浏览器窗口中。 提示:请记住,没有返回值的函数真正返回的都是 undefined。 一元加法和一元减法:加法对数字不起作用,减法为求负数2.8.2位运算符有符号整数范围-21 亿~~21 亿,无符号 0~~42 亿 所有整数字面量都默认存储为有符号整数,只有位运算符才能创建无符号整数var iNumber = 18; alert(iNumber.toString(2));//10010,2 代表转换成二进制 iNumber = -18; alert(iNumber.toString(2));//-10010,未显示成补码形式 ECMAScript 为了避//免开发者访问符号位位运算符 NOT(~),求负数减一 var iNumber = 25; // var i2 = ~iN// alert(i2);//-26, AND(&),只要都为 1 才是 1 25&10 = 8 25 =
OR(|)25|3 = 27 XOR(^)不一样就为 1,25^3 = 26 有符号左移(&&):右侧 0 填充,有符号右移(&&)符号位填充。 无符号右移(&&&):正数同有符号,负数用 0 填充数字会变得很大的正数。故小心 详细见网址: http://www.w3school.com.cn/js/pro_js_operators_bitwise.asp2.8.3Boolean 运算符Boolean 运算符非常重要,它使得程序语言得以正常运行。 Boolean 运算符有三种:NOT、AND 和 OR。ToBoolean 操作在学习各种逻辑运算符之前,让我们先了解一下 ECMAScript-262 v5 规范中描述的 ToBoolean 操作。 抽象操作 ToBoolean 将其参数按照下表中的规则转换为逻辑值:0,undefined,null,NaN,&&参数类型 Undefined Null Boolean Number String Object 逻辑 NOT 运算符 由感叹号(!)表示。 与逻辑 OR 和逻辑 AND 运算符不同的是,逻辑 NOT 运算符返回的一定是 Boolean 值。 逻辑 NOT 运算符的行为如下: 结果 false false 结果等于输入的参数(不转换) 如果参数为 +0, -0 或 NaN,则结果为 false;否则为 true。 如果参数为空字符串,则结果为 false;否则为 true。 true? ? ? ? ? ?如果运算数是对象,返回 false 如果运算数是数字 0,返回 true 如果运算数是 0 以外的任何数字,返回 false 如果运算数是 null,返回 true 如果运算数是 NaN,返回 true 如果运算数是 undefined,发生错误通常,该运算符用于控制循环: var bFound = var i = 0;while (!bFound)bFound = } else { i++; } }{if (aValue[i] == vSearchValues) {在这个例子中,Boolean 变量(bFound)用于记录检索是否成功。找到问题中的数据项时,bFound 将被设置为 true,!bFound 将等于 false,意味着运 行将跳出 while 循环。 判断 ECMAScript 变量的 Boolean 值时,也可以使用逻辑 NOT 运算符。这样做需要在一行代码中使用两个 NOT 运算符。无论运算数是什么类型,第一个 NOT 运算符返回 Boolean 值。第二个 NOT 将对该 Boolean 值求负,从而给出变量真正的 Boolean 值。 var sRed = &red&; var iZero = 0; var oObject = new O document.write(&sRed 的逻辑值是 & + (!!sRed));// true document.write(&iZero 的逻辑值是 & + (!!iZero));// false document.write(&oObject 的逻辑值是 & + (!!oObject));// true 逻辑 AND 运算符 在 ECMAScript 中,逻辑 AND 运算符用双和号(&&)表示: 例如: var bTrue = var bFalse = var bResult = bTrue && bF 下面的真值表描述了逻辑 AND 运算符的行为: 运算数 1 true true false false 运算数 2 true false true false 结果 true false false false逻辑 AND 运算的运算数可以是任何类型的,不止是 Boolean 值。 如果某个运算数不是原始的 Boolean 型值,逻辑 AND 运算并不一定返回 Boolean 值:? ? ? ? ?如果一个运算数是对象,另一个是 Boolean 值,返回该对象。 如果两个运算数都是对象,返回第二个对象。 如果某个运算数是 null,返回 null。 如果某个运算数是 NaN,返回 NaN。 如果某个运算数是 undefined,发生错误。与 Java 中的逻辑 AND 运算相似,ECMAScript 中的逻辑 AND 运算也是简便运算,即如果第一个运算数决定了结果,就不再计算第二个运算数。对于逻辑 AND 运算来说,如果第一个运算数是 false,那么无论第二个运算数的值是什么,结果都不可能等于 true。 考虑下面的例子: var bTrue = var bResult = (bTrue && bUnknown); alert(bResult); //发生错误 //这一行不会执行这段代码在进行逻辑 AND 运算时将引发错误,因为变量 bUnknown 是未定义的。变量 bTrue 的值为 true,因为逻辑 AND 运算将继续计算变量 bUnknown。这样做就会引发错误,因为 bUnknown 的值是 undefined,不能用于逻辑 AND 运算。 如果修改这个例子,把第一个数设为 false,那么就不会发生错误: var bFalse = var bResult = (bFalse && bUnknown); alert(bResult); //输出 &false&在这段代码中,脚本将输出逻辑 AND 运算返回的值,即字符串 &false&。即使变量 bUnknown 的值为 undefined,它也不会被计算,因为第一个运算数的 值是 false。 提示:在使用逻辑 AND 运算符时,必须记住它的这种简便计算特性。 逻辑 OR 运算符 ECMAScript 中的逻辑 OR 运算符与 Java 中的相同,都由双竖线(||)表示: var bTrue = var bFalse = var bResult = bTrue || bF 下面的真值表描述了逻辑 OR 运算符的行为: 运算数 1 true true false 运算数 2 true false true 结果 true true true falsefalsefalse与逻辑 AND 运算符相似,如果某个运算数不是 Boolean 值,逻辑 OR 运算并不一定返回 Boolean 值:? ? ? ? ?例如:如果一个运算数是对象,并且该对象左边的运算数值均为 false,则返回该对象。 如果两个运算数都是对象,返回第一个对象。 如果最后一个运算数是 null,并且其他运算数值均为 false,则返回 null。 如果最后一个运算数是 NaN,并且其他运算数值均为 false,则返回 NaN。 如果某个运算数是 undefined,发生错误。与逻辑 AND 运算符一样,逻辑 OR 运算也是简便运算。对于逻辑 OR 运算符来说,如果第一个运算数值为 true,就不再计算第二个运算数。var bTrue = var bResult = (bTrue || bUnknown); alert(bResult); //输出 &true&与前面的例子相同,变量 bUnknown 是未定义的。不过,由于变量 bTrue 的值为 true,bUnknown 不会被计算,因此输出的是 &true&。 如果把 bTrue 改为 false,将发生错误: var bFalse = var bResult = (bFalse || bUnknown); alert(bResult); //发生错误 //不会执行这一行2.8.4加减乘除运算符乘性运算符(*) : 无穷大=infinity,负无穷大 = -infinity NaN*XX=NaN,infinity*0=NaN,infinity*2(-2)=infinity(-infinity), infinity*infinity= 加法运算符(+) : 5+5=10;5+&5&=55;注意类型,若有字符串就都转换成字符串 关系运算符(&,&,&=,&=),两个表达式对应字符比较 5&2//&a&&&B& //false,按照字母顺序比较,但大小总在小写前边,应&a&.toUpperCase() &5&&&23&//true,比较的是&5&和&2&的代码 53&50&a&&23 //false,&a&无法转换成数字2.8.5等性运算符判断两个变量是否相等是程序设计中非常重要的运算。在处理原始值时,这种运算相当简单,但涉及对象,任务就 稍有点复杂。 ECMAScript 提供了两套等性运算符:等号和非等号用于处理原始值,全等号和非全等号用于处理对象。等号和非等号在 ECMAScript 中,等号由双等号(==)表示,当且仅当两个运算数相等时,它返回 true。非等号由感叹号加等号(!=)表示,当且 仅当两个运算数不相等时,它返回 true。为确定两个运算数是否相等,这两个运算符都会进行类型转换。 执行类型转换的规则如下:? ? ? ?如果一个运算数是 Boolean 值,在检查相等性之前,把它转换成数字值。false 转换成 0,true 为 1。 如果一个运算数是字符串,另一个是数字,在检查相等性之前,要尝试把字符串转换成数字。 如果一个运算数是对象,另一个是字符串,在检查相等性之前,要尝试把对象转换成字符串。 如果一个运算数是对象,另一个是数字,在检查相等性之前,要尝试把对象转换成数字。 在比较时,该运算符还遵守下列规则:? ?值 null 和 undefined 相等。 在检查相等性时,不能把 null 和 undefined 转换成其他值。 ? ?如果某个运算数是 NaN,等号将返回 false,非等号将返回 true。 如果两个运算数都是对象,那么比较的是它们的引用值。如果两个运算数指向同一对象,那么等号返回 true,否则两个运算数不等。 重要提示:即使两个数都是 NaN,等号仍然返回 false,因为根据规则,NaN 不等于 NaN。 下表列出了一些特殊情况,以及它们的结果: 表达式 值null == undefined true &NaN& == NaN 5 == NaN NaN == NaN NaN != NaN false == 0 true == 1 true == 2 undefined == 0 null == 0 &5& == 5 false false false true true true false false false true全等号和非全等号等号和非等号的同类运算符是全等号和非全等号。这两个运算符所做的与等号和非等号相同,只是它们在检查相等性前,不执行类型 转换。 全等号由三个等号表示(===),只有在无需类型转换运算数就相等的情况下,才返回 true。 例如: var sNum = &66&; var iNum = 66; alert(sNum == iNum); //输出 &true& alert(sNum === iNum); //输出 &false& 在这段代码中,第一个 alert 使用等号来比较字符串 &66& 和数字 66,输出 &true&。如前所述,这是因为字符串 &66& 将被转换成 数字 66,,然后才与另一个数字 66 进行比较。第二个 alert 使用全等号在没有类型转换的情况下比较字符串和数字,当然,字符 串不等于数字,所以输出 &false&。 非全等号由感叹号加两个等号(!==)表示,只有在无需类型转换运算数不相等的情况下,才返回 true。 例如: var sNum = &66&; var iNum = 66; alert(sNum != iNum); //输出 &false& alert(sNum !== iNum); //输出 &true& 这里,第一个 alert 使用非等号,把字符串 &66& 转换成数字 66,使得它与第二个运算数 66 相等。因此,计算结果为 &false&, 因为两个运算数是相等的。 第二个 alert 使用的非全等号。 该运算是在问: &sNum& 与 &iNum& 不同吗?这个问题的答案是: 是的 (true) , 因为 sNum 是字符串,而 iNum 是数字,它们当然不同。2.8.6条件运算符r = (a&b)?a:b;与 java 相同2.8.7赋值运算符请注意赋值(=)和等于(==)的区别2.8.8逗号运算符用逗号运算符可以在一条语句中执行多个运算。 例如:var iNum1 = 1, iNum = 2, iNum3 = 3; 逗号运算符常用变量声明中。2.9 语句 2.9.1 if 语句if(condition) statement1elsestatement2其中 condition 可以是任何表达式,计算的结果甚至不必是真正的 boolean 值,ECMAScript 会把它转换成 boolean 值。2.9.2迭代语句do-while、while、for 语句for-in 语句 for 语句是严格的迭代语句,用于枚举对象的属性。 它的语法如下:for例子:(propertyinexpression) statementfor (sProp in window) { alert(sProp); } 这里,for-in 语句用于显示 window 对象的所有属性。 前面讨论过的 PropertyIsEnumerable() 是 ECMAScript 中专门用于说明属性是否可以用 for-in 语句访问的方法。2.9.3有标签的语句可以用下列语句给语句加标签,以便以后调用: label : statement 例如: start : i = 5; 在这个例子中,标签 start 可以被之后的 break 或 continue 语句引用。2.9.4break 语句和 continue 语句break 和 continue 语句的不同之处 break 语句可以立即退出循环,阻止再次反复执行任何代码。 而 continue 语句只是退出当前循环,根据控制表达式还允许继续进行下一次循环。 与有标签的语句一起使用 break 语句和 continue 语句都可以与有标签的语句联合使用,返回代码中的特定位置。 通常,当循环内部还有循环时,会这样做,例如: var iNum = 0;outermost: for (var i=0; i&10; i++) { for (var j=0; j&10; j++) { if (i == 5 && j == 5) {} iNum++; } } alert(iNum); //输出 &55& 在上面的例子中,标签 outermost 表示的是第一个 for 语句。正常情况下,每个 for 语句执行 10 次代码块,这意味着 iNum++ 正常情况下将被执行 100 次, 在执行完成时, iNum 应该等于 100。 这里的 break 语句有一个参数, 即停止循环后要跳转到的语句的标签。 这样 break 语句不止能跳出内部 for 语句(即使用变量 j 的语句),还能跳出外部 for 语句(即使用变量 i 的语句)。因此,iNum 最后的值是 55,因为当 i 和 j 的值都等于 5 时,循环将 终止。 可以以相同的方式使用 continue 语句: var iNum = 0;outermost: for (var i=0; i&10; i++) { for (var j=0; j&10; j++) { if (i == 5 && j == 5) {} iNum++; } } alert(iNum); //输出 &95&在上例中, continue 语句会迫使循环继续, 不止是内部循环, 外部循环也如此。 当 j 等于 5 时出现这种情况, 意味着内部循环将减少 5 次迭代, 致使 iNum 的值为 95。 提示:可以看出,与 break 和 continue 联合使用的有标签语句非常强大,不过过度使用它们会给调试代码带来麻烦。要确保使用的标签具有说明性,同时 不要嵌套太多层循环。2.9.5with 语句with 语句用于设置代码在特定对象中的作用域。判断{}内是否为对象的方法后执行,很慢的。 它的语法: with (expression) statement 例如: var sMessage = &hello&; with(sMessage) { alert(toUpperCase()); } 在这个例子中, with 语句用于字符串, 所以在调用 toUpperCase() 方法时, 解释程序将检查该方法是否是本地函数。 如果不是, 它将检查伪对象 sMessage, 看它是否为该对象的方法。然后,alert 输出 &HELLO&,因为解释程序找到了字符串 &hello& 的 toUpperCase() 方法。 提示:with 语句是运行缓慢的代码块,尤其是在已设置了属性值时。大多数情况下,如果可能,最好避免使用它。 //输出 &HELLO&2.9.6switch 语句switch (expression) case value: case value: case value: case value: ... case value: default: 每个情况(case)都是表示“如果 expression 等于 value,就执行 statement”。 关键字 break 会使代码跳出 switch 语句。如果没有关键字 break,代码执行就会继续进入下一个 case。 关键字 default 说明了表达式的结果不等于任何一种情况时的操作(事实上,它相对于 else 从句)。ECMAScript 和 Java 中的 switch 语句 ECMAScript 和 Java 中的 switch 语句有两点不同。在 ECMAScript 中,switch 语句可以用于字符串,而且能用不是常量的值说明情况: var BLUE = &blue&, RED = &red&, GREEN = &green&;switch (sColor) { case BLUE: alert(&Blue&); case RED: alert(&Red&); case GREEN: alert(&Green&); default: alert(&Other&); } 这里,switch 语句用于字符串 sColor,声明 case 使用的是变量 BLUE、RED 和 GREEN,这在 ECMAScript 中是完全有效的。2.10 函数函数如何返回值? 函数 sayHi() 未返回值,不过不必专门声明它(像在 Java 中使用 void 那样)。 即使函数确实有值,也不必明确地声明它。该函数只需要使用 return 运算符后跟要返回的值即可。 另一个重要概念是,与在 Java 中一样,函数在执行过 return 语句后立即停止代码。因此,return 语句后的代码都不会被执行。 如果函数无返回值,那么可以调用没有参数的 return 运算符,随时退出函数。 例如:function sayHi(sMessage) { if (sMessage == &bye&) { } alert(sMessage); }这段代码中,如果 sMessage 等于 &bye&,就永远不显示警告框。 注释:如果函数无明确的返回值,或调用了没有参数的 return 语句,那么它真正返回的值是 undefined2.10.1无重载函数无重载,可以在同一作用域定义两个相同的名字,但真正使用的是最后一个 function add(ss){alert('ab'+ss);} function add(){alert('a');} a('b');//a,调用最后一个2.10.2arguments在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。 例如,在函数 sayHi() 中,第一个参数是 message。用 arguments[0] 也可以访问这个值,即第一个参数的值(第一个参数位于位置 0,第二个参数位于 位置 1,依此类推)。 因此,无需明确命名参数,就可以重写函数: function sayHi() { if (arguments[0] == &bye&) { } alert(arguments[0]); }检测参数个数还可以用 arguments 对象检测函数的参数个数,引用属性 arguments.length 即可。 下面的代码将输出每次调用函数使用的参数个数: function howManyArgs() { alert(arguments.length); } howManyArgs(&string&, 45); howManyArgs(); howManyArgs(12); 上面这段代码将依次显示 &2&、&0& 和 &1&。 注释:与其他程序设计语言不同,ECMAScript 不会验证传递给函数的参数个数是否等于函数定义的参数个数。开发者定义的函数都可以接受任意个数的参数 (根据 Netscape 的文档,最多可接受 25 个),而不会引发任何错误。任何遗漏的参数都会以 undefined 传递给函数,多余的函数将忽略。模拟函数重载用 arguments 对象判断传递给函数的参数个数,即可模拟函数重载: function doAdd() { if(arguments.length == 1) { alert(arguments[0] + 5); } else if(arguments.length == 2) { alert(arguments[0] + arguments[1]); } } doAdd(10); //输出 &15& doAdd(40, 20); //输出 &60&当只有一个参数时,doAdd() 函数给参数加 5。如果有两个参数,则会把两个参数相加,返回它们的和。所以,doAdd(10) 输出的是 &15&,而 doAdd(40, 20) 输出的是 &60&。 虽然不如重载那么好,不过已足以避开 ECMAScript 的这种限制2.10.3Function 类ECMAScript 最令人感兴趣的可能莫过于函数实际上是功能完整的对象。 Function 类可以表示开发者定义的任何函数。 用 Function 类直接创建函数的语法如下: var function_name = new function( arg1, arg2, ..., argN, function_body) 在上面的形式中,每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串。 记得下面这个函数吗? function sayHi(sName, sMessage) { alert(&Hello & + sName + sMessage); } 还可以这样定义它: var sayHi = new Function(&sName&, &sMessage&, &alert(\&Hello \& + sName + sMessage);&); 虽然由于字符串的关系,这种形式写起来有些困难,但有助于理解函数只不过是一种引用类型,它们的行为与用 Function 类明确创建的函数行为是相同的。 请看下面这个例子: function doAdd(iNum) { alert(iNum + 20); } function doAdd(iNum) { alert(iNum + 10); } doAdd(10); //输出 &20&如你所知,第二个函数重载了第一个函数,使 doAdd(10) 输出了 &20&,而不是 &30&。 如果以下面的形式重写该代码块,这个概念就清楚了: var doAdd = new Function(&iNum&, &alert(iNum + 20)&); var doAdd = new Function(&iNum&, &alert(iNum + 10)&); doAdd(10); 请观察这段代码,很显然,doAdd 的值被改成了指向不同对象的指针。函数名只是指向函数对象的引用值,行为就像其他对象一样。甚至可以使两个变量指向 同一个函数: var doAdd = new Function(&iNum&, &alert(iNum + 10)&); var alsodoAdd = doA doAdd(10); alsodoAdd(10); //输出 &20& //输出 &20&在这里, 变量 doAdd 被定义为函数, 然后 alsodoAdd 被声明为指向同一个函数的指针。 用这两个变量都可以执行该函数的代码, 并输出相同的结果 - &20&。 因此,如果函数名只是指向函数的变量,那么可以把函数作为参数传递给另一个函数吗?回答是肯定的! function callAnotherFunc(fnFunction, vArgument) { fnFunction(vArgument); } var doAdd = new Function(&iNum&, &alert(iNum + 10)&); callAnotherFunc(doAdd, 10); //输出 &20&在上面的例子中,callAnotherFunc() 有两个参数 - 要调用的函数和传递给该函数的参数。这段代码把 doAdd() 传递给 callAnotherFunc() 函数,参数 是 10,输出 &20&。 注意:尽管可以使用 Function 构造函数创建函数,但最好不要使用它,因为用它定义函数比用传统方式要慢得多。不过,所有函数都应看作 Function 类的 实例。 Function 对象的 length 属性 如前所述,函数属于引用类型,所以它们也有属性和方法。 ECMAScript 定义的属性 length 声明了函数期望的参数个数。例如: function doAdd(iNum) { alert(iNum + 10); }function sayHi() { alert(&Hi&); } alert(doAdd.length); alert(sayHi.length); //输出 &1& //输出 &0&函数 doAdd() 定义了一个参数,因此它的 length 是 1;sayHi() 没有定义参数,所以 length 是 0。 记住,无论定义了几个参数,ECMAScript 可以接受任意多个参数(最多 25 个),这一点在《函数概述》这一章中讲解过。属性 length 只是为查看默认情 况下预期的参数个数提供了一种简便方式。 Function 对象的方法 Function 对象也有与所有对象共享的 valueOf() 方法和 toString() 方法。这两个方法返回的都是函数的源代码,在调试时尤其有用。例如: function doAdd(iNum) { alert(iNum + 10); } document.write(doAdd.toString()); 上面这段代码输出了 doAdd() 函数的文本2.10.4闭包ECMAScript 最易让人误解的一点是,它支持闭包(closure)。 闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。 简单的闭包实例--全局变量 在 ECMAScript 中使用全局变量是一个简单的闭包实例。请思考下面这段代码: var sMessage = &hello world&; function sayHelloWorld() { alert(sMessage); } sayHelloWorld(); 在上面这段代码中,脚本被载入内存后,并没有为函数 sayHelloWorld() 计算变量 sMessage 的值。该函数捕获 sMessage 的值只是为了以后的使用,也 就是说, 解释程序知道在调用该函数时要检查 sMessage 的值。 sMessage 将在函数调用 sayHelloWorld() 时 (最后一行) 被赋值, 显示消息 &hello world&。 复杂的闭包实例 在一个函数中定义另一个会使闭包变得更加复杂。例如: var iBaseNum = 10; function addNum(iNum1, iNum2) { function doAdd() { return iNum1 + iNum2 + iBaseN } return doAdd(); } 这里, 函数 addNum() 包括函数 doAdd() (闭包) 。 内部函数是一个闭包, 因为它将获取外部函数的参数 iNum1 和 iNum2 以及全局变量 iBaseNum 的 值。 addNum() 的最后一步调用了 doAdd(),把两个参数和全局变量相加,并返回它们的和。 这里要掌握的重要概念是,doAdd() 函数根本不接受参数,它使用的值是从执行环境中获取的。 可以看到,闭包是 ECMAScript 中非常强大多用的一部分,可用于执行复杂的计算。 提示:就像使用任何高级函数一样,使用闭包要小心,因为它们可能会变得非常复杂 闭包(closure)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 下面就是我的学习笔记,对于 Javascript 初学者应该是很有用的。 一、变量的作用域 要理解闭包,首先必须理解 Javascript 特殊的变量作用域。 变量的作用域无非就是两种:全局变量和局部变量。 Javascript 语言的特殊之处,就在于函数内部可以直接读取全局变量。 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量。 function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数内部声明变量的时候,一定要使用 var 命令。如果不用的话,你实际上声明了一个全局变量! function f1(){ n=999; } f1(); alert(n); // 999 二、如何从外部读取局部变量? 出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。 那就是在函数的内部,再定义一个函数。 function f1(){ var n=999; function f2(){ alert(n); // 999 } } 在上面的代码中,函数 f2 就被包括在函数 f1 内部,这时 f1 内部的所有局部变量,对 f2 都是可见的。但是反过来就不行,f2 内部的局部变量,对 f1 就是不可见的。 这就是 Javascript 语言特有的&链式作用域&结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见 的,反之则不成立。 既然 f2 可以读取 f1 中的局部变量,那么只要把 f2 作为返回值,我们不就可以在 f1 外部读取它的内部变量了吗! function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 三、闭包的概念 上一节代码中的 f2 函数,就是闭包。 各种专业文献上的&闭包&(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。 由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成&定义在一个函数内部的函数&。 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。 四、闭包的用途 闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。 怎么来理解这句话呢?请看下面的代码。 function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000 在这段代码中,result 实际上就是闭包 f2 函数。它一共运行了两次,第一次的值是 999,第二次的值是 1000。这证明了,函数 f1 中的局部变量 n 一直保存在内存 中,并没有在 f1 调用后被自动清除。 为什么会这样呢?原因就在于 f1 是 f2 的父函数,而 f2 被赋给了一个全局变量,这导致 f2 始终在内存中,而 f2 的存在依赖于 f1,因此 f1 也始终在内存中,不会在 调用结束后,被垃圾回收机制(garbage collection)回收。 这段代码中另一个值得注意的地方, 就是&nAdd=function(){n+=1}&这一行, 首先在 nAdd 前面没有使用 var 关键字, 因此 nAdd 是一个全局变量, 而不是局部变量。 其次,nAdd 的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以 nAdd 相当于是一个 setter,可以在函数外部对函数内部的局 部变量进行操作。 五、使用闭包的注意点 1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决方法是, 在退出函数之前,将不使用的局部变量全部删除。 2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量 当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。第三章 对象基础 3.1 面向对象术语对象 ECMA-262 把对象(object)定义为“属性的无序集合,每个属性存放一个原始值、对象或函数”。严格来说,这意味着对象是无特定顺序的值的数组。 尽管 ECMAScript 如此定义对象,但它更通用的定义是基于代码的名词(人、地点或事物)的表示。 类 每个对象都由类定义,可以把类看做对象的配方。类不仅要定义对象的接口(interface)(开发者访问的属性和方法),还要定义对象的内部工作(使属性和 方法发挥作用的代码)。编译器和解释程序都根据类的说明构建对象。 实例 程序使用类创建对象时,生成的对象叫作类的实例(instance)。对类生成的对象的个数的唯一限制来自于运行代码的机器的物理内存。每个实例的行为相同, 但实例处理一组独立的数据。由类创建对象实例的过程叫做实例化(instantiation)。 在前面的章节我们提到过,ECMAScript 并没有正式的类。相反,ECMA-262 把对象定义描述为对象的配方。这是 ECMAScript 逻辑上的一种折中方案, 因为对象定义实际上是对象自身。即使类并不真正存在,我们也把对象定义叫做类,因为大多数开发者对此术语更熟悉,而且从功能上说,两者是等价的。 面向对象语言的要求 一种面向对象语言需要向开发者提供四种基本能力: 1. 2. 3. 4. 封装 - 把相关的信息(无论数据或方法)存储在对象中的能力 聚集 - 把一个对象存储在另一个对象内的能力 继承 - 由另一个类(或多个类)得来类的属性和方法的能力 多态 - 编写能以多种方法运行的函数或方法的能力ECMAScript 支持这些要求,因此可被是看做面向对象的。 对象的构成 在 ECMAScript 中,对象由特性(attribute)构成,特性可以是原始值,也可以是引用值。如果特性存放的是函数,它将被看作对象的方法(method), 否则该特性被看作对象的属性(property)。3.2 对象应用 对象的创建和销毁都在 JavaScript 执行过程中发生,理解这种范式的含义对理解整个语言至关重要。 声明和实例化 对象的创建方式是用关键字 new 后面跟上实例化的类的名字:var oObject = new Object(); var oStringObject = new String();第一行代码创建了 Object 类的一个实例, 并把它存储到变量 oObject 中。 第二行代码创建了 String 类的一个实例, 把它存储在变量 oStringObject 中。 如果构造函数无参数,括号则不是必需的。因此可以采用下面的形式重写上面的两行代码:var oObject = new O var oStringObject = new S对象引用 在前面的章节中,我们介绍了引用类型的概念。在 ECMAScript 中,不能访问对象的物理表示,只能访问对象的引用。每次创建对象,存储在变量中的都是 该对象的引用,而不是对象本身。 对象废除 ECMAScript 拥有无用存储单元收集程序(garbage collection routine),意味着不必专门销毁对象来释放内存。当再没有对对象的引用时,称该对象被废 除(dereference)了。运行无用存储单元收集程序时,所有废除的对象都被销毁。每当函数执行完它的代码,无用存储单元收集程序都会运行,释放所有的 局部变量,还有在一些其他不可预知的情况下,无用存储单元收集程序也会运行。 把对象的所有引用都设置为 null,可以强制性地废除对象。例如:var oObject = new O // do something with the object here oObject =当变量 oObject 设置为 null 后,对第一个创建的对象的引用就不存在了。这意味着下次运行无用存储单元收集程序时,该对象将被销毁。 每用完一个对象后,就将其废除,来释放内存,这是个好习惯。这样还确保不再使用已经不能访问的对象,从而防止程序设计错误的出现。此外,旧的浏览器 (如 IE/MAC) 没有全面的无用存储单元收集程序, 所以在卸载页面时, 对象可能不能被正确销毁。 废除对象和它的所有特性是确保内存使用正确的最好方法。 注意:废除对象的所有引用时要当心。如果一个对象有两个或更多引用,则要正确废除该对象,必须将其所有引用都设置为 null。 早绑定和晚绑定 所谓绑定(binding),即把对象的接口与对象实例结合在一起的方法。 早绑定(early binding)是指在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。在 Java 和 Visual Basic 这样的语 言中,有了早绑定,就可以在开发环境中使用 IntelliSense(即给开发者提供对象中属性和方法列表的功能)。ECMAScript 不是强类型语言,所以不支持早 绑定。 另一方面,晚绑定(late binding)指的是编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需检查对象是否支持属性 和方法即可。ECMAScript 中的所有变量都采用晚绑定方法。这样就允许执行大量的对象操作,而无任何惩罚。3.3 对象的类型http://www.jb51.net/w3school/js/pro_js_object_types.htm 在 ECMAScript 中,所有对象并非同等创建的。 一般来说,可以创建并使用的对象有三种:本地对象、内置对象和宿主对象。3.3.1本地对象ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。简单来说,本地对象就是 ECMA-262 定义的类(引 用类型)。它们包括:? ? ? ? ? ? ? ? ?Object Function Array String Boolean Number Date RegExp Error ? ? ? ? ? ? ? ? ? ? ? ? ? ?EvalError RangeError ReferenceError SyntaxError TypeError URIError JavaScript 高级教程:ECMAScript 引用类型 JavaScript 高级教程:ECMAScript Function 类 JavaScript 参考手册:Array 对象 JavaScript 参考手册:Boolean 对象 JavaScript 参考手册:Date 对象 JavaScript 参考手册:Number 对象 JavaScript 参考手册:String 对象 JavaScript 参考手册:RegExp 对象Array 对象Array 对象用于在单个的变量中存储多个值。 创建 Array 对象的语法:new Array(); new Array(size); new Array(element0, element1, ..., elementn);参数 参数 size 是期望的数组元素个数。返回的数组,length 字段将被设为 size 的值。 参数 element ..., elementn 是参数列表。当使用这些参数来调用构造函数 Array() 时,新创建的数组的元素就会被初始化为这些值。它的 length 字段也 会被设置为参数的个数。 返回值 返回新创建并被初始化了的数组。 如果调用构造函数 Array() 时没有使用参数,那么返回的数组为空,length 字段为 0。 当调用构造函数时只传递给它一个数字参数,该构造函数将返回具有指定个数、元素为 undefined 的数组。 当其他参数调用 Array() 时,该构造函数将用参数指定的值初始化数组。 当把构造函数作为函数调用,不使用 new 运算符时,它的行为与使用 new 运算符调用它时的行为完全一样。Array 对象属性FF: Firefox, IE: Internet Explorer 属性 constructor index input length prototype 设置或返回数组中元素的数目。length 与 String 一样,是最后位置加 1(下标从 0 开始) 使您有能力向对象添加属性和方法。 描述 返回对创建此对象的数组函数的引用。 FF 1 1 1 1 1 IE 4 4 4 4 4ar[3] = &purple&;//下一个未使用的位置是 3,赋值为 purple,增加一项数组的大小变为 4,但如果把值放 在数组 25 的位置呢?ECMAScript 将 3 到 24 的位置都置为 null, 并把数组大小增加到 26。 数组最多可存放 42 亿位置。还可以用字面量定义数组:var ar = [&red&,&green&,&blue&]; 未明确使用 Array 类,方括号暗示把其中的值放在 Array 对象中。 Array 对象覆盖了 to_String 和 valueOf 方法返回特殊的字符串,通过调用每项的 to_String 方法在用逗号 分隔。//red,green,blue Array 对象方法FF: Firefox, IE: Internet Explorer 方法 concat() join() pop() push() reverse() shift() slice() sort() splice() toSource() toString() toLocaleString() unshift() valueOf() 描述 连接两个或更多的数组,并返回结果。 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。 删除并返回数组的最后一个元素 向数组的末尾添加一个或更多元素,并返回新的长度。 颠倒数组中元素的顺序。 删除并返回数组的第一个元素 从某个已有的数组返回选定的元素 对数组的元素进行排序 删除元素,并向数组添加新元素。 返回该对象的源代码。 把数组转换为字符串,并返回结果。 把数组转换为本地数组,并返回结果。 向数组的开头添加一个或更多元素,并返回新的长度。 返回数组对象的原始值 FF 1 1 1 1 1 1 1 1 1 1 1 1 1 1 IE 4 4 5.5 5.5 4 5.5 4 4 5.5 4 4 6 41.join() 方法用于把数组中的所有元素放入一个字符串。 元素是通过指定的分隔符进行分隔的。 语法 arrayObject.join(separator) 参数 separator 返回值 返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 separator 字符串而生成 的。 例子 1 在本例中,我们将创建一个数组,然后把它的所有元素放入一个字符串: &script type=&text/javascript&& var arr = new Array(3) arr[0] = &George& arr[1] = &John& arr[2] = &Thomas& document.write(arr.join()) &/script& 输出: George,John,Thomas 例子 2 描述 可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。 在本例中,我们将使用分隔符来分隔数组中的元素: &script type=&text/javascript&& var arr = new Array(3) arr[0] = &George& arr[1] = &John& arr[2] = &Thomas& document.write(arr.join(&.&)) &/script& 输出: George.John.Thomas split:把字符串转换为数组。 var s = &red,green,blue&; var ar = s.split(&,&);//若不输入参数,分解成字符数组。 var s = &red&; var ar = s.split();//r,e,d2.concat 和 slice:跟字符串处理一样返回子数组。 3.尾行为 push 和 pop:只在尾部添加和删除。栈行为(后进先出) ar.push(&a&,&b&,&c&);//可传递一个或多个参数。 4.头行为 shift()和 unshift():在顶点 0 位置移除,顶点添加其他后退 var ar = [&red&,&green&,&blue&]; ar.shift();//green,blue。放参数也不起作用。就移除一个顶点。 var ar = [&red&,&green&,&blue&]; ar.unshift(&a&,&b&);//a,b,red,green,blue (先进先出队列行为可用 push()和 shift()) 。中部行为:splice,把数据插入中部,根据参数不同行为不同。 删除:2 个参数-开始位置和删除个数,ar.splice(0,2)//删除前两个 添加:3 个参数-开始位置、0(要删除数组的个数) 、插入项。 ar.splice(2,0,&1&,&2&);//a,b,1,2,c,d。在 2 处插入 a,b 替换删除:3 个参数-开始位置、删除个数、插入项。 ar.splice(2,1,&1&,&2&);//a,b,1,2,d 删除数与插入数不必等 与排序有关 reverse 和 sort:数组颠倒和升序排序。 注 sort 把数组中元素转换成字符串后再安装 ASCII 码进行排序。 故此数字数组不会安装大小顺序。 如[2,3,23,4]=&[2,23,3,4]解决方式自定义排序函数。 先看下基本比较函数 var ar = [&red&,&green&,&blue&]; ar.sort(comp); alert(ar);//blue,green,red function comp(val1,val2){//必须 2 个参数 1 个返回值(-1,1,0) if(val1 & val2){return -1;} else if(val1 & val2){return 1;} else {return 0;} }//与 String 类的 localCompare 方法原理一样。 转换成数字: function comp(val1,val2){ if(parseInt(val1) & parseInt(val2)){return -1;} else if(parseInt(val1) & parseInt(val2)){return 1;} else {return 0;} }Date 类:与 java 一样把日期存储为距离 UTC(Universal Time Code 通用时间代码也叫格林尼治)时间 1970 年 1 月 1 日凌晨 12 点的毫秒数。毫秒数 可以避免千年虫,可以精确表示出 1970 年以后的 285616 年的日期。 创建方式: var d = new Date();//创建当前日期时间 有两种创建方式: 第一种只声明距离 UTC 毫秒数 var d = new Date(20000); parse()和 UTC() parse()接收字符串为参数,将其转换为距离 UTC 的毫秒数传给 Date。通常是地点特定的。在美国支持下面日期格式: mm/dd/yyyy(6/13/2012) mmmm dd,yyyy(Nov 12,2012)// new Date(Date.parse(&May 25,2012&));// 如果 parse 中参数不能转换为日期返回 NaN new Date(Date.parse(&May 95,2012&));// UTC()返回的也是毫秒数,参数为年、月、日、小时、分、秒、毫秒。必须声明年月其他可选。设置月份格外注意值是从 0 到 11(0 代表一月,11 代表十二月) new Date(Date.UTC());//2012 年 1 月 2 日 第二种是直接声明 UTC()方法接收参数。年月必须其他可选 new Date();// new Date();// Date 类覆盖了 valueOf 和 toString:返回毫秒数和实现特定字符串。toString 在不同的浏览器或不同的国家显示方式不一样。3.3.2内置对象ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这 意味着开发者不必明确实例化内置对象,它已被实例化了。ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个 内置对象都是本地对象)。 相关页面 JavaScript 参考手册:Global 对象 JavaScript 参考手册:Math 对象Global 对象:特别的对象因其根本不存在,var p = G//提示 Global 不存在全局属性和函数可用于所有内建的 JavaScript 对象。 顶层函数(全局函数) FF: Firefox, IE: Internet Explorer 函数 decodeURI() decodeURIComponent() encodeURI() encodeURIComponent() escape() 描述 解码某个编码的 URI。 解码一个编码的 URI 组件。 把字符串编码为 URI。 把字符串编码为 URI 组件。 对字符串进行编码。 FF 1 1 1 1 1 IE 5.5 5.5 5.5 5.5 3 eval() getClass() isFinite() isNaN() Number() parseFloat() parseInt() String() unescape() 顶层属性(全局属性) FF: Firefox, IE: Internet Explorer 方法 Infinity java NaN Packages undefined 全局对象描述 描述计算 JavaScript 字符串,并把它作为脚本代码来执行。 返回一个 JavaObject 的 JavaClass。 检查某个值是否为有穷大的数。 检查某个值是否是数字。 把对象的值转换为数字。 解析一个字符串并返回一个浮点数。 解析一个字符串并返回一个整数。 把对象的值转换为字符串。 对由 escape() 编码的字符串进行解码。131 1 1 1 1 1 14 33 33FF 代表正的无穷大的数值。 代表 java.* 包层级的一个 JavaPackage。 指示某个值是不是数字值。 根 JavaPackage 对象。 指示未定义的值。 1 1 1IE 445.5全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。全局 对象不是任何对象的属性,所以它没有名称。 在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。但通常不必用这种方式引用全局对象,因为全局对象是作用域链的头,这意味着所有非限定 性的变量和函数名都会作为该对象的属性来查询。例如,当 JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是 作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。 全局对象只是一个对象,而不是类。既没有构造函数,也无法实例化一个新的全局对象。 在 JavaScript 代码嵌入一个特殊环境中时,全局对象通常具有环境特定的属性。实际上,ECMAScript 标准没有规定全局对象的类型,JavaScript 的实现 或嵌入的 JavaScript 都可以把任意类型的对象作为全局对象,只要该对象定义了这里列出的基本属性和函数。例如,在允许通过 LiveConnect 或相关的技 术来脚本化 Java 的 JavaScript 实现中,全局对象被赋予了这里列出的 java 和 Package 属性以及 getClass() 方法。而在客户端 JavaScript 中,全 局对象就是 Window 对象,表示允许 JavaScript 代码的 Web 浏览器窗口。 例子 在 JavaScript 核心语言中,全局对象的预定义属性都是不可枚举的,所有可以用 for/in 循环列出所有隐式或显式声明的全局变量,如下所示:var variables = &&; for (var name in this) { variables += name + &&br /&&; } document.write(variables);TIYMath 对象Math 对象用于执行数学任务。 使用 Math 的属性和方法的语法:var pi_value=Math.PI; var sqrt_value=Math.sqrt(15);注释:Math 对象并不像 Date 和 String 那样是对象的类,因此没有构造函数 Math(),像 Math.sin() 这样的函数只是函数,不是某个对象的方法。您无 需创建它,通过把 Math 作为对象使用就可以调用其所有属性和方法。 Math 对象属性 FF: Firefox, IE: Internet Explorer 属性 E LN2 LN10 LOG2E LOG10E PI SQRT1_2 SQRT2 Math 对象方法 FF: Firefox, IE: Internet Explorer 方法 abs(x) acos(x) asin(x) atan(x) atan2(y,x) ceil(x) cos(x) exp(x) floor(x) log(x) max(x,y) min(x,y) pow(x,y) random() round(x) sin(x) sqrt(x) 描述 返回数的绝对值。 返回数的反余弦值。 返回数的反正弦值。 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间) 。 对数进行上舍入。 返回数的余弦。 返回 e 的指数。 对数进行下舍入。 返回数的自然对数(底为 e) 。 返回 x 和 y 中的最高值。 返回 x 和 y 中的最低值。 返回 x 的 y 次幂。 返回 0 ~ 1 之间的随机数。 把数四舍五入为最接近的整数。 返回数的正弦。 返回数的平方根。 FF 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 IE 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 描述 返回算术常量 e,即自然对数的底数(约等于 2.718) 。 返回 2 的自然对数(约等于 0.693) 。 返回 10 的自然对数(约等于 2.302) 。 返回以 2 为底的 e 的对数(约等于 1.414) 。 返回以 10 为底的 e 的对数(约等于 0.434) 。 返回圆周率(约等于 3.14159) 。 返回返回 2 的平方根的倒数(约等于 0.707) 。 返回 2 的平方根(约等于 1.414) 。 FF 1 1 1 1 1 1 1 1 IE 3 3 3 3 3 3 3 3 tan(x) toSource() valueOf()返回角的正切。 返回该对象的源代码。 返回 Math 对象的原始值。1 1 13 4min 和 max 用于判断一组数中的最大和最小。可以接受多个参数。 var imax = Math.max(1,2,5,7,23,2);//imax = 23 Math.abs()求绝对值。 把小数舍入为整数: ceil():向上取舍。ceil(25.5)//26 floor:向下取舍。ceil(25.5)//25 round:标准舍入函数,四舍五入。ceil(25.5)//26 注意:方法间不能交换使用。 sqrt:求平方根。 random:求随机数。返回 0 到 1 的随机数,不包括 0 和 1. number = Math.floor(Math.random()*total+first);// 几个数就×几,从几开始就+几。 如果想 1 到 10 随机数:Math.floor(Math.random()*10+1) 如果 2 到 9:Math.floor(Math.random()*8+2) function select(first,last){//总数 = 最大-最小 return Math.floor(Math.random()*(last-first)+first); }3.3.3宿主对象所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。 所有 BOM 和 DOM 对象都是宿主对象。 相关页面 JavaScript 高级教程:JavaScript 实现 W3School 参考手册:JavaScript 参考手册 W3School 教程:HTML DOM 教程3.4 作用域作用域指的是变量的适用范围。 公用、私有和受保护作用域 概念 在传统的面向对象程序设计中,主要关注于公用和私有作用域。公用作用域中的对象属性可以从对象外部访问,即开发者创建对象的实例后,就可使用它的公用 属性。而私有作用域中的属性只能在对象内部访问,即对于外部世界来说,这些属性并不存在。这意味着如果类定义了私有属性和方法,则它的子类也不能访问 这些属性和方法。 受保护作用域也是用于定义私有的属性和方法,只是这些属性和方法还能被其子类访问。 ECMAScript 只有公用作用域 对 ECMAScript 讨论上面这些作用域几乎毫无意义,因为 ECMAScript 中只存在一种作用域 - 公用作用域。ECMAScript 中的所有对象的所有属性和方法 都是公用的。因此,定义自己的类和对象时,必须格外小心。记住,所有属性和方法默认都是公用的! 建议性的解决方法 许多开发者都在网上提出了有效的属性作用域模式,解决了 ECMAScript 的这种问题。 由于缺少私有作用域,开发者确定了一个规约,说明哪些属性和方法应该被看做私有的。这种规约规定在属性前后加下划线: obj._color_ = &blue&; 这段代码中,属性 color 是私有的。注意,下划线并不改变属性是公用属性的事实,它只是告诉其他开发者,应该把该属性看作私有的。 有些开发者还喜欢用单下划线说明私有成员,例如:obj._color。 静态作用域 静态作用域定义的属性和方法任何时候都能从同一位置访问。在 Java 中,类可具有属性和方法,无需实例化该类的对象,即可访问这些属性和方法,例如 java.net.URLEncoder 类,它的函数 encode() 就是静态方法。 ECMAScript 没有静态作用域 严格来说,ECMAScript 并没有静态作用域。不过,它可以给构造函数提供属性和方法。还记得吗,构造函数只是函数。函数是对象,对象可以有属性和方法。 例如: function sayHello() { alert(&hello&); }sayHello.alternate = function() { alert(&hi&); }sayHello(); sayHello.alternate(); TIY//输出 &hello& //输出 &hi&这里,方法 alternate() 实际上是函数 sayHello 的方法。可以像调用常规函数一样调用 sayHello() 输出 &hello&,也可以调用 sayHello.alternate() 输 出 &hi&。即使如此,alternate() 也是 sayHello() 公用作用域中的方法,而不是静态方法。 关键字 this this 的功能 在 ECMAScript 中,要掌握的最重要的概念之一是关键字 this 的用法,它用在对象的方法中。关键字 this 总是指向调用该方法的对象,例如: var oCar = new O oCar.color = &red&; oCar.showColor = function() { alert(this.color); };oCar.showColor(); TIY//输出 &red&在上面的代码中,关键字 this 用在对象的 showColor() 方法中。在此环境中,this 等于 oCar。下面的代码与上面的代码的功能相同: var oCar = new O oCar.color = &red&; oCar.showColor = function() { alert(oCar.color); };oCar.showColor(); TIY 使用 this 的原因//输出 &red&为什么使用 this 呢?因为在实例化对象时, 总是不能确定开发者会使用什么样的变量名。 使用 this, 即可在任何多个地方重用同一个函数。 请思考下面的例子: function showColor() { alert(this.color); };var oCar1 = new O oCar1.color = &red&; oCar1.showColor = showCvar oCar2 = new O oCar2.color = &blue&; oCar2.showColor = showC oCar1.showColor(); oCar2.showColor(); TIY 在上面的代码中, 首先用 this 定义函数 showColor(), 然后创建两个对象 (oCar1 和 oCar2) , 一个对象的 color 属性被设置为 &red&, 另一个对象的 color 属性被设置为 &blue&。两个对象都被赋予了属性 showColor,指向原始的 showColor () 函数(注意这里不存在命名问题,因为一个是全局函数,而另一个 是对象的属性) 。 调用每个对象的 showColor(), oCar1 输出是 &red&, 而 oCar2 的输出是 &blue&。 这是因为调用 oCar1.showColor() 时, 函数中的 this 关键字等于 oCar1。调用 oCar2.showColor() 时,函数中的 this 关键字等于 oCar2。 注意,引用对象的属性时,必须使用 this 关键字。例如,如果采用下面的代码,showColor() 方法不能运行: function showColor() { alert(color); }; 如果不用对象或 this 关键字引用变量,ECMAScript 就会把它看作局部变量或全局变量。然后该函数将查找名为 color 的局部或全局变量,但是不会找到。 结果如何呢?该函数将在警告中显示 &null&。 //输出 &red& //输出 &blue&3.5 定义类或对象使用预定义对象只是面向对象语言的能力的一部分,它真正强大之处在于能够创建自己专用的类和对象。 ECMAScript 拥有很多创建对象或类的方法。 工厂方式 原始的方式 因为对象的属性可以在对象创建后动态定义,所有许多开发者都在 JavaScript 最初引入时编写类似下面的代码:var oCar = new OoCar.color = &blue&;oCar.doors = 4; oCar.mpg = 25; oCar.showColor = function() { alert(this.color); };TIY 在上面的代码中,创建对象 car。然后给它设置几个属性:它的颜色是蓝色,有四个门,每加仑油可以跑 25 英里。最后一个属性实际上是指向函数的指针, 意味着该属性是个方法。执行这段代码后,就可以使用对象 car。 不过这里有一个问题,就是可能需要创建多个 car 的实例。 解决方案:工厂方式 要解决该问题,开发者创造了能创建并返回特定类型的对象的工厂函数(factory function)。 例如,函数 createCar() 可用于封装前面列出的创建 car 对象的操作:function createCar() { var oTempCar = new O oTempCar.color = &blue&; oTempCar.doors = 4; oTempCar.mpg = 25; oTempCar.showColor = function() { alert(this.color); }; return oTempC } var oCar1 = createCar(); var oCar2 = createCar();TIY 在这里,第一个例子中的所有代码都包含在 createCar() 函数中。此外,还有一行额外的代码,返回 car 对象(oTempCar)作为函数值。调用此函数,将 创建新对象, 并赋予它所有必要的属性, 复制出一个我们在前面说明过的 car 对象。 因此, 通过这种方法, 我们可以很容易地创建 car 对象的两个版本 (oCar1 和 oCar2),它们的属性完全一样。 为函数传递参数 我们还可以修改 createCar() 函数,给它传递各个属性的默认值,而不是简单地赋予属性默认值:function createCar(sColor,iDoors,iMpg) { var oTempCar = new O oTempCar.color = sC oTempCar.doors = iD oTempCar.mpg = iM oTempCar.showColor = function() { alert(this.color); }; return oTempC } var oCar1 = createCar(&red&,4,23); var oCar2 = createCar(&blue&,3,25); oCar1.showColor(); oCar2.showColor();TIY 给 createCar() 函数加上参数,即可为要创建的 car 对象的 color、doors 和 mpg 属性赋值。这使两个对象具有相同的属性,却有不同的属性值。 在工厂函数外定义对象的方法 虽然 ECMAScript 越来越正式化, 但创建对象的方法却被置之不理, 且其规范化至今还遭人反对。 一部分是语义上的原因 (它看起来不像使用带有构造函数 new 运算符那么正规),一部分是功能上的原因。功能原因在于用这种方式必须创建对象的方法。前面的例子中,每次调用函数 createCar(),都要创建新函数 showColor(),意味着每个对象都有自己的 showColor() 版本。而事实上,每个对象都共享同一个函数。 有些开发者在工厂函数外定义对象的方法,然后通过属性指向该方法,从而避免这个问题://输出 &red& //输出 &blue&function showColor() { alert(this.color); } function createCar(sColor,iDoors,iMpg) { var oTempCar = new O oTempCar.color = sC oTempCar.doors = iD oTempCar.mpg = iM oTempCar.showColor = showC return oTempC } var oCar1 = createCar(&red&,4,23); var oCar2 = createCar(&blue&,3,25); oCar1.showColor(); oCar2.showColor();TIY//输出 &red& //输出 &blue& 在上面这段重写的代码中,在函数 createCar() 之前定义了函数 showColor()。在 createCar() 内部,赋予对象一个指向已经存在的 showColor() 函数的 指针。从功能上讲,这样解决了重复创建函数对象的问题;但是从语义上讲,该函数不太像是对象的方法。 所有这些问题都引发了开发者定义的构造函数的出现。 构造函数方式 创建构造函数就像创建工厂函数一样容易。 第一步选择类名, 即构造函数的名字。 根据惯例, 这个名字的首字母大写, 以使它与首字母通常是小写的变量名分开。 除了这点不同,构造函数看起来很像工厂函数。请考虑下面的例子:function Car(sColor,iDoors,iMpg) { this.color = sC this.doors = iD this.mpg = iM this.showColor = function() { alert(this.color); }; } var oCar1 = new Car(&red&,4,23); var oCar2 = new Car(&blue&,3,25);TIY 下面为您解释上面的代码与工厂方式的差别。首先在构造函数内没有创建对象,而是使用 this 关键字。使用 new 运算符构造函数时,在执行第一行代码前先 创建一个对象,只有用 this 才能访问该对象。然后可以直接赋予 this 属性,默认情况下是构造函数的返回值(不必明确使用 return 运算符)。 现在,用 new 运算符和类名 Car 创建对象,就更像 ECMAScript 中一般对象的创建方式了。 你也许会问,这种方式在管理函数方面是否存在于前一种方式相同的问题呢?是的。 就像工厂函数,构造函数会重复生成函数,为每个对象都创建独立的函数版本。不过,与工厂函数相似,也可以用外部函数重写构造函数,同样地,这么做语义 上无任何意义。这正是下面要讲的原型方式的优势所在。 原型方式 该方式利用了对象的 prototype 属性,可以把它看成创建新对象所依赖的原型。 这里,首先用空构造函数来设置类名。然后所有的属性和方法都被直接赋予 prototype 属性。我们重写了前面的例子,代码如下:function Car() { } Car.prototype.color = &blue&; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.showColor = function() { alert(this.color); }; var oCar1 = new Car(); var oCar2 = new Car();TIY 在这段代码中, 首先定义构造函数 (Car) , 其中无任何代码。 接下来的几行代码, 通过给 Car 的 prototype 属性添加属性去定义 Car 对象的属性。 调用 new Car() 时,原型的所有属性都被立即赋予要创建的对象,意味着所有 Car 实例存放的都是指向 showColor() 函数的指针。从语义上讲,所有属性看起来都属 于一个对象,因此解决了前面两种方式存在的问题。 此外,使用这种方式,还能用 instanceof 运算符检查给定变量指向的对象的类型。因此,下面的代码将输出 TRUE:alert(oCar1 instanceof Car);原型方式的问题//输出 &true&原型方式看起来是个不错的解决方案。遗憾的是,它并不尽如人意。 首先, 这个构造函数没有参数。 使用原型方式, 不能通过给构造函数传递参数来初始化属性的值, 因为 Car1 和 Car2 的 color 属性都等于 &blue&, doors 属 性都等于 4,mpg 属性都等于 25。这意味着必须在对象创建后才能改变属性的默认值,这点很令人讨厌,但还没完。真正的问题出现在属性指向的是对象, 而不是函数时。函数共享不会造成问题,但对象却很少被多个实例共享。请思考下面的例子:function Car() { } Car.prototype.color = &blue&; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.drivers = new Array(&Mike&,&John&); Car.prototype.showColor = function() { alert(this.color); }; var oCar1 = new Car(); var oCar2 = new Car(); oCar1.drivers.push(&Bill&); alert(oCar1.drivers); //输出 &Mike,John,Bill& alert(oCar2.drivers); //输出 &Mike,John,Bill&TIY 上面的代码中,属性 drivers 是指向 Array 对象的指针,该数组中包含两个名字 &Mike& 和 &John&。由于 drivers 是引用值,Car 的两个实例都指向同一 个数组。 这意味着给 oCar1.drivers 添加值 &Bill&, 在 oCar2.drivers 中也能看到。 输出这两个指针中的任何一个, 结果都是显示字符串 &Mike,John,Bill&。 由于创建对象时有这么多问题,你一定会想,是否有种合理的创建对象的方法呢?答案是有,需要联合使

我要回帖

更多关于 javascript选中select 的文章

 

随机推荐