相等操作符
最早的ECMAScript中的相等和不相等操作符会在执行比较之前,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质疑。最后ECMAScript的解决方案就是提供两组操作符:相等和不相等-先转换再比较,全等和不全等-仅比较而不转换。
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
1.如果有一个操作数是布尔值,则在比较相等性之前现将其转换为数值-false 转换为0,true 转换为1。
2.如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值。
3.如果一个操作数是数值,另一个操作数不是,则调用对象的valueOf方法,用得到的基本类型值按照前面的规则进行比较;
基于第三点比较规则来说话,这两个操作符在进行比较时则要遵循下列规则:
a:null和undefined是相等的
b:要比较相等性之前,不能将null和undefined转换成其他任何值
c:如果有一个操作数是NaN,则相等操作符也返回false,而不相等操作符返回true。重要提示:即使两个操作数都是NaN,相等操作符也返回false,因为按照规则,NaN不等于NaN
d:如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true,否则,返回false
break/continue/ return语句
break用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到break,系统将完全结束循环,开始执行循环之后的代码。break不仅可以结束其所在的循环,还可结束其外层循环。
continue语句会立即退出当前循环,然后继续执行后面的循环,直到循环结束。而break则是完全中止循环。
return关键字并不是专门用于跳出循环的,return的功能是结束一个方法。 一旦在循环体内执行到一个return语句,return语句将会结束该方法,循环自然也随之结束。与continue和break不同的是,return直接结束整个方法,不管这个return处于多少层循环之内。
以下是三段代码来表示三者的区别:
//break
var num = 0;
for(var = 1 ; i < 10 ; i++){
if(i%5 == 0){
break;
}
num++;
}
alert(num); //4
//continue
var num = 0;
for(var = 1 ; i < 10 ; i++){
if(i%5 == 0){
continue;
}
num++;
}
alert(num); //8
//return
function test(i){
if(i == 3){
return
}
console.log(i)
}
for (var i = 0; i < 10 ; i++ ){
test(i)
} // 0 1 2 4 5 6 7 8 9
width语句
width语句在开发中很少会用到,本来不想写它,后来想了想,其特殊的语法形式,也有必要写下来。
width语句的作用是将代码的作用域设置到一个特定的环境中。定义width语句的目的主要为了简化多次编写同一个对象的工作。如下:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
上面代码块多次用到location这个对象,如果使用width语句可以写成如下所示:
width(location){
var qs = serarch.substring(1);
var hostName = hostname;
var url = href;
}
使用width语句,从写法上来说变的复杂了,将简单的取值赋值语句包在了函数写法中。再看一下以下的写法:
width(window){
var qs = window.location.serarch.substring(1);
var hostName = window.location.hostname;
var url = window.location.href;
}
这样,就好理解width语句真正的作用。
从运行环境上来说,width语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的蒂尼,就会查询location对象中是否有同名属性。如果发现了同名属性,则以location对象属性的值作为变量的值。
大量使用width语句会导致性能下降,也会让调试代码变得困难,而且在严格模式下不允许使用width语句,否则将会视为语法错误。
理解函数的参数
在给一个函数传递参数时,我们可以显式的传参,也在不传参的情况下,用arguments这个对象来表示传入到函数中的参数。
函数传参有这么一条潜规则:你可以多传参数,函数体内部可以不用,但是你不可以少传参数,那函数体内部就会报错。
从函数接受参数的情况来看,函数接收到的参数在函数内部始终会用arguments这个对象来表示。arguments是一个类数组的对象,但它并不是Array的实例。你可以用数组的方法访问arguments对象中的每一项,arguments[0],arguments[1]......。所以arguments的长度是由传入的参数个数决定的,没有传入参数就会自动被赋予undefined值。
function test(a,b){
console.log(typeof arguments)
}
test(1,2) //'object'
另外还有一点,arguments的类型是object,这一点别忘了。
最后还有一点,arguments对象内部每一项的值都会与传入的参数一一对应,注意:是每一项的值。也就是说,传入的参数值与arguments中对应的值会保持同步,不过,由于arguments这个对象始终存在并作用于函数体内部,并且读取传入的参数值和读取对应arguments对象内部的值并不是访问相同的内存空间,他们的内存空间是独立的。
函数重载
首先理解什么是函数重载?
函数重载是函数的一种特殊情况,允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的[形式参数](指参数的个数、类型或者顺序)必须不同,也就是说用同一运算符完成不同的运算功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。不能只有函数返回值类型不同。
但是在js中,函数是无法重载的,例如:
function test(num){
return num+100
}
function test(num){
return num+200
}
var result = test(100); //300
不过,由于函数中有arguments这个对象,我们可以根据它的长度,类型或者顺序来实现函数重载。
这里说一个非常典型的案例:
我们现在有这样的一个需求,有一个people对象,里面存着一些人名,如下:
var people = {
values: ["Dean Edwards", "Sam Stephenson", "Alex Russell", "Dean Tom"]
};
我们希望people对象拥有一个find方法,当不传任何参数时,就会把people.values里面的所有元素返回来;当传一个参数时,就把first-name跟这个参数匹配的元素返回来;当传两个参数时,则把first-name和last-name都匹配的才返回来。因为find方法是根据参数的个数不同而执行不同的操作的,所以,我们希望有一个addMethod方法,能够如下的为people添加find的重载:
addMethod(people, "find", function() {}); /*不传参*/
addMethod(people, "find", function(a) {}); /*传一个*/
addMethod(people, "find", function(a, b) {}); /*传两个*/
还有一个问题,就是这个全局的addMethod方法该怎么实现呢?如下所示
function addMethod(object, name, fn) {
var old = object[name]; //把前一次添加的方法存在一个临时变量old里面
object[name] = function() { // 重写了object[name]的方法
// 如果调用object[name]方法时,传入的参数个数跟预期的一致,则直接调用
if(fn.length === arguments.length) {
return fn.apply(this, arguments);
// 否则,判断old是否是函数,如果是,就调用old
} else if(typeof old === "function") {
return old.apply(this, arguments);
}
}
}
现在,我们一起来分析一个这个addMethod函数,它接收3个参数,第一个为要绑定方法的对象,第二个为绑定的方法名称,第三个为需要绑定的方法(一个匿名函数)。函数体的的分析已经在注释里面了。
OK,现在这个addMethod方法已经实现了,我们接下来就实现people.find的重载啦!全部代码如下:
//addMethod
function addMethod(object, name, fn) {
var old = object[name];
object[name] = function() {
if(fn.length === arguments.length) {
return fn.apply(this, arguments);
} else if(typeof old === "function") {
return old.apply(this, arguments);
}
}
}
var people = {
values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
/* 下面开始通过addMethod来实现对people.find方法的重载 */
// 不传参数时,返回peopld.values里面的所有元素
addMethod(people, "find", function() {
return this.values;
});
// 传一个参数时,按first-name的匹配进行返回
addMethod(people, "find", function(firstName) {
var ret = [];
for(var i = 0; i < this.values.length; i++) {
if(this.values[i].indexOf(firstName) === 0) {
ret.push(this.values[i]);
}
}
return ret;
});
// 传两个参数时,返回first-name和last-name都匹配的元素
addMethod(people, "find", function(firstName, lastName) {
var ret = [];
for(var i = 0; i < this.values.length; i++) {
if(this.values[i] === (firstName + " " + lastName)) {
ret.push(this.values[i]);
}
}
return ret;
});
// 测试:
console.log(people.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(people.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(people.find("Dean Edwards")); //["Dean Edwards"]