咖啡加点糖:javascript中该注意的几点
1、JavaScript允许对任意数据类型做比较
第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较。
浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:
Math.abs(1/3- (1-2/3)) <0.0000001;//true
2、JavaScript的设计者希望用null表示一个空的值,而undefined表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。
3、数组Array
3.1、直接给Array的length赋一个新的值会导致Array大小的变化,即数组的length属性是可写的
如果赋一个比length大的值,数组多出的部分用undefined代替
如果通过索引赋值时,索引超过了范围,同样会引起Array大小的变化:
var arr = [1,2,3];
arr[5] ='x';
arr;// arr变为[1, 2, 3, undefined, undefined, 'x']
如果赋一个比length小的值,那么多出的部分就会被截断。(引申:如果赋0,则是清空数组)
但是需要注意一点,通过点属性的方式及 "att.属性 " 来添加内容是不会引起length长度的变化,即js中的length只用来描述索引元素的长度,忽略关联元素
3.2、对于Array,如果不给slice()传递任何参数,它就会从头到尾截取所有元素。利用这一点,我们可以很容易地浅复制一个Array:
var arr = ['A','B','C','D','E','F','G'];
var aCopy = arr.slice();
aCopy;// ['A', 'B', 'C', 'D', 'E', 'F', 'G']
aCopy === arr;// false
3.3、对于数组的concat()方法,习惯上我们认为它接收一个数组参数,其实它可以接收任意个非数组元素和Array(如果是个一维数组,则会进行一次拆分,否则不拆分),然后全部添加到新的Array里:
var arr = ['A','B','C'];
arr.concat(1,2, [3,4]);// ['A', 'B', 'C', 1, 2, 3, 4]
注意:concat对添加的数组只进行一次拆分,如果接收的Array是个多维数组,不会进行拆分
3.4、Array的sort()方法默认把所有元素先转换为String再排序,如果不知道sort()方法的默认排序规则,直接对数字排序,绝对栽进坑里!
4、对象
对象的属性访问可以通过两个方式进行:一种是"对象.属性名",一种是 对象[ '属性名' ]
如果属性名是个合常规的,上边两种方式都可以,但是如果属性名中含有特殊的字符,如“-” 或者 空格 ,则只能通过第二种方式进行访问
测试一个对象是否拥有某一属性,可以用in操作符 或者 hasOwnProperty()方法,in会搜索原型链,检索对象自己的和继承过来的属性,而hasOwnProperty()方法只会检测是否是自己的属性
5、for( item in target)
如果target是一个对象,则item代表的是对象的属性
如果target是一个数组,由于Array也是对象,而它的每个元素的索引被视为对象的属性的一部分,所以item代表的是索引,但是如果不通过Array提供的方法(方法和初始化)向数组中添加元素,你在数组上手动添加的属性不会被认为是索引的,即length是不会发生变化的,但是遍历的时候属性包括了索引和添加上来的内容,如下
var a = ['A','B','C'];
a.name ='Hello';
for(var x in a) {
alert(x);// '0', '1', '2', 'name'
}
a // ['A','B','C']
a.length // 3
6、方法内部有关变量的提升
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,只是提升声明,赋值操作是在代码执行到对应位置的时候才执行
functionfoo(){
var x ='Hello, '+ y;
alert(x);
var y ='Bob';
}
等价于:
functionfoo(){
var y;// 提升变量y的申明
var x ='Hello, '+ y;
alert(x);
y ='Bob';
}
所以在开发中,应严格遵守“在函数内部首先申明所有变量”这一规则
7、作用域
Javascript的变量范围是以函数为基础的,每个函数都有它自己的变量范围,Javascript这一点上表现的很酷,根本不理睬一些无意义的花括弧包起来的范围。
不在任何函数内定义的变量就具有全局作用域
var a = 1 等价于 window.a = 1
如果对象中定义了方法,要保证方法中this指向正确,必须用obj.xxx()的形式调用,否则this会被调用方法时调用该方法的对象改写
方法.apply(obj, [ ])、方法.call(obj, arg1, arg2, ...)可以动态改变方法中的this指向,对于普通方法的调用,我们可以使用 方法.apply(null, [ ])和 方法.call(null,arg1,arg2,...)的方式,比如调用Math.max(3, 5, 4),分别用apply()和call()实现如下:
Math.max.apply(null, [3,5,4]);// 5
Math.max.call(null,3,5,4);// 5
8、闭包
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值(即函数有一个参数来接收变量)并用“创建一个匿名函数并立刻执行”的语法将i的值传入,无论该循环变量后续如何更改,已绑定到函数参数的值不变。
闭包的实质:闭包就是携带状态的函数!是携带状态的函数!是携带状态的函数!并且它的状态可以完全对外隐藏起来。!
9、包装对象
包装对象用new创建:
var n =new Number(123);// 123,生成了新的包装类型
var b =new Boolean(true);// true,生成了新的包装类型
var s =new String('str');// 'str',生成了新的包装类型
虽然包装对象看上去和原来的值一模一样,显示出来也是一模一样,但他们的类型已经变为object了!所以,包装对象和原始值用===比较会返回false!
所以闲的蛋疼也不要使用包装对象!尤其是针对string类型!!!
如果我们在使用Number、Boolean和String时,没有写new,那么Number()、Boolean()和String()被当做普通函数,把任何类型的数据转换为number、boolean和string类型(注意不是其包装类型)
所以闲的蛋疼也不要使用包装对象!尤其是针对string类型!!!
10、字符串型数值快速转变为数值类型,只需在字符串前边加上“+”
var a = '123.4' ;
var b = +a + 100 // b=223.4
11、数值快速取整:数值|0
总结一下,有这么几条规则需要遵守:
不要使用new Number()、new Boolean()、new String()创建包装对象;
用parseInt()或parseFloat()来转换任意类型到number;
用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...};
typeof操作符可以判断出number、boolean、string、function和undefined;
判断Array要使用Array.isArray(arr);
判断null请使用myVar === null;
判断某个全局变量是否存在用typeof window.myVar === 'undefined';
函数内部判断某个变量是否存在用typeof myVar === 'undefined'。
12、
event.currentTarget指向事件所绑定的元素,
而event.target始终指向事件发生时的元素。