1. javascript伪协议
javascript: document.write("use js in browser address bar directly"); void(0);
利用javascript 伪协议可以在浏览器地址栏中直接写javascript进行测试。默认情况下,javascript 伪协议会将程序执行结果在一个新的页面中显示,要阻止这种情况的话,程序需要返回undefined, 而 void(0)的作用就是返回undefined.
2. undefined
undefined 是js中的一个特殊值,它代表'无';因为js没有强制检验对象存在的机制,所以任何一个未经定义和使用的标识,都可以用"无"来表示。这个"无"在Javascript文法中就是undefined。这种情况在Java中是不存在的,所以Java中没有类似undefined的机制。
2.1 产生undefined变量的情形
1) 变量未声明,
2) 变量声明了但未赋值
3) 任何一个不带return指令的函数运算的默认返回值都是undefined。
4) 使用了return,但缺省了return的表达式,那么函数的返回值依然是undefined
5) 使用void, void(0);
6) 由于JS中函数的实参和形参的数量可以不相同,如果实参量少于形参数量,那么多出来的形参的值就是undefined;
2.2 undefined变量的使用
一个类型为undefined的变量,如果直接使用,通常会抛出一个系统级别的Error, 只有delete和typeof例外。
所以如果要判断一个变量是否是undefined的,不能使用
if(x) {}; //假定x是一个undefined类型的变量,这条语句会抛出异常
而要使用
if(typeof(x)=="undefined") {}
但是,如果是下面这种情况,
var x; //x定义了但是没有赋值,所以是undefined。
if(x) {}; //x是undefined, 但这条语句不会抛出异常。
但是,如果是一个类型为undefined的对象的属性,是可以直接使用的
例:
var a = new Object();
if (!a.x) { //虽然a.x是undefined的,但是这种情况下不会出错, !a.x返回true, 详见14条
alert(typeof(a.x)); //输出undefined
}
函数的形参也有同样的特性
function add(a, b){
if(!b) b=30; //如果在调用时,b没有被赋值,b就是undefined, 但这种情况下,b依然可以使用。
return a+b;
}
alert(add(3));
3. null
null 代表 "空",它和undefined不同,"空"依然是一种存在,而"无"则是存在的对立面。
x=null; typeof(x); //返回object, 注意null可以理解为将要引用对象,但它本身不是对象,不要把它看作特殊的对象。
4. typeof
根据上面对undefined和null的认识,可以得到如下结论, 注意返回undefined的两种情况,1. 变量未声明, 2.变量声明了但未赋值
typeof(x); //变量未声明, 返回"undefined"
var x; typeof(x); //变量声明了但未赋值,返回"undefined"
var x=null; typeof(x); // 返回 "object"
x=null; typeof(x); //返回"object", 注意null可以理解为将要引用对象,但它本身不是对象,不要把它看作特殊的对象。
Ø typeof返回值
typeof 对数值,字符串或者布尔值三种原始类型分别返回:"number", "string" 和 "boolean"。对对象,数组和null, 它返回的是"object"。对函数,类和闭包,它返回"function"。
由于typeof对所有对象和数组类型返回的都是"object",所以它只有在区别对象和原始类型时才有效;要区别一种对象和另一种对象,必须使用其他方法,例如
instanceof运算符和constructor 属性。
例:
if(typeof(object)=="type") || this.constructor == type || this instanceof type) return true;
5. constructor属性
constructor可以获得一个对象的构造函数,typeof(objectX.constructor)=="function"。
例:
function Point(x,y) {
this.x=x;
this.y=y;
this.f = function(){alert(typeof(this.constructor));
}
var p = new Point(1,2);
p.f(); //输出:"function"
alert(typeof(p.constructor));//输出:"function"
p.add = function (x, y) {
new this.constructor(this.x+x, this.y+y); //contructor可以直接进行函数调用
}
6. JS中的文件的依赖关系
js中的函数的定义需要放在使用的前面。如果函数的定义和使用被放置在了不同的文件中,需要把这些文件都包含在网页中,如下所示:
<script type="text/javascript" src="js/testjs.js"/></script>
<script type="text/javascript" src="js/test2js.js"/></script>
这种情况下,就需要使用者对各个文件的依赖关系要很清楚,在复杂的项目里面就比较吃力。
Dojo框架实现了包的机制,可以自动处理依赖关系,非常方便。
7. 客户端生命周期
客户端生命周期起始于浏览器开始装载某个请求的特定数据,结束于浏览器发起的一个新的请求(页面跳转或刷新),分为两个阶段。
初始化阶段:装载页面数据
运行阶段:消息等待,事件处理
一个比较好的习惯是把除声明以外的所有的脚本指令都放到运行阶段执行,避免初始化阶段DOM元素加载失败或者低级的次序问题而导致脚本失效。
Ø onload事件
元素的onload事件将在元素被完全加载后由浏览器发起。利用body元素的onload事件就可以实现将所有运行指令放在body元素及所有子元素被加载以后执行,避免了脚本的失效。
8. 推荐的的如何放置javascript代码的例子:
<html>
<head>
<script>
...
<!--
//这里放置函数和闭包
function add(x, y)
{
return x+y;
}
-->
</script>
</head>
<script>
<!--
//这里放置全局变量
var a = 100;
var b = 200;
-->
</script>
<body>
</body>
</html>
注:只要不在函数内部声明的变量似乎都是全局的,上面的例子中的全局变量声明即使放到<head>或<body>内部都是可以的。
9. with 关键字
var oMemery = {
numStack: [];
}
with(object) {
numStack[0]=1;
}
使用with的性能比不使用要差,所以不要在循环语句中使用它,尽量少用。
10. Array
1) 数组的创建,用new Array()或[];
2) 直接调用Array()函数和通过new 运算符将Array()作为构造函数调用的结果是一样的。例如:var a = Array(1, 2, 3)和 var a = new Array(1, 2, 3) 等价。
3) 由于JS是一种动态类型语言,因此数组元素不必具有相同的类型,这和Java是不同的。
例:
var a = new Array();
a[0] = 1.2;
a[1] = "JS";
a[2] = true;
a[3] = {x:1, y:2};
a[4] = new Array {1, 2, 3};
4) 用new Array()创建数组可以包含参数,如果参数个数大于1,那么这些参数为数组对应的元素进行初始化;如果参数的个数等于1并且为数字,该参数指定的是数组的长度。
例:
var a = new Array {1, 2, 3};
var b = new Array(10);
5) 数组可以用length属性取得长; 注意当数组被当作普通对象或哈希表来使用的时候(也就是说普通对象采用数组语法[]来访问的时候),数组的length属性是不起作用的
6) length属性可以读,也可以写,通过改变length的值,可以在数组的末尾添加或删除元素。例如,设置数组的length为0,可以清除数组中的所有元素。如果给length设置了一个比它当前值小的值,那么数组将被截断,这个长度之外的元素都会被抛弃,他们的值也就丢失了;如果给length设置的值比当前的值大,那么新的,未初始化的元素就会被添加到数组末尾。
7) delete操作虽然能够删除数组元素,但它并不会改变数组的length属性,因此如果要删除数组某个下标值之后的值,应当直接修改length的值。
8) 数组还提供了一些插入和删除数组元素的方法,这些方法会恰当的改变数组length的值,所以要尽量使用这些方法,尽少使用delete。
9) 数组常量中还可以直接存放未初始化的元素,只要在逗号之间省去该元素的值就可以了
var sparseArray = [1,,,,5];
10) 利用Array的apply方法可以很容易的将Argument和其他一些带下标的对象转换为数组:
function test () {
//将传给函数test()的参数列表转换为数组对象返回
return Array.apply(this, arguments);
}
Ø 数组常用操作
1) push() 和 pop() ,在数组的末尾插入或删除一个元素
var a = new Array();
a.push(1);
a.push(2, 3, 4); //数组现在是[1, 2, 3, 4]
var b = a.pop(); //数组现在是[1,2,4]
2)unshift 和 shift, 在数组的头部插入或删除元素。使用unshift可以将一个或多个元素插入到数组的头部,使用shift则依次从头部删除他们。
var a = new Array();
a.unshift(1);
a.unshift(2,3,4); //数组现在是[2, 3, 4, 1],注意顺序
alert(a.length); //输出:4
var b = a.shift(); //数组现在是[3,4,1]
alert(a.length); //输出:3
3) join
join() 方法可以把一个数组的所有元素都转换成字符串,然后再把他们连接起来。你可以指定一个可选的字符串来分隔结果字符串中的元素。如果没有指定分隔字符串,那么默认使用逗号分割元素。
例:
var a = [1,2,3];
var s = a.join(); //s的值是字符串”1,2,3”
var b = [“A”, “k”, “I”, “r”, “a”];
var name = b.join(“”); //name的值是Akira
4) reverse
reverse 方法颠倒数组元素的顺序并返回颠倒后的数据。它在原数组上执行这一操作,也就是说它并不是创建一个重新排列的新数组,而是在已经存在的数组中对元素进行重排。
例:
var a = new Array(1, 2, 3);
a.reverse();
var s = a.join(); //现在s是”3,2,1”, a是[3,2,1]
5) sort
对数组排序,它可以接受一个参数,这个参数是比较两个元素值得一个闭包,如果这个参数缺省,那么sort方法将按照默认的规则对元素进行排序。
例:
var arr = [3,2,1,6,7];
arr.sort();
6) concat
该方法创建并返回一个数组,这个数组包含了调用concat()的原始数组的元素,其后跟随的是concat的参数,如果其中有些参数是数组,那么它将被展开,其元素将被添加到返回的数组中,但是要注意,concat()并不能递归的展开一个元素为数组的数组。
例:
var arr = new Array();
var arr2 = arr.concat(1, 2,3, [1,2,3],[[4,5],6]);
alert(arr2.join(“@”)); //输出:1@2@3@1@2@3@4,5@6 ,注意子数组[4,5]没有被展开
concat只能展开数组,并不能展开Arguments这种类似数组带有下标的对象,如果想追加参数列表到数组末尾,可以采用两种方法:
法1:将arguments转换成数组后再追加
法2:使用concat方法的apply形式
function test() {
var a = [“these”, “ are”];
return a.concat.apply(a, arguments);
}
7) slice
创建并返回一个数组的片断或者说是子数组。
var arr = [1,2,3,4,5,6,7];
alert(arr.slice(1,3));
//输出:[2,3],下标为1的元素到下标为3的元素(不包括第二个参数值指定的元素)
alert(arr.slice(-3, -1));
//输出:[5,6],倒数第3个到倒数第1个(不包括第二个参数值指定的元素,即倒数第一个)
var b = a.slice(0); //快速的复制a数组的元素给b
var b = a.slice(); //也是快速的复制a数组的元素给b。
如果a是一个类似于arguments的类数组类变量,他们本身没有slice方法,但可以通过下面的方法复制其元素到一个新的数组。
var b = Array.prototype.slice.call(a);
8) splice 方法
在原数组上插入或删除数组元素,不创建新数组。splice()返回的是删除的元素组成的数组。
详见<<王者归来>>P210页
9) toString 和 toLocaleString
toString类似于join方法的无参实现,它将数组中的每一个元素都转换成字符串,然后用”,”连接起来,并返回连接后的字符串。
11. String
1) 字符串本身内容是不可更改的
2) 常用函数
var s = "ABC";
s.length;//取字符串的长度
s.charAt(index);//获取index位置上的字符
s.charCodeAt(index);//取index位置上字符的Unicode编码
s= String.fromCharCode(numX,numX,...,numX);//将每个Unicode字符编码转换为字符,然后返回一个字符串
s.substring(start, end);//抽取子串
s.slice(...);//抽取子串
s.indexOf(char);//查找指定字符在字符串中第一次出现的位置
s.search(regexp);//查找和匹配子串
s.replace(searchValue, replaceValue);//完成子串的替换
s.split(separator);//分割字符串
例:
var str= "www.51js.com";
var pos=str.indexOf(".");
document.write(str.substring(pos+1) + "<br/>");
var parts = str.split(".");
document.write(parts[1]+"<br/>");
pos=str.search("51js");
document.write(pos+"<br/>");
str=str.replace("51js", "无忧脚本");
document.write(str);
12. 正则表达式
1) 创建方法:
法1:var regexp = /^HTML/;
法2:var regexp = new RegExp("^HTML");
2) test()
//./.test(buf);
//./ 是正则表达式, test是正则表达式提供的匹配方法。
13. Infinity,NaN, undefined, null 的布尔规则, isNaN(), isFinite()
if(Infinity) // false
if(NaN) //false
if(null) //false
if(undefined) //false
if(!undefined) //true
isNaN(3) //false
isNaN ('a') //true
isFinite(Infinity) //false
isFinite(-Infinity) //false
isFinite(NaN) //false
特殊规则:
NaN与任何值或引用类型比较都是false
如果比较运算中有一个是NaN或undefined类型,>, <, >=, <= , 比较结果都是false, 详见27条
null==undefined; //true
14. 错误处理方式
(1)异常机制:js核心异常类, Error, EvalError, RangeError, SyntaxError, TypeError, URIError, ReferenceError
js没有MathError, 对于数学运算,如果得不到结果,将返回 NaN 或 Infinity
js 没有 RegexpError, 如果正则表达式对象出错, js 返回 SyntaxError
SyntaxError, 比较特殊,它不会沿堆栈传播,所以很难被catch,也就是说,正则表达式出错通过catch是捕获不到的,这种情况下,需要借助于另外一种方式。
手动抛出异常方法:throw new Error(”cc”);
(2) window.onerror 方式
javascript的window对象有一个特别的属性onerror,如果你将某个function赋值给window的onerror属性,那么但凡这个window中有javascript错误出现,该function都
会被调用,也就是说这个function会成为这个window的错误处理句柄。
// Display error messages in a dialog box, but never more than 3
window.onerror = function(msg, url, line) {
if (onerror.num++ < onerror.max) {
alert("ERROR: " + msg + "/n" + url + ":" + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;
onerror句柄的3个参数分别是错误信息提示,产生错误的javascript的document ulr,错误出现的行号。
onerroe句柄的返回值也很重要,如果句柄返回true,表示浏览器无需在对该错误做额外的处理,也就是说浏览器不需要再显示错误信息。而如果返回的是false,浏览器
还是会提示错误信息。
15. 变量
1) 用var声明
2) 声明可以缺省, 缺省声明直接赋初值的变量作用域默认为全局
3) 对于未声明也未赋初值得变量,如果直接使用,会抛出一个系统级别的Error,但delete 和 typeof 是个例外
4) 对于delete来说,无论变量是否赋初值,都返回一个布尔值true
5) 对于typeof, 任何一个未赋初值的标识符,无论是否已经被声明,都将返回一个字符串'undefined'。
使用这个特性可以用来判断某个对象是否已经被声明过或赋过值
if(typeof(System)=="undefined") {
System = new Object();
}
Ø 变量的作用域
1. 在js中,闭包和函数是独立的域,除此以外的任何程序段落都不是独立的域,换句话说,js没有块级作用域。
2. 事实上,在js中,变量真正意义上的作用域是运行时的调用域,而不是静态的语法域。
3. 在域中,以var声明的变量只在当前域或者当前域的子域起作用。
4. 一般情况下,在域中缺省var声明以及在域之外的变量为全局变量。
例:
for(var i=2; i<12; i++) {
expression;
}
alert(i); //虽然i是在for内部声明的,但是由于JS没有块级作用域,所以i是可以用在这里的。
16. 数据类型
1) 在JS中,所有的数值都是由浮点型表示的,因此精度问题相当严重。
2) 要将一个变量中的整数类型用16进制表示,可以用number的toString(16)方法来完成。如, (233).toString(16);
3) JS中的特殊常量
Infinity (JS1.3版本以后才有)
NaN (1.3版本以后才有)
Number.MAX_VALUE
Number.MIN_VALUE
Number.NaN (JS1.1版本就有了,以下一样)
Number.POSITIVE_INFINITY
Number.NEGATIVE_INFINITY
isNaN(3) //false
isNaN ('a') //true
isFinite(Infinity) //false
isFinite(-Infinity) //false
isFinite(NaN) //false
4) 数组
5) 对象
6) 函数
Ø 浮点数精度相关函数
Math.floor(12.5); //取出比当前数值小的最大整数
Math.round(12.5); //四舍五入
Math.ceil(12.5); //取出比当前数值大的最小整数
Math.abs(12.5); //取绝对值
var a = 12.34356;
a.toFixed(2); //toFixed(n)保留n位小数
Ø JS基本类型
JS中有三种主要的原始值(primitive,基本类型),它们是 Boolean值、数字(Number)和字串(String) (注:Boolean, Number和String是与它们相对应的对象)
原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法。
var bFound = false;
alert(bFound.toString());//outputs "false"
var bFound = false;
alert(bFound.toString());//outputs "false"
Ø 值类型和引用类型
JS中的基本类型,包括数值,布尔型,和null, undefined, 都是值类型;字符串在大部分行为上表现得和值类型相同(如在用==比较的时候,比较的是它的内容),从这一
点上看,也可以看成是值类型。
对象,数组和函数,都是引用类型。
Ø 值和引用的相互转换:装箱和拆箱
装箱:
var a =10;
var o_a = new Number(a);
拆箱:
o_a.valueOf();//返回原始值
Ø valueOf 和 toString (自动类型转换)
JS中所有对象都具有这两个方法,它是root类Object中的方法。
toString()----返回对象的原始字符串表示。不同的ECMAScript实现具有不同的值。
valueOf()----返回最适合该对象的原始值(primitive value)。对于许多类,该方法返回的值都与toString()的返回值相同。
当对象(包括自定义对象)在进行表达式计算时,根据运算类型的不同,JS总是会自动调用对象的valueOf()方法或者toString()方法将对象转换为运算的类型。
转换规则为:
优先转换为字符串的表达式(如对象的哈希表示[]),必定先调用对象的toString()方法,而优先转换为数值的表达式(如比较运算),则必定先调用自定义对象的valueOf()
方法。
Ø 强制类型转换
1) string --> number: parseInt, pareFloat
parseInt 和 parseFloat
JS提供了两种把非数字的原始值转换成数字的方法,即parseInt()和parseFloat()。
注意:只有对String类型(Number除外)调用这些方法,才能正确运行对其他类型返回的都是NaN。
2) 构造函数的强制类型转换 (从下面的例子中也可以看出不同对象之间相互转换的规则)
//字符串类型
var str='100';
var num=Number(str); //强制转换为number,输出:100
var obj=Object(str); //强制转换为object, 输出:100
var bool=Boolean(str); //强制转换为boolean, 输出:true
var fun=Function(str); //强制转换为function,输出:function, function anonymous(){100}
//数值类型
var num = 100;
var str = String(num);
var bool= Boolean(num); //输出:100
var obj = Object(num); //输出:100
//布尔类型
var bool=true;
var str = String(bool); //输出:true, 类型是String
var num = Number(bool); //输出:1
var obj = Object(bool); //输出:true, 但类型是object
//对象类型
var obj={};
var str=String(obj); // 输出:[object Object]
var num = Number(obj); //输出:NaN
var bool = Boolean(obj); //输出:true
17. 运算符
Ø 算术运算符的数据类型转换
1) js中的算术运算符会进行必要的数据类型转换 (+, -, *, /, %)
转换通常调用 对象的valueOf() 或 toString()方法来实现。
2) 3 + "" + 4; //得到"34"
"3"-0+1; //得到4
3) 0/0 得到 NaN, num/0 得到Infinity
4) parseFloat("123abc");//得到数值123, parseFloat会进行一个分析的过程
"123abc"-0;//得到NaN, "-"的隐式数值转换通常要求完全匹配。
true-0; // 1
false-0;// 0
parseFloat(true);// 得到 NaN
parseFloat(flase);//得到 NaN
Ø 关系运算符 "=="
1) 对于布尔,数值和字符串三种值类型数据,比较运算数的值是否相等。
2) 对于数组,对象和函数等引用类型数据,比较他们引用的是否是同一个对象。
3) 如果两个运算数的类型不同,"=="将尝试进行类型转换 ("!=" 和 "=="类似,也会进行转换)
1=="1" //返回 true, "1" 会先转换为1
"1"==object //首先会尝试将ojbect转换成字符串,再进行比较
true=1 //返回true, true会先转换为1
null==null;//返回true
undefined==undefined;//返回true
null==undefined;//返回true
NaN与任何值或引用类型比较都是false
a) null==undefined;
返回true, 这是一个非常特殊的情况
b) ===, 与==基本一致,但是不会尝试做类型转换。
Ø 所有关系运算符进行比较时都会进行数据类型转换
转换的优先顺序为:
1) 如果有一个是number,尝试转换成number
2) 如果有一个是string, 尝试转换成string
3) 如果没有一个是number或string, 尝试转换成number或string
4) true 转换成1, false转换成0
Ø 如果比较运算中有一个是NaN或undefined类型,>, <, >=, <= , 比较结果都是false
(null>undefined)||null==undefined; //返回true
null>=undefined;//返回false
从上面可以看出,在JS中, a>=b 和 a>b||a==b并不总是成立的,所以在进行a和b大小比较的时候要考虑到这个因素。
Ø 逻辑运算符
1) 仅将计算结果尝试转换为布尔值,不会对操作数进行转换
2) 在逻辑运算中,逻辑运算的最终返回值是计算结果的值,而不是计算结果转换后的布尔值
例, b = a || c; 如果a是一个对象且可以被转换为true, 则 a||c就是true, 但是b的值是a, 而不是true。
Ø 对象运算符
in, instanceof, new, delete, .运算符 和 []
a) Delete
delete可以删除运算数所指定的对象属性,数组元素或者变量, 返回true或false。注意:用var语句声明的变量不能被删除,delete会返回false;
如:
var x=3; //var x; 也可以
delete x; //返回false, 表示删除失败。
如果delete 的操作数是一个不存在的对象属性,数组元素,或者是一个没有通过var 声明的变量,delete都返回true, 也就是说,删除成功了。
x=3;
delete x; //返回true, 删除成功
b) JS的垃圾回收机制
delete删除一个属性或变量后不只是把它们的值置为undefined;当一个属性删除后,该属性将不再存在。另外,JS delete和C中的含义不同,delete操作并不一定会立即
导致该对象的内存回收,这个是由JS控制的,JS内部也具有类似于Java的垃圾回收机制。
另外IE浏览器提供了一个内置的函数CollectGarbage(),通知浏览器立即进行垃圾的回收。
c) [] 和 .
[] 和 . 都可以用来存取对象或数组元素。区别在于.运算符要求第二个运算数必须是合法的标志符,而[]运算符的第二个运算数可以是数组下标,对象标志符,或者是任
意类型的值,但不能是未定义的标志符,当第二个运算数是对象的时候,他总是先调用toString()方法来进行转换,如果转换失败,再调用valueOf()方法来转换。另外,true和
false总是被转换成字符串值"true"和"false",这个和关系运算符不同,主要原因其实也简单,关系运算符进行类型转换时总是尝试先转换成Number,然后才是String,所以true就
被转换成了1; 而[]进行转换时首先尝试的是String, 所以true被转换成了"true"。
a.x 和 a[x]等价, b[1]和b["1"]等价
d) in
使用in 有两种方式:
1) 普通用法
var aSet = new Object();
if("key1" in aSet) //判断aSet是否包含属性key1
delete aSet["key1"];
2) 用在for 中进行对象属性的遍历
for(variable in object)
statement;
18. switch 语句
1) JS中的swith语句允许case语句后面跟随任意的表达式,这一点和Java不同,在Java中,case表达式必须是编译时的常量,它们的值必须为整数类型。
2) 匹配case表达式使用等同运算符 (===)类判定的,这个运算符不会尝试做类型转换。
19. 对象
1) 创建
var o = new Object();
var now = new Date();
var pattern = new RegExp("//sjava//s", i);
2) 对象属性的创建
方法一:
var point = new Object();
point.x = 2.3;
point.y = -1.2;
point.getDistance = function() {
return Math.sqrt(this.x*this.x+this.y*this.y);
}
方法二:构造函数法
function Point(x, y) {
this.x=x;
this.y=y;
this.getDistance = function() {
return Math.sqrt(this.x*this.x+this.y*this.y);
}
//重写toString方法
this.toString = function() {
return "(" + this.x + "," + this.y + ")";
}
}
var pointA = new Point(1, 2);
var pointB = new Point(3, 4);
//通过重载过的toString()方法显示它们
document.write(pointA+"<br/>");
document.write(pointB+"<br/>");
方法三:JSON法
var point = {x:1, y:2};
var rectangle = {
upperLeft: {x:2, y:2},
lowerRight: {x:4, y:4}
}
注意:通过方法三定义的变量x,y其实隐含了this指针,所以他们是对象的属性,而不是全局变量。JSON方法的构造函数时Object(),他们的类型都是基类Object。
例:
var point = {x:1, y:2};
alert(x);//报错,x没有定义,undefined
alert(point.x);//正确
3) 代词this 总是指向真正调用这个方法的对象,即函数的所有者。
Ø Object对象
1) 它是所有对象的根对象。
2) 内置属性
constructor
hasOwnProperty()
isPrototypeOf()
propertyIsEmuerable()
toLocaleString()
toString()
valueOf()
Ø prototype关键字
采用prototype关键字可以将属性添加到一个对象类所有的实例中去。
例:
function Point(x, y) {}
Point.prototype.move = function (x, y) {…}
var a = new Point(2, 3);
var b = new Point(1, 2);
a.move(2,4); //Point对象实例a和b都有move方法
b.move(3,4);
Ø 类属性和对象属性
比较Point.move= function(x,y){…} 和Point.prototype.move=function(x,y){…}的区别:
前者是将move直接作为Point类的属性,即Function类的实例的属性。后者才是将move作为Point实例的属性。这里需要提到一个关于对象层次的问题。在上面的例子中Point既是Point的实例的类,同时也是Funtion类的一个实例,所以Function是比Point抽象层次根高的一个概念。
例:
Point2D.type = “2D Point”; //这里是类的属性,我们只能通过类本身来访问它
Alert(Point2D.type); // 输出:”2D Point”
Point2D.prototype.deminsion=2; //这是对象属性,我们只能通过对象来访问它
var p = new Point2D (1, 2);
alert(p.deminsion); //输出:2
alert(Point2D.deminsion);//输出:undefined
alert(p.type); //输出:undefined
Ø JS反射机制
通过 for(var p in object)可以枚举对象属性。
Ø 构造函数
构造函数通常没有返回值,如果函数被作为构造函数并且有返回值,具有如下特性:
1) 如果函数的返回值是一个引用类型(数组,对象或者函数)的数据,那么将这个函数作为构造函数用new运算符执行构造时,运算的结果将被它的返回值取代,这时候,构造函数体内的this值丢失了,取而代之的是被返回的对象。
2) 如果函数的返回值是一个值类型,那么这个函数作为构造函数用new运算符执行构造时,它的返回值将被舍弃。new表达式的结果仍然是this所引用的对象。
例:
function test()
{
this.a = 10;
return function() {return 1};
}
var a = new test(); //运行结果a的值和b的值相同,都是test函数返回的闭包。
var b = test();
3) 用户可以自己判别函数是作为构造函数执行,还是作为普通函数执行
function f () {
if(this&& this.constructor==arguments.callee) { //作为构造函数执行}
else {//作为普通函数执行}
}
Ø 常见内置对象
Math, Date, Error, Array, String, RegExp, Number, Boolean, Function, Arguments
20. 函数和闭包
1) JS中,函数是一个真正的数据类型,可以被存储在变量,数组和对象属性中,而且函数还可以作为参数传递给其他函数。
2) 函数的定义
法1:通过function
声明式:
function f1(a, b, c) {...}; //在程序中,可以用直接用f1这个名字引用这个函数,如 var f2=f1;
引用式:
var f1 = function(a, b, c) {...};
法2:通过Function
var square = new Function("x, y", "return x*y"); //x,y是参数,return x*y是函数体
注:
由于Function通过一个字符串定义函数体,所以他可以用来动态的构造函数脚本,比如通过拼接字符串的方式生成执行代码。
通过function定义的函数和通过Function定义的函数是一样的,它们都生成了一个Function对象的实例,可以调用Function对象的方法。
如:
function f1(a, b);
f1.call(objectX, a, b); //以objectX为函数的所有者调用函数。call和函数所有者的内容见后面的内容。
3) 声明式和引用式的区别:
声明式函数定义的代码先于函数执行代码被解析器解析,所以可以先使用,后定义。
引用式函数定义是在函数运行中进行动态解析的,需要先定义,后使用。
4) functon f1(){} 和 var f1=function() {}在语法上是等价的
所以:
function A() {
function B() {
...
}
}
与
function A() {
var B = function() {
...
}
}
等价, 由于B是用var修饰的,所以B是函数A内部的局部变量,而不是全局变量。
因此,对于以下的表达式,可以如下分析:
(function A() {
fun = B;
function B() {
...
}
}) ();
将函数A的私有函数B通过全局变量fun暴露出来, B其实就是一个闭包。
5) 函数对象定义时的形参数量可以通过length属性获得,如下面的max.length的值为2。
function max(x, y) {
...
}
6) 函数的实参和形参的数量可以不相同,如果实参量少于形参数量,那么多出来的形参的值就是undefined;
如果实参多于形参,那么多出来的那部分实参就不能通过形参标识符的形式访问,但可以通过特殊的Arguments对象访问。
从这个特性可以看出,要在JS中实现重载是没有直接的方法的,假定你定义了function f(a,b)和function f(a)函数,当你执行f(1)的时候, JS并不能区分你要执行的是哪个函数,它总是执行最后面定义的那个。
要在JS中实现类似于函数重载那样的功能,需要借助于arguments对象来间接的实现,可以参考<<JS王者归来>>的P168页的例子。
Ø 函数的调用对象
函数在调用的时候会生成一个临时的调用对象,通常这个调用对象会被添加到作用域链的头部,取代当前域成为默认的域,而在函数体内的(局部变量)和(参数)就会成为
这个域上的变量,或者这个临时对象的属性来访问。一般情况下,这个域在函数调用结束后就会被销毁,但是有可能这个域在函数调用结束之前就被外部引用,并且这种引用并没有随着函数调用的结束而结束,在这样的情况下,被引用的环境就不会被销毁。
除了局部变量和形式参数以外,调用对象还定义了一个特殊属性,名为arguments(类型为Arguments),它的状态和局部变量和形参是相同的。
this 代词也是JS函数调用对象的上下文环境中的一个属性,它和arguments之类的上下文属性具有类似的特性。
Ø Arguments对象
1) Arguments对象是一个集合,可以按照数字下标获取传递给函数的参数值,并且arguments.length可以获得传递给函数的实参数量。
2) Arguments的行为有点像数组,但它其实不是数组,它不具备JS核心数组的一些方法如: join, sort, slice等。
例:
function f(a, b, c) {
alert(f.length); //输出形参数量:3
alert(arguments.length); //输出实参数量:4
alert(arguments[0]);
}
f(1,2,3,4);
3) arguments中的参数始终是相应命名参数的别名,不管这个参数是值类型还是引用类型,改变arguments中的参数值一定会影响到对应的命名参数,反之亦然。
4) Arguments对象还提供了一个有用的属性,叫做callee, 它被用来引用当前正在执行的函数,它提供了一种匿名的递归调用能力,这对于闭包来说非常有用。
例:计算10的阶乘
(function(x){return x>1?x*arguments.callee(x-1):1})(10);
Ø 函数的所有者 -- this
1) 函数的所有者是指调用这个函数的对象,在函数内部可以用this来表示。
2) 函数的所有者不一定是指向函数定义本身所在的对象,它是一个运行时的概念,它的确切含义应该为:当前调用这个函数的对象。
3) this代词不一定只在对象方法的上下文中才出现,全局函数调用和其他几种不同的上下文中也都有this代词。
4) this 代词也是JS函数调用对象的上下文环境中的一个属性,它和arguments之类的上下文属性具有类似的特性。
5) 构造函数的所有者是构造函数创建的对象本身。
6) 在浏览器客户端JS中,顶层定义的全局函数的所有者是window,它们也可以视为Window对象的属性。
注: 因为JS中的函数可以有多个引用,理论上来说,既可以作为这个对象的方法,也可以作为那个对象的方法,而且这种指派可以在运行时才确定。因此,JS方法的灵活
性注定了this指针只能在运行环境中动态的判定,甚至直到函数被调用时才能最终确定,所以它才被设计成为了函数调用上下文的属性。
例:
function Point(x,y) {
this.x=x;
this.y=y;
var z=3;
this.f = function(){ alert(this.constructor); }
this.n=function() { alert(z); }
}
//
var p = new Point(1,2);
p.f(); //输出1:Point(){...}, 由于Point被当作了构造函数,它的所有者就是它创建的对象,所以this指向p。
//
p.n(); //输出2:3
//
Point(1,2);
f(); //输出3:Window(){}
对于输出2,虽然Point被当作构造函数调用,但是它仍然具备闭包的特性,由于它的成员函数n引用了它的局部变量z,所以该构造函数的调用对象不会销毁,形成了闭包。从这个例子也可以看出,不论一个函数是被正常调用还是被作为构造函数调用,它都会生成调用对象,遵循闭包的法则。
对于输出3, 由于直接调用了Point函数,而Point函数是顶层定义的全局函数,所以函数内部的this指向的是window对象,所以this.x,this.y,this.y都是全局对象。
//在这种情况下,Point的定义和下面的等价。
function Point(x,y) {
x=x;
y=y;
var z=3;
f = function(){ alert(this.constructor); }
n=function() { alert(z); }
}
Ø call和apply
JS给Function原型定义了两个方法,它们是call 和 apply。使用这两个方法可以像调用其他对象方法一样调用函数,并指定函数的所有者。call()和apply()方法的第一个参数都是要调用函数的对象,用call()和apply()调用函数时,函数内的this属性总是引用这个参数。call() 的剩余参数是要传递给要调用函数的值,他们的数量可以是任意的。apply()方法和call()类似,只不过它只接受两个参数,除了调用者之外,它的第二个参数是一个带下标的集合(比如数组,Arguments对象),apply()方法把这个集合中的元素作为参数传递给调用的函数。
注意:apply()和call()的调用者一定是要被调用的函数对象,即Function对象的实例。
如: function f() {};
如果要用apply调用函数f,使得f的所有者变成objectX,写法就是f.apply(objectX);
function add(x, y) {
…
}
var p = {};
add.call(p, 3, 4); // call方法, 把add函数作为p的方法调用
add.apply(p, [3, 4]); //apply方法
Ø this代词在浏览器中的异步问题
通过0级DOM或者2级DOM的方式将方法注册为事件处理寒暑,结果发现this代词指向引发这个事件的对象,而不是指向被注册方法本身的对象。
另一个异步机制--计时器,也存在类似的问题。
解释:
由于函数或者闭包是被指派给定时器或者事件代理的,直到定时器激活或者事件触发时相应的方法才被调用,但是在这个异步的过程中,方法所属的对象可能发生任何事情,所以异步调用的方法不能依赖于它所属的对象。
21. 浏览器和DOM
Ø Window对象
1) Window对象的生命周期
Window对象的生命周期比较特殊,通常来说,一个Window对象在某个浏览器进程被终止前总是存在的。也就是说,即使关闭了浏览器窗口,这个窗口所引用的Window对象依然存在,除非这个浏览器进程已经完全终止。
例:
w = window.open();//打开一个新窗口
w.close();
alert(w.closed);//即使创建的窗口关闭,该窗口的window句柄依然存在。
2) window句柄总是引用当前窗口Window对象
3) 在浏览器中,窗口的框架也是由Window对象表示的,尽管我们用Frame对象来称呼框架,它其实和Window对象是同一类对象。(window.frams[0])
Ø Document对象
1) 基本属性
title: 文档标题
domain:提交文档页面的Web服务器所在的域
referrer:把浏览器带到当前文档的链接,如果当前页面不是从其他页面跳转过来的 ,则为空,它是只读的。
location:等同于URL,但不是DOM标准
URL:DOM标准,document.URL = window.loaction.href
lastModified
document.title = “Title”; //修改文档标题
2) 外观属性
alinkColor, linkColor, vlinkColor, fgColor, bgColor
3) 子对象接口
anchors[], applets[], forms[], images[], links[], cookie
注意:
a. 对于anchors[],只有有name属性的a标签才会被加入anchors
b. 子对象接口是集合,但他们不是数组,没有数组的方法。
c. 可以用Form对象的elements[]子接口遍历表单输入元素。
Ø 对话框
有三种弹出对话框的方法:
alert(“Hello”);
if( confirm(“Are you sure”)) {…}; // 要求用户点击OK或Cancel键
prompt(“What’s your name”);// 要求用户输入
Ø 状态栏
使用window.status和window.defaultStatus来改变状态栏的默认行为。
setInterval(“window.status=new Date();”,1000);
Ø Location对象(window.location)
1) Location 对象的href属性是一个字符串,它包含完整的URL文本。
2) Location的其他属性,如protocal, host, pathname, search,则声明了URL的各个部分。
3) Location的search属性包含的是URL问号之后的那部分,即查询字符串。
4) Location自身的toString方法返回他的href属性本身。
5) 将一个URL字符串赋给Location对象本身或者Location对象的href属性,将会引发浏览器装载并显示那个URL所指的页面文档。这个特性经常被用于Web应用的页面重定向或者页面刷新。
如:self.location.href = www.baidu.com
self.location = top.location;
6) Location对象还提供了另外两个方法来支持web页面重定向,他们是reload()和replace(URL)方法。reload导致页面重新装载。replace会装载一个新的页面,他和给location赋值的方式的不同之处在于:如果使用replace()使一个新的文档覆盖当前文档,浏览器上的Back按钮就不能够使用户返回原始文档,而location赋值方法就可以。
7) window.location和document.URL的值相同,但是当存在服务器重定向时,document.URL包含的是实际装载的URL,而location.href包含的则是原始请求的文档的URL。
Ø 其他内置对象
Navigator: 提供浏览器本身信息,如版本号等等。
Screen: 提供屏幕分辨率,颜色等信息。
History: back, forward, go
Ø DOM
1) 在HTML中,Element是我们最常见到的一类DOM对象,一个Element通常对应于一个HTML标记。他的DOM对象实现了ElementNode子接口,其nodeType属性指等于Node.ELEMENT_NODE。
例:
var div1 = document.createElement(“div”); //创建一个div标记
div1.innerHTML=”<h1>Hello world!</h1>”;//设置div的内容
div1.setAttribute(“name”, “myDiv”); //设置div的属性
document.getElementsByTagName(“body”)[0].appendChild(div1);
2) removeChild(node), replaceChild(newChild, oldChild), insertBefore(…)
3) getElementById(), getElementByName(), getElementByTagName()
4) create attribute and textNode
var main = document.getElementById(‘doc’);
var attr = document.createAttribute(‘temp’);
attr.value = ‘temporary’;
main.setAttributeNode(attr);
var output = main.getAttribute(‘temp’);
var text = document.createTextNode(output);’//create textNode
main.appendChild(text);
5) HTMLTableElement.rows, HTMLTableElement.lastChild,
HTMLTableElement.rows 取得表的所有行
HTMLTableElement.lastChild取得表的最后一行
6) ElementNode.attributes 属性
7) Node.parentElement (wok on IE only), the standard is parentNode
Ø 事件处理
基本事件处理
1) 事件绑定有两种方式
方式1:<button id=’btn1’ οnclick=”var x=3; alert(‘x’); ”>btn</button>
方式2:
<button id=’btn1’>btn</button>
<script>
btn1.οnclick=function() {alert(“Hello!”);}
</script>
注:在基本事件处理中,同一个DOM对象的同一个方法只允许注册绑定一个处理函数,多次绑定会被覆盖。
2) 事件处理函数的返回值
通常情况下,处理函数的返回值没有意义,但有一些情况例外:
例外1:
在Form对象的onsubmit事件里,事件句柄返回false将阻止浏览器对表单的提交。
<form action=”http://www.baidu.com” οnsubmit=”return false;”>
例外2:
在其他一些事件里,返回的布尔值也有类似的意义,例如在IE地址栏上键入下面的代码可以让大部分页面上的右健菜单失效。
Javascript: document.body.oncontextmenu = function() {self.event.returnValue=false;}; void(0);
标准事件传播机制(Mozilla)
1) 分为捕捉(capturing),目标节点自身,起泡(bubbling) 三个阶段。
2) 任何一个阶段都可以通过调用Event.stopPropagation()方法阻止某个事件的继续传播。
注:IE不支持这个方法,但可以通过Event.cancelBubble=true来达到同样的效果。
3) 任何一个阶段都可以通过通过调用Event对象的preventDefault()方法来阻止浏览器默认动作的发生。
注:IE不支持这个方法,但可以通过Event.returnValue=false 来达到同样的效果。
4) IE事件模型的事件传播只支持两个阶段:目标节点自身和起泡。
在Mozilla中通过addEventListener注册事件时,也可以通过对第三个参数指定为false来阻止在捕捉阶段捕捉事件,以达到和IE同样的效果。
5) 并不是所有事件都支持起泡,具体来说,事件起泡只适用于原始事件或用户输入事件,不适用于高级的语义事件。
标准事件处理程序(Mozilla支持,IE不支持)
1)注册事件使用 addEventListener(eventType, processFunction, true/false)
其中,第三个参数如果为true, 则指定的事件处理程序将在事件传播的捕捉阶段用于捕捉事件,如果为false,则事件处理程序就是常规的,即只在目标自身和起泡阶段处理事件。
btn1.addEventListener("click", function(){
return function(){
alert(this);
}
}.call(btn1), true );
2) 同一个DOM对象的addEventListener方法允许为同一个事件类型注册多个事件处理函数,这在基本事件模型中是不可以的。
3) removeEventListener()用来删除已注册的事件处理函数
IE事件处理程序
1) 注册事件:attachEvent(eventType, processFunction), 和addEventListener功能相同,只是少了第三个参数,因为IE没有捕捉阶段。
2) 删除事件:detachEvent(…)
3) IE模型中作为第一个参数传递的事件类型必须包括前缀”on”,这一点和标准模型有所区别。
4) 事件处理函数内部的this代词总是指向window对象。
<button id="btn1" >btn</button>
<script>
var btn1 = document.getElementById("btn1");
btn1.attachEvent("onclick", function(){alert(this==window);}); //返回了true
</script>
浏览器的差异性
1) 在事件参数问题上,在Mozilla中,Event对象是被作为事件接受函数的参数传递的(所以没有event这个属性),而在IE中则是作为Window对象的属性。
event = event || window.event;
2) 在IE中,srcElement代表事件的触发者,而Mozilla浏览器中相应的属性是target。
var target = event.srcElement || event.target;
this与事件
1) 在简单事件模型(DOM0)中,this关键字引用的是实际发生事件时捕获了事件的对象。
如:
<button id="btn1" οnclick="alert(this.tagName);" >btn</button> //输出:BUTTON
2) 标准事件处理程序(Mozilla)中的this指针并不一定指向实际捕获了事件的对象,所以需要进行特殊处理。
例:
<button id="btn1" >btn</button>
<script>
var btn1 = document.getElementById("btn1");
btn1.addEventListener("click", function(){
return function(){
alert(this==btn1);
}.call(btn1); //通过call方法的使用将btn1指定为事件处理函数的所有者
}, true );
</script>
注:我实际试验的结果显示:通过addEventListener注册的事件的所有者和简单事件模型中是一样的,都是指向了捕获了事件的对象,并不如上面所说的那样,估计它指向的对象是不一定的,不管如何,都使用上面的方法处理。
3) 在IE事件模型中,事件处理函数内部的this代词总是指向window对象。可以用上面同样的方法进行特殊处理。
<button id="btn1" >btn</button>
<script>
var btn1 = document.getElementById("btn1");
btn1.attachEvent("onclick", function(){ return function(){ alert(this==btn1);}.call(btn1);} );
</script>
Ø 捕获鼠标事件setCapture
1) IE中可以通过node.setCaputur()e让DOM对象捕捉到鼠标事件, 通过node.releaseCapture()来释放鼠标事件。
2) 在 Mozilla中,对应的方法为Event.captureEvents(…)。
Ø 获得当前鼠标位置所在的DOM元素
document.elementFromPoint(x, y) 可以根据屏幕上像素点的位置获得相应的DOM元素。当鼠标事件发生时,event.x和event.y是鼠标当前所在位置的像素坐标,因此,document.elementFromPoint(event.x, event.y)就恰好获得了当前鼠标位置所在的DOM元素。
Ø DOM 中,节点的class属性被写作className
Ø setInterval 和 setTimeout
var intv = setInterval('A()', 5); //每隔5毫秒执行动作A
var tout = setTimeout('A()',5); //5毫秒以后执行动作A
clearInterval(intv); //清除计数器
clearTimeout(tout); //取消定时器