一: 我们应该注意的
1、== != 与 === !===
JavaScript 有两组相等运算符:=== 和 !==,以及它们邪恶的孪生兄弟 == 和 !==。=== 和 !== 这一组运算符会按照你期望的方式工作。如果两个运算数类型一致且拥有相同的值,那么 === 返回true,而 !== 返回false。而它们邪恶的孪生兄弟只有两个运算数类型一致时才会做出正确的判断,但是如果两个运算数是不同的类型时,它们试图去强制转换其值的类型。转换的规则复杂且难以记忆。这里有这一些有趣的例子:
'' == '0'; //false 0 == ''; //true 0 == '0'; //true false == 'false'; //false false == '0'; //true false == undefined; //false false == null; //false null == undefined; //true ' \t\r\n ' == 0; //true |
通过上面的例子,大师建议我们:永远不要使用 == 和 != ,请始终使用 === 和 !== ,有这两个运算符的话,以上例子全部为false!
2、eval
eval 函数传递一个字符串给JavaScript编译器,并且执行其结果。它是一个被滥用得最多的JavaScript特性。例如:如果你知道点表示法,但不知道下标表示法,就可能会这么写:(我为了实现一个功能,专门研究了一下eval函数,最后用下面的方法写出了想要的功能,高兴得很!当第二天看到这位大师这么说的时候,想死的心都有了!哎!)
eval("myValue = myObject."+ myKey +";");
而不是这么写:
myValue = myObject[myKey];
使用eval形式的代码会更加难以阅读。而且也会降低语言的性能。
所以,大师建议我们:eval、Function、setTimeout、setInterval 这四个方法应该避免使用!
我决定了,以后都不用了,呵呵!
3、++ --
作为一条原则,大师建议我们:不再使用这两个运算符,用 += 和 -= 代替!
4、function 语句对比函数表达式
下面的语句:
function foo(){}
意思相当于:
var foo = function (){};
大师建议我们:用第二种形式!
5、类型的包装对象
JavaScript 有一套类型的包装对象。例如
new Boolean(false)
会返回一个对象,该对象有一个valueOf方法会返回被包装的值。这其实完全没有必要,并且有时还令人困惑。
大师建议我们:不要使用new Boolean 、 new Number 或 new String,同时避免使用 new Object 和 new Array。可使用{}和[]来代替!
6、new
new这个东西说了很多,也很有用(个人感觉),可是大师建议我们:一个更好的应对策略就是根本不去使用 new 。
7、JavaScript的假值
这些值全部等同于假,但它们 是不可互换的。例如,这是用一种错误的方式去确定一个对象是否缺少一个成员元素:
value = myObject[name];
if (value == null) {
alert(name + ' not found. ');
}
undefined 是缺失的成员元素的值,但该代码片段用null 来测试。它使用了会强制类型转换的==运算符,而不是更可靠的===运算符。
8、NaN
NaN是一个特殊的数量值,它表示不是一个数字,可是他的类型却是number ,可以通过下面的代码来验证:
typeOf NaN === 'number' // true
NaN === NaN //false
NaN !== NaN //true
isNaN (NaN) //true
isNaN (0) //false
isNaN('oops') //true
isNaN('0') //false
大师列出了上面几条,最后建议我们:自己写一个判断是不是数字的方法!
var isNumber = function(value){
return typeof value === 'number' && isFinite(value);
};
9、parseInt
/** * parseInt 是一个将字符串转换为整数的函数, * 它在遇到非数字时停止解析, * 以致于出现下面的情况。 */ var myValue1 = parseInt("16"); //myValue1 = 16 var myValue2 = parseInt("16 is a number"); //myValue2 = 16 /** * 如果一个字符串是以0开头的, * 那么该字符串将被基于八进制而不是十进制来求值。 * 在八进制中,8和9不是数字,所以会出现下面错误。 */ var myValue3 = parseInt("08"); //myValue3 = 0 var myValue4 = parseInt("09"); //myValue4 = 0 /** * 这个错误导致了程序解析日期和时间时出现问题。 * 幸运的是,parseInt可以接受一个基数作为参数, * 我们可以用下面的方法去代替 */ var myValue5 = parseInt("08", 10); //myValue5 = 8 |
10、编码风格
大师建议我们:{}的{ 一定要放到一行的结束,不要放到一行的开始!
二:对象
1、对象字面量(对象的定义)
/** * 1、对象是属性的容器。 * 2、“名/值”对 "first-name"/"Jerome"。 * 3、属性名只要是符合合法的命名且不是保留字,可以不用“”号括住, * 所以"first-name"必需括住,因为“-”不是合法的命名, * 而first_name,则不必括住。 * 4、属性之间用“,”号分隔。 * 5、对象可以嵌套。 */ var empty_object = {}; var stooge = { "first-name": "Jerome", "last-name": "Howard" }; var flight = { airline: "Oceanic", number: 815, departure: { IATA: "SYD", time: "2009-09-09 14:55", city: "Sydney" }, arrival: { IATA: "LAX", time: "2009-09-09 14:55", city: "Los Angeles" } }; |
2、检索(对象属性访问)
/** * 要检索对象中的值,可以用[]和.两种方式, * 但是要注意,属性名要符合规则且不是保留字的时候才能用.的方式访问 */ var strValue1 = stooge["first-name"]; //strValue1 = Jerome,这样是对的 var strValue2 = stooge.first-name; //这样是错误的 var strValue3 = stooge["last_name"]; //strValue3 = Howard,这样是对的 var strValue4 = stooge.last_name; //strValue4 = Howard,这样是对的 /** * 如果你尝试检索一个不存在的成员元素的时候,将返回一个undefined值 */ stooge["middle-name"] //undefined flight.status //undefined stooge["FIRST-NAME"] //undefined /** * || 运算符可以用来填充默认值 */ var middle = stooge["middle-name"] || "asdf"; var status = flight.status || "unknown"; /** *对象通过引用来传递,它们永远不会被拷贝 */ var x = stooge; x.nickname = 'Curly'; var nick = stooge.nickname; //nick = Curly |
三:函数
1、函数字面量(函数定义)
var add = function (a, b){ return a + b; }; |
2、调用
函数除了声明时定义的形式参数,还有两参数this 和 arguments。
函数的调用有四种:方法调用模式、函数调用模式、构造器调用模式、apply 调用模式,这些模式在如何初始化关键参数this上存在差异。
·方法调用模式
当一个函数保存为对象的一个属性时,我们称它为一个方法。
//创建 myObject,它有一个value属性和一个increment方法。 //increment方法接受一个可进的参数,如果不是数字,那么默认使用数字1 //this被绑定到myObject var myObject = { value: 0, increment: function(inc){ this.value += typeof inc === 'number' ? inc : 1; } }; myObject.increment(); document.writeln(myObject.value); //1 myObject.increment(2); document.writeln(myObject.value); //3 |
·函数调用模式
当一个函数并非一个对象的属性时,那么它被当作一个函数来调用。
// sum的值为7,add函数在这里被调用是函数调用模式 // this被绑定到全局对象 var sum = add(3, 4); |
·构造器调用模式
/** * 在调用函数的时候,前面加上一个new来调用,称为构造器调用 * this被绑定到新函数 * 注意:大师建议不使用这种方式的构造器函数 */ //创建一个名为Quo的构造器函数,它构造一个带有status属性的对象 var Quo = function(string){ this.status = string; }; //给Quo的所有实例提供一个名为get_status的公共方法 Quo.prototype.get_status = function(){ return this.status; }; //构造一个Quo实例 var myQuo = new Quo("confused"); |
·apply调用(看不懂,不记录了)
3、异常
var add = function(a, b){ if(typeof a !== 'number' || typeof b !== 'number'){ throw { name: 'TypeError', message: 'add needs numbers' }; } }; var try_it = function(){ try{ add("seven"); }catch(e){ document.writeln(e.name + ':' + e.message); } }; try_it(); |
4、给类型增加方法
//JavaScript给类型增加方法的时候,必须以下面这种格式 String.prototype.methodName = function(){ }; //这样做很麻烦,所以我们用一个方法解决 Function.prototype.method = function(methodName, methodFunc){ if(!this.prototype[methodName]){ this.prototype[methodName] = methodFunc; } return this; }; //这样我们在以后扩展方法的时候就方便多了 Number.method('integer', function(){ return Math[this < 0 ? 'celling' : 'floor'](this); }); document.writeln((-10 / 3).integer()); //-3 //JavaScript缺少一个移除字符串末端空白的方法 String.method('trim', function(){ return this.replace(/^\s+|\s+$/g, ''); }); document.writeln(" asdf ".trim()); |
5、递归
6、闭包
一个函数可以访问它被创建时所处的上下文环境,这被称为闭包!
例:
//把一个节点的颜色由黄色渐到白色 var face = function(node){ var level = 1; var step = function(){ var hex = level.toString(16); node.style.backgroundColor = 'FFFF' + hex + hex; if(level < 15){ level += 1; setTimeout(step, 100); } }; setTimeout(setp, 100); }; face(document.body); |
一个很不好的例子:
/** * 可以想象一下,下面的alert会出现什么问题呢? * alert只会出现1个数字,nodes.length + 1 之后的数字,为什么呢? * 因为函数有访问它被创建时所处的上下文环境,这被称为闭包!i一直在变! */ var add_the_handlers = function(nodes){ var i; for(i=0; i<nodes.length; i+=1){ nodes.on allowScriptAccess="never" allowNetworking="internal" wmode="transparent"click = function(){ alert(i); }; } }; /** * */ var add_the_handlers = function(nodes){ var i; for(i=0; i<nodes.length; i+=1){ nodes.on allowScriptAccess="never" allowNetworking="internal" wmode="transparent"click = function(){ return function(e){ alert(e); } }(i); } }; |
7、继承
/** * 推荐以这种方式实现继承 */ if(typeof Object.beget !== 'function'){ Object.beget = function(o){ var F = function(){}; F.prototype = o; return new F(); }; } var myMammal = { name : 'Herb the Mammal', get_name : function(){ return this.name; }, says : function(){ return this.saying || ''; } }; var myCat = Object.beget(myMammal); myCat.name = 'Henrietta'; myCat.saying = 'meow'; myCat.purr = function(n){ var i, s = ''; for(i = 0; i < n; i += 1){ if(s){ s += '-'; } s += 'r'; } return s; }; myCat.get_name = function(){ return this.says + ' ' + this.name + ' ' + this.says; }; |
三、数组
1、如何判断一个对像是不是数组
/** * 判断一个对象是不是数组 */ var is_array = function(value){ return value && typeof value === 'object' && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length')); }; /** * 扩展reduce方法, * 接收一个计算函数和一个计算的初始值, * 并返回最后的计算结果 */ Array.method('reduce', function(func, value){ var i; for(i=0; i<this.length; i+=1){ value = func(this[i], value); } return value; }); //下面是用法 var data = [4, 8, 15, 16, 23, 42]; var add = function(a, b){ return a + b; }; var mult = function(a, b){ return a * b; }; var sum = data.reduce(add, 0); //sum = 108 var product = data.reduce(mult, 1); // product = 7418880 |
2、维度
JavaScript的数组通常不会初始化,如果你用[]得到一个新的数组,它将是空的。如果你访问一个不存在的元素,则将得到的值是undefined。我们可以设置一个Array.dim方法,用于创建和初始化数组。
/** * 创建并初始化一个数组 */ Array.dim = function(dimension, initial){ var a = [], i; for(i=0; i<dimension; i+=1){ a[i] = initial; } return a; }; /** * 创建并初始化一个二维数组 */ Array.matrix = function (m, n, initial){ var a, i, j, mat = []; for(i=0; i<m; i+=1){ a = []; for(j=0; j<n; j+=1){ a[j] = initial; } mat[i] = a; } }; |
四、正则表达式
1、先确定哪些方法可以使用正则表达式
regexp.exec、regexp.test、string.match、string.replace、string.search、string.split |
2、通过两个例子解析正则表达式
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/; var url = "http://www.ora.com:80/goodparts?q#fragment"; /** * 让我们来调用parse_url的exec方法。 * 如果能成功地匹配我们传给它的字符串, * 它将会返回一个数组,该数组包含了从这个url中提取出来的片段 * result结果为: * ['http://www.ora.com:80/goodparts?q#fragment', * 'http', * '//', * 'www.ora.com', * '80', * 'goodparts', * 'q', * 'fragment' * ] */ var result = parse_url.exec(url); /** * 匹配一个数字,test方法返回boolean */ var parse_number = /^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i; parse_number.test('1'); //true parse_number.test('number'); //false parse_number.test('98.6'); //true parse_number.test('132.21.03.10'); //false parse_number.test('123.45E-67'); //true parse_number.test('123.45D-67'); //false /** * 解析: * 1、^字符表示这个字符串的开始。 * 2、$字符表示这个字符串的结束。 * 3、+字符表示1或多次。 * 4、?字符表示0或1次。 * 5、*字符表示0或多次。 * 6、_字符表示任一字符 * 7、[]字符表示只能对应包含的数据,[A-Za-z]表示:可以对应26个英文字母的大小写 * 8、(?:...)字符表示一个非捕获型分组,(...)表示一个捕获型分组, * 说白了就是(...)括中的对应数据会放到匹配结果中, * (?:...)就不会放到匹配结果中 * 9、{m, n}表示m-n之间次。 * 10、|字符表示或 * 11、.字符匹配除行结束符以外的任何字符 * 12、\d=[0-9],\D相反。 * 13、\s表示空白,\S相反。 * 14、\w=[0-9A-Z_a-z],\W相反。 */ |