对之前看
《JavaScript高级程序设计》
时没有注意到的一些知识点,结合本书做以补充
语法
注释
源于PL/I的/* */
型既可以出现在字符串字面量
中,也可能出现在正则表达式字面量
中,如
/*
var a = /a*/.match(s);
*/
故一般建议使用//
型注释
保留字
语句、变量、参数、属性名、运算符
和标记
等标识符
不允许使用保留字,此外在对象字面量中,或用点运算提取对象属性时,也都不能用保留字做属性名
数字
JavaScript只有一种数字类型,即64位
浮点数
字符串
1.JavaScript中所有的字符都是16位
的
2.转义字符\
可以将反斜线
,引号
,和控制字
符等不被允许的字符插入到字符串中
3.\u
用来约定指定数字字符编码"A"==="\u0041"
4.字符串一旦被创建就无法被改变,只能通过+
等操作符创建新字符串
语句
1.JavaScript中的代码块{}
不会创建新的作用域,故变量应该定义在函数的头部,而不是代码块中
2.被当做假的值false
,null
,' '
,0
,NaN
3.JavaScript不允许在return
关键字和表达式之间换行,也不允许在break
关键字和标签之间换行
对象
定义
JavaCript中的对象是可变
的键控
集合,是属性的容器,属性名可以是包括空字符串
在内的任意字符串,属性值是除undefined
值之外的任何值
对象字面量
一个对象字面量就是包围在一对花括号中的零个或多个名/值
对,其中若 属性
是合法的JavaScript标识符(字母开头,加数字、下划线组成)且不是保留字,则并不强制要求加引号
对象值得检索
属性名为非合法的JavaScript标识符,需用[]
来读值,为合法的标识符且不是保留字,则也可以用.
来读值,通常更倾向于.
,因为其效率更高更紧凑,尝试从undefined
的成员属性中读值会导致TypeError
异常,如
flight.equipment // undefined
flight.equipment.model // throw "TypeErrpr"
引用
对象通过引用传递,不会被传递,换句话说var a = {}; b = a;
b则和a指向同一块内存
原型
何为委托
?沿原型链查找的过程就是委托(ps:原型链的末端是Object.prototype)
反射
获取对象上的属性而不要原型中的属性,使用hasOwnProperty()
检查,若为对象实例上的属性,则会返回true
枚举
for in
可用来遍历一个对象上的所有属性名(包括实例和原型),但需注意的是for in
遍历出来的属性是无序的
删除
delete
无法删除原型中的属性
减少全局变量污染
措施:声明一个全局变量
,作为命名空间
,然后将应用资源都放到这个命名空间中,可以有效的减少与其他程序,组件,类库的冲突
函数
函数对象
Js中函数就是对象,连接到Function.prototype
上,而Function.prototype
则连接到Object.prototype
上
函数字面量
函数字面量包含以下4
个部分:保留字
:function函数名
:可省略,此外函数还可以通过函数名来进行递归调用形参
:函数中的形参不会被如普通变量一样被初始化为undefined
,而是在函数被调用的时候被初始化为实参所提供的值语句
:包围在花括号中的一组语句被称为函数的主体
调用
在一个函数中调用另一个函数,会暂停对当前函数的执行,并将控制权和参数传递给新函数,此外每个函数还会接收两个附加参数this
和arguments
(其中this
的值取决于函数的调用模式)
函数的调用模式主要存在以下4
种:
1.方法调用模式:函数被保存为一个对象的属性时,当此函数被调用时this
即指向该对象
2.函数调用模式:当函数并非对象的属性,就被当做一个函数调用时,this
会被绑定到全局对象上,但这种设计在实际开发中会造成很多的不便,因此我们常会再声明一个变量并将其赋值为this
,如下:
var obj = {name:'aleen'};
obj.sayName = function(){
var that = this;
var say = function(){
alert(that.name);
}
say();
}
obj.sayName();
3.构造器调用模式:使用new调用构造函数时,会创建一个连接到构造函数prototype成员的新对象,并将函数中的this
绑定到新对象上
4.Apply调用模式:因为Js中的函数也是对象,所以函数也拥有方法,任何函数在调用时使用Apply方法,可以改变函数运行时this
的运行对象(apply的第二个参数可传入一个参数数组
)
var obj = {age:21};
var sayName = function(){alert(this.age)}
sayName.apply(obj);
参数
arguments
:在函数内部可以通过该变量访问所有在函数运行时传递给函数的实参(包括没有被形参接收的参数),但是注意arguments
只是一个类数组的对象,拥有length属性,但并没有任何数组方法
返回
一个函数总会返回一个值,如果没指定值,则返回undefined
,注意当函数在调用时前面加上了new
并且返回值不是一个对象,则返回新对象即this
异常处理
Js中可用throw
来抛出异常
isNum = function(num){
if(typeof num != 'number'){
throw {
name:'type error',
message:'there needs a number'
}
}
}
try{
isNum('a');
}catch(e){
document.writeln(e.name+';'+e.message);
}
扩充类型的功能
即使用prototype
在原型上扩充实例可以共享的方法,但有一点需注意的是——基本类型的原型是公用结构,所以在类库混用时需小心使用,保险的做法是只在确定没有改方法时再添加它
递归
注意一些语言为尾递归提供了优化,可以显著提高速度,但Js并没有提供尾递归的优化,深度递归可能会因为堆栈溢出而运行失败
尾递归示例:
var factorial = function factorial(i,a){
a = a||1;
if(i<2){
return a;
}
return factorial(i-1,a*i);
};
console.log(factorial(4));
作用域
没有块级作用域(ES6中已有块级作用域了),故最好在函数顶部声明所有函数体中可能用到的变量
闭包
一个函数可以访问它被创建时的上下文环境,即成为闭包
此外:要避免在循环中创建函数
回调
回调函数是异步处理的,故不会因为网络传输或服务器响应慢而导致客户端出现假死状态
模块
简而言之:模块是一个提供接口却隐藏状态和实现的函数或对象
一般形式:定义一个拥有私有变量和特权函数(特权函数利用闭包特性可以访问私有变量)的函数,最后返回这个特权函数或者将其保存到一个可访问到的地方
级联
级联使得我们可以单独在一条语句中依次调用同一个对象的很多方法
柯里化
简单来说,柯里化就是将函数与传递给它的参数相结合产生新的函数的过程
function add(a,b){
return a+b;
}
Function.prototype.curry = function(){
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function(){
return that.apply(null,args.concat(slice.apply(arguments)));
};
}
var add1 = add.curry(1);
console.log(add1(6));
记忆
例如斐波那契
var fibonacci = function(n){
return n < 2 ? n : fibonacci(n-1)+fibonacci(n-1)
}
频繁递归调用会消耗极大的资源,因此可以采用记忆策略,将已经计算过的值缓存起来
var fibonacci = function(){
var meno = [0,1];
var lib = function(n){
var result = meno[n];
if(typeof result !== 'number'){
result = fib(n-1)+fib(n-2);
meno[n]=result;
}
return result;
}
return fib;
}();
数组
JavaScript中的数组是一种类似于数组的对象,它将数组的下标转变成
字符串
,并用其作为属性数组字面量
数组字面量var num = [1,2,3];
和对象字面量var num_obj = {'0':1,'1':2,'2',3}
基本一致都是包含3个属性的对象,但它们的原型不同,num
的原型是Array.prototype
而num_obj
的原型是Object.prototype
,故 num
有length
属性而num_obj
没有
ps
:不同于强类型的语言,Js中的数组元素可以是不同类型的值
长度
1.Js中的length
属性是没有上界的,当使用大于或等于length
的位置来存储元素时,不会发生数组越界错误,而会自动增加length
的值以容纳新元素
2.length
属性的值不一定等于数组里的属性个数,如
myArray[100]=true;
myArray.length //101
//数组中只包含一个属性
3.[]
下标运算符会将所含表达式转换为一个字符串
,当表达式有`toString
方法时,就使用该方法的值
4.JavaScript中的数组下标必须是大于等于0并小于等于2的32次方-1的整数
5.设置更大的length
不会给数组分配更多的空间,而把length
设小将会导致所有下标大于等于新length
的属性会被删除
删除
因为数组也是对象,所以可以用delete来删除数组中的元素,但它只会将那块的值移除掉,而保留那块的空间,故那块的值就变成undefined
了,因此常用splice
方法来删除元素,但此方法的特点在 删除元素时是将被删除的元素之后的元素全部移除,再不断添加到新位置,这种做法对于大型数组来说效率不高
枚举
数组可用for in
来枚举元素,但它不会保证数组元素的顺序,因此我们常用在枚举时我们常用for
循环
数组和对象的判断
由于在Js中,数组和对象极易混淆,所以一个较好的判断其类型的方法是:
var is_Array = function (value){
return Object.prototype.toString.apply(value).slice(8,-1) === 'Array';
}
指定初始值
Js不会给数组元素预设初值,[]
得到的新数组为空,访问一个不存在的元素则是undefined
代码风格
1.代码块内容和对象字面量缩进4个空格
2.if和()之间放置一个空格
3.每个逗号和冒号后面都使用一个空格
4.在if和while这样的结构化语句里,始终使用代码块
5.将{放在一行的结尾,而不是第二行的开头
6.尽量使程序具备自我说明,并添加必要注释,推荐行注释
7.尽量在每个函数开始的地方,声明所有变量
8.避免switch语句块的条件穿越到下一条case
优美的特性
毒瘤
全局变量
代码块中没有块级作用域
自动插入分号,故应该在将{放在上一行的尾部,而不是下一行的头部
保留字,Js中的保留字不能用来做变量名和参数,当保留字被用作对象字面量的键值时,必须用
''
括起来,并用[]
引用