一 闭包
各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
上一段代码中的f2函数,就是闭包。
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中:
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
二 JS中函数声明与函数表达式的不同
Js中的函数声明是指下面的形式:function functionName(){}
这样的方式来声明一个函数,而函数表达式则是类似表达式那样来声明一个函数,如:var functionName = function(){}
看到这两一种写法时会产生疑惑,这两种写法差不多,在应用中貌似也都是可行的,那他们有什么差别呢?
事实上,js的解析器对函数声明与函数表达式并不是一视同仁地对待的。对于函数声明,js解析器会优先读取,
确保在所有代码执行之前声明已经被解析,而函数表达式,如同定义其它基本类型的变量一样,
只在执行到某一句时也会对其进行解析,所以在实际中,它们还是会有差异的,具体表现在,
当使用函数声明的形式来定义函数时,可将调用方式在函数声明之后,而后者,这样做会报错。
三 数值
NaN“非数值”,具有毒性,bug。typeof类型是number(这是typeof操作符最奇怪的行为之一)。
.1+.2 结果为0.30000000000000004。
利用Math.round()方法获得两位数的精度。
Math.round((.1+0.2)*100)/100 结果为0.3;
"sdxf".indexOf("x")在sdxf中出现的位置:3
"sdxf".charAt(4)在sdxf中的第4位字母是:f
类型比较:1=="1"返回true,1==="1"返回false。
变量:i=0,隐式声明,隐式声明的变量总是具有全局作用域的,即使该变量是在某个函数函数体中声明。(建议用var关键字声明,而且建议在作用域的顶部声明)
var i=0;显式声明。
var i;未初始化,包含特定的值undefined。
四 对象
对象:对象是属性的集合,每一个属性具有一个名称和一个值。属性可以包含除undefined外的任何类型,在创建对象之后可以对属性赋值。
实例化对象,第一种方法使用new关键字:
var myObject=new Object();
new 关键字将调用构造函数,或者更通用的说法是构造器。构造器将初始化新创建的对象。
构造函数:
function cName(name){
alert(name);
}
var ccname=new cName("构造函数");
实例化对象,第二种方法使用字面量(这种方式创建对象更像其他语言中的散列【hash】或关联数组):
var myObject={};
var domArr={
"name":"构造函数2",
"age":myObject
}
var nm=domArr["name"];//可以用.,如下
var ag=domArr.age;
alert(nm);
alert(ag);
因为属性也可以是对象,可以在任意层上嵌套对象:
var tem={ "name":{"bbo":{"uu":"h", "dd":"i"}}};
alert(tem.name.bbo.uu) 结果为 h。
javascript是一种动态语言,因此要更新一个属性,只需要重新对属性值赋值即可。要移除一个属性,只需使用delete操作符,删除不存在的属性不存在危险。
javascript使用原型继承,对象直接从其他对象继承,从而创建新的对象。简而言之,对象继承另外一个对象中的属性。因为javascript中没有类。
例如,人,javascript中是没有人这个类的,而是张三继承了李四这个人的特征【属性】(眼睛,鼻子,年龄等),形成了新的对象“张三”。
在创建新对象时,注意对象的初始化:
var object1=object2={}//引用的是同一个对象。
var object1={},object2={}//引用的不是同一个对象。
五 函数、闭包、变量
我们认为尽量避免使用全局变量,但有些情况是必须的,所以我们会采取一些办法避免扰乱全局名称空间。
一种办法是使用单个全局变量作为顶层对象,包含程序或框架中的所有方法和属性。安装惯例,命名空间都采用全部字母大写,但值得注意的是常量也用大写格式。
BBNN_PPO={};
BBNN_PPO.Clem={
"name":"agele",
"age":"5"
}
BBNN_PPO.Dem={
"name":"Cdiny",
"age":"4.5"
}
另外一种是采用闭包:
函数:函数是一个代码块,他封装了一系列的javascript语句。函数可以接收参数,也可以返回值。
与c#和java不同,javascript中的函数是第一类(first-class)的对象。
返回值,接参数:
function calc(x){
return x*10;
}
alert(calc(30));
结果300。
如果函数没有返回一个特定的值,则它将返回一个undefined的值:
var x=2;
function calc()
{
x=x*10;
}
alert(x);//结果为20
alert(calc());//结果为 undefined
函数赋值给一个变量:
var calc=function(x)
{
return x*2;
}
alert(calc(5));//结果为 10。
给函数名使用圆括号,将执行该函数并返回函数的值,而不是返回对该函数的引用。
将函数作为参数传递给其他函数:
function demo(fn){
alert("这是接收来的:"+fn());
}
demo(function(){ return "大哥哥"});
//结果为“这是接收来的:大哥哥”
二式:
function demo(fn)
{
alert("这是接收来的:"+fn());
}
function calc()
{
return 2*3;
}
demo(calc);
//结果为“这是接收来的:6”
匿名函数:一个特别重要的变体就是立即调用函数表达式,或称为自执行匿名函数。
这一模式的本质是将一个函数表达式包装在一对括号中,然后立即调用该函数。
这一技巧非常简单,将函数表达式包装在一对圆括号中,将迫使javascript引擎将function(){}块识别为一个函数表达式,而不是一个函数语句的开始。
(function(x,y){
alert(x+y);
})(5,6);//结果为 11,最后这圆括号(5,6),可以写在前面个圆括号内
由于这一的函数表达式将被立即调用,因此该模式用于确保代码块的“执行上下文”按照预的期效果执行,这是这种模式最重要的用途之一。
通过将参数传入函数,在执行时就可以捕获函数所创建的闭包中的变量。
闭包就是这样一个函数:它处在一个函数体中,并应用了函数体当前“执行上下文”中的变量。
闭包的应用,及函数中将另外一个函数作为返回值:
var x=24;
alert(x);结果为:24
var message=(function(x){
return function(){//函数中另外一个函数作为返回值
alert("闭包中的x是:"+x);//结果是:闭包中的x是:24
}
})(x);
message();//没结果
x=12;
alert(x);//结果为:12
message();//结果是:"闭包中的x是:24",无论外部作用域x的值发生什么变化,闭包将记住函数执行时变量x的值。
“执行上下文”:它是一种对象,代码在“执行上下文”环境中执行。可以通过this关键字来访问“执行上下文”,this关键字是对当前“执行上下文”对象的引用。
若代码不存在于用户自定义对象或函数中,那么它将属于“全局上下文”。
在javascript中,没有定义具体“上下文”的函数,将把它们的this关键词的值绑定为undefined值。
alert(this);//[object DOMWindow]
eval函数setTimeout函数具有自己独立的“执行上下文”。
在开始执行一个函数或方法时,javascript创建了一个新的“执行上下文”并进入该函数的“执行上下文”,当函数执行完毕并返回时,控制权将交给原来的“执行上下文”。
在创建闭包时当前“上下文”将保留下来,否则当前“上下文”将被当着垃圾收集。
作用域和闭包:
讨论作用域时,考虑定义变量的位置和变量的生存期非常重要。作用域是指在什么地方可以使用该变量。
在javascript中,作用域维持在函数级别,而不是块级别。因此,参数以及使用var关键字定义的变量,仅在当前函数中可见。
嵌套函数:除了不能访问this关键字和参数之外,嵌套函数可以访问外部函数中定义的变量。这一机制是通过闭包来实现的,它指的是:即使在外部函数解释执行之后,内部嵌套函数继续保持它对外部函数的引用。闭包还有助于简绍名称空间的冲突。
每次调用一个包裹(enclosed)的函数时,虽然函数的代码并每月改变,但是javascript将为每一次调用创建一个新的作用域。
function getCom(value){
return function(value){
return value;
}
}
var a=getCom(),b=getCom(),c=getCom();
alert(a(0));//结果为 0 ,每一次调用都创建新的作用域
alert(b(1));//结果为 1
alert(c(2));//结果为 2
alert(a===b);//false;
});
当定义一个独立函数(没有绑定任何对象)时,this关键字绑定与全局名称空间。
作为一个最直接的结果,当在一个方法内创建一个函数时,内部函数的关键字this将绑定与全局名称空间,而不是绑定该方法。
为了解决这个问题,可以将包裹方法的this关键字简单地赋值给一个名为that的中间变量。
var obj={};
obj.mathod=function(){
var that=this;
this.counter=0;
var count=function(){
that.counter+=1;
alert(that.counter);//上层函数定义的值传不进来
}
count();//结果为 1;
count();//结果为 2;
alert(this.counter);//当没有调用到内部函数时,值为0;现在值为2
}
obj.mathod();
(个人理解:一个小偷,一个叫花子,都想吃到食物(内心世界=内部函数),小偷进入主人家(家=外部函数),但却找不到食物(食物=外部函数变量),叫花子侧在家门口(外部函数之外)乞讨,却得到了食物(函数变量),所以小偷想获得食物必须迂回到外部方能求得食物)
在javascript中并没有官方的访问级别语法,javascript没有类似于java语言中的private或protected这样的访问级别关键字。默认情况下,对象中的成员都是共有和可访问的。但在javascript中可以实现与私有或公有属性类似的访问级别效果。要实现私有方法或属性,请使用闭包:
function reTun(){
var chaer="狠狠不如意";//私有
this.oo=function(){//闭包
return chaer;//公有
}
}
var dem=new reTun();
alert(dem.chaer);//undefined
alert(dem.oo());//狠狠不如意
this.oo()是函数reTun的专有方法,它可以访问reTun中的私有成员。另外chaer变量是“私有”的,他的值只能通过专有方法进行访问
使用模块:与私有和专有访问级别类似,在javascript中没有内置的包语法。模块模式是一种常用的编码模式
TTMMOON={};// 创建名称空间
TTMMOON.teme=(function(){//函数表达式
var destion="",molde="",fulle="";//私有成员
//公有访问方法
return{
//设置器
setDestion:function(dest){
this.destion=dest;
},
setModl:function(mold){
this.molde=mold;
},
setFulle:function(full){
this.fulle=full;
},
//访问器
getDestion:function(){
return this.destion;
},
getModl:function(){
return this.molde;
},
getFulle:function(){
return this.fulle;
},
//其他成员
toString:function(){
alert(this.getModl()+"-嗨,中国"+this.getFulle()+"-谢谢您们!"+this.getDestion());
}
//在这里执行初始化过程
}
}());
var myTimeus=TTMMOON.teme;
myTimeus.setModl("我叫joy,");
myTimeus.setFulle(",我很开心");
myTimeus.setDestion("我会回来的。");
myTimeus.toString();
});
六、数据类型、javascript实践
扩展类型:
javascript支持将方法和其他属性绑定到内置类型,比如字符串、数值和数组类型。与其他任何对象类似,String对象也具有prototype属性。开发人员可以为String对象扩充一些便利的方法。例如,String没有将字符串false或true转换为布尔值的方法。
可以用下面的代码为String对象添加该功能:
String.ptrototype.boolean=function()
{
return "true"==this;
};
var t='true'.boolean();
var f='false'.boolean();
alert(t);//ture
alert(f);//false
简化二:
Function.prototype.mathod=function(name,func)
{
this.prototype[name]=func;
return false;
}
String.mathod("boolean",function(){
return "true"==this;
})
"true".boolean();//true
javascript的最佳实践:
1、使用parseInt()函数将字符串转换为整数,但确保总是指定基数。更好的办法是使用一元操作符(+)将字符串转化为数值:
Good:parseInt("010",10);//转化为十进制
Better:+"010";//高效
2、是用等同(===)操作符来比较两个值,避免意料之外的类型转换。
类型比较:1=="1"返回true,1==="1"返回false。
3、在定义对象字面量时,在最后一个值的结尾不要使用逗号:
var obj={
name : "any",
age : "20"
}
4、不要使用with语句。
5、在循环中创建函数应该谨慎小心,这种方式非常低效。
6、eval()函数可以接受一个字符串,并将其视为javascript代码执行。应该限制eval()函数的使用,它很容易在代码中引入各种各样严重的安全问题。
7、使用分号作为语句的结尾,当精简代码时特别有用。
8、应该避免使用全局变量。应该总是使用var关键字来声明变量。将代码包裹在匿名函数中,避免命名冲突,请使用模块来组织代码。
9、对函数名使用首字母大写表示该函数将作为new操作符的构造函数,但请不要对其他任何函数的函数名使用首字母大写。