您的位置:首页 > Web前端 > JavaScript

JavaScript学习小结

2012-11-27 10:18 281 查看
通过对JavaScript高级程序设计一书的学习,我对这门脚本语言有了进一步的了解,特写下这几篇博文,总结一下自己的收获。

第一、了解了JavaScript的背景。

它最初是Netscape公司为了在客户端对表单进行简单验证,减轻对服务器的资源消耗而开发的一种客户端语言。而后出现了多个不同的版本,JavaScript的标准化问题提上了议事日程。1997年,ECMA-262标准诞生,它以JavaScript1.1为蓝本定义了一种名为ECMAScript(发音为“ek-ma-script”)的新脚本语言。

值得注意的是,ECMAScript与Web浏览器没有依赖关系,它提供了核心语言功能。Web浏览器只是ECMAScript实现可能的宿主环境之一。我们现在所说的JavaScript是ECMAScript在Web浏览器环境中扩展后的产物,扩展的内容有两部分,一部分是BOM(BrowserObjectModel浏览器对象模型,提供了与浏览器交互的方法和接口),另一部分是DOM(Document
ObjectModel文档对象模型,提供了访问和操作网页内容的方法和接口)。

第二,对JavaScript中的数据类型有了比较清晰系统的认识。

常说,JavaScript是宽松数据类型的语言,这里的“宽松”,我理解的就是在预编译时不会对声明的变量进行类型检查,对变量一视同仁(使用function声明的函数对象稍有不同)。但是,在执行过程中对变量进行赋值时,就涉及到了变量的存储,这样就把数据值分成了两类,存储在栈内存中的简单数据类型(也成为“基本数据类型”)和存储在堆内存中的引用类型,简单数据类型按值访问,引用类型按引用访问,这一点和Java是一样的。

【简单数据类型】

简单数据类型有5种:undefined、null、number、boolean、string。

在变量声明但未赋值时,其类型即为undefined。null表示一个空对象指针。另外,得知undefined其实是由null类型派生出来的,在使用“==”对两者进行判断时是返回true的,但使用“===”进行逻辑判断时返回false。两者的相似点是,它们都是只有一个值的类型。区别在于,对一个变量显示赋值为undefined是没有意义的,但显示赋值为null却有意义,表示该变量准备用来存放引用类型。

只能用===运算来测试某个值是否是未定义的,因为==运算符认为undefined值等价于null。注释:null表示无值,而undefined表示一个未声明的变量,或已声明但没有赋值的变量,或一个并不存在的对象属性。

number类型表示整数和浮点数。比较常用的有这样几个函数,isNaN()和Number()、parseInt()、parseFloat()。

isNaN()是用来判断是参数值是不是非数字(isNotaNumber?),如果参数值不能转换为数值,那么返回true,返回数字则表示参数值是数字或者能够转换为数值。后三个函数都是用来进行数值转换的。

创建Boolean对象的语法:

newBoolean(value);	//构造函数
Boolean(value);		//转换函数

参数

参数value由布尔对象存放的值或者要转换成布尔值的值。

返回值

当作为一个构造函数(带有运算符new)调用时,Boolean()将把它的参数转换成一个布尔值,并且返回一个包含该值的Boolean对象。

如果作为一个函数(不带有运算符new)调用时,Boolean()只将把它的参数转换成一个原始的布尔值,并且返回这个值。

注释:如果省略value参数,或者设置为0、-0、null、""、false、undefined或NaN,则该对象设置为false。否则设置为true(即使value参数是字符串"false")。

string类型用于表示由零或多个16为Unicode字符组成的字符序列,即字符串。有三点需要注意:字符串类型采用单引号、双引号表示都行,但前后需要一致;调用字符串的length属性得到的值可能不准确(如果包含转义序列时);比较常用的是两个字符串转换函数toString()和String()。

【引用类型】

引用类型就是指对象,它是一组无序属性的集合,其属性可以包含简单数据类型、对象或者函数。该类型类似于Java中的类。Object类型是引用类型的基础类型,其他所有类型都从Object继承了基本的行为。就连我们熟悉的函数,在JavaScript中也是继承了Object类型。

①对象的属性

对象的属性可以分为两类:数据属性和访问器属性(我们常用的是数据属性)。属性本身的各种特征是由属性的特性的决定的。



下边是一个例子,可以用来更好地理解数据属性和访问器的特性。

[javascript]
viewplaincopyprint?

//name、_age、sayHi三个属性都是数据属性
varperson={
//name和sayHi属性的configurable、enumerable、writable三个特性值为true
name:"wang",
sayHi:function(){

alert("hi");
}
};

Object.defineProperty(person,"_age",{
//该属性的configurable和enumerable两特性值为false
writable:true,
value:12
});

//age属性是访问器属性
Object.defineProperty(person,"age",{
//该属性的configurable和enumerable两特性值为false,set特性值为undefined
get:function(){
returnthis._age+1;
}

});

alert(person._age);//在读取数据属性时,会返回其value特性值,结果是12
alert(person.age);//在读取访问器属性时,会调用其get特性指向的函数,结果返回13

//另外,对数据属性进行赋值时,改变的是该数据属性的value特性值;和对访问器属性进行赋值时,会调用其set特性指向的函数

//访问属性的特性调用此方法
vardescriptor=Object.getOwnPropertyDescriptor(person,"age");
alert(descriptor.writable);

//name、_age、sayHi三个属性都是数据属性
varperson={
//name和sayHi属性的configurable、enumerable、writable三个特性值为true
name:"wang",
sayHi:function(){
alert("hi");
}
};

Object.defineProperty(person,"_age",{
//该属性的configurable和enumerable两特性值为false
writable:true,
value:12
});

//age属性是访问器属性
Object.defineProperty(person,"age",{
//该属性的configurable和enumerable两特性值为false,set特性值为undefined
get:function(){
returnthis._age+1;
}

});

alert(person._age);//在读取数据属性时,会返回其value特性值,结果是12
alert(person.age);//在读取访问器属性时,会调用其get特性指向的函数,结果返回13

//另外,对数据属性进行赋值时,改变的是该数据属性的value特性值;和对访问器属性进行赋值时,会调用其set特性指向的函数

//访问属性的特性调用此方法
vardescriptor=Object.getOwnPropertyDescriptor(person,"age");
alert(descriptor.writable);


上边例子的Object.defineProperty可以定义为一个Object.defineProperties。

[javascript]
viewplaincopyprint?

varperson={};

Object.defineProperties(person,{
name:{
value:"wang"
},
sayHi:{
value:newFunction("alert('hi');")
},
_age:{
writable:true,
value:12
},
age:{
get:function(){
returnthis._age+1;
}
}
});

varperson={};
Object.defineProperties(person,{
name:{
value:"wang"
},
sayHi:{
value:newFunction("alert('hi');")
},
_age:{
writable:true,
value:12
},
age:{
get:function(){
returnthis._age+1;
}
}
});


我们可以从例子中看出调用Object.defineProperties函数时,需要两个参数,第一个参数是一个表示对象的变量,第二个参数是一个采用对象字面量形式的对象。

②JavaScript面向对象的设计

特别需要注意的是,其实现方式与Java中的类不同。在没有类的情况下,JavaScript主要是通过构造函数(还有一种创建对象的方式是对象字面量)来创建对象的,是通过原型链来实现类型的继承的。
我觉得,原型链在面向对象的设计中最重要,以下边的代码为例来说明原型链的概念。

functionPerson(){

this.name="zhang";

this.age=1;

}


Person.prototype.sayHi=function(){

alert("hi~");

};


varp1=newPerson();

p1.name="wang";

varp2=newPerson();

p2.job="IT";


alert(p1.name);//wang

alert(p2.name);//zhang


p1.sayHi();//hi~

p2.sayHi();//hi~



通过下图可以了解原型链,虚线串连起来的红线就是一条原型链。之所以用虚线,是因为不能通过代码直接访问。



在访问对象属性时既然会其原型链进行搜索,那么如何判断一个对象的属性是存在与本地还是存在于原型链中呢?
原来在基础类型Object的原型对象中存在有hasOwnProperty属性(该属性是一个函数),其他对象都共享了该方法。调用对象的hasOwnProperty方法,将要判断的属性名作为参数传入,就能返回属性是否存在与对象本地。那么怎么判断属性是否存在于原型中呢?in操作符能判断出属性在原型链中是否能找到,无论该属性存在于实例中还是原型中。可以将in操作符与hasOwnProperty函数结合使用,即可判断出属性是否存在于原型中了。
//判断属性是否存在于原型中

functionhasPrototypeProperty(object,name){

return(nameinobject)&&(!object.hasOwnProperty(name));

}


理解了原型链,我们就容易理解new操作符的作用了。在例子中有varp1=newPerson();这样的语句使用了new操作符,结合指针关系图,我们可以认为,在执行这行代码时,是先隐式创建了一个空对象this,然后对this对象添加属性,最后再隐式地返回这个this对象给p1。

另外,如果构造函数的代码中已经显式地返回了对象这时用new操作符来调用改构造函数,那么返回的将不是隐式创建的新对象(这个新对象还存在于内存中),而是这个显式的对象。如果构造函数中的代码中显式地返回了简单类型的数据,则没有影响,即还是会将隐式创建的this对象返回的。如将Person()的代码改为
functionPerson(){

varo={};

this.name="zhang";

this.age=1;

o.name="li";

returno;

}

再执行varp1=newPerson();那么p1.name的值将为"li",而不是"zhang"。内存中会有一个对象,name属性值为"zhang",age属性值为1的对象。特别注意的是,这个时候执行varp1=newPerson();与执行varp1=Person();两者返回的对象是相同的,但后者会把name属性和age属性添加给全局对象window。

③几个典型的引用类型
继承自Object类型的,比较典型的有这么几个类型。
【Array类型】
1.首先数组有两种方式,使用构造函数和数组字面量(使用中括号)。
2.JavaScript中数组的length属性和java中不同,是可以对其进行赋值的,并且没有数组下标越界的概念。
3.Array类型提供了一系列方法

Array类型的方法

方法类别
具体的方法说明
转换方法

join()将数组转换为字符串,将传入的字符串作为分隔符,默认以逗号分割

栈方法

可将数组看成栈来使用,提供了push和pop方法

队列方法

可将数组看成队列类使用,提供了unshift和shift方法(pop和push方法同上)

重排序方法

反转数组项的reverse方法、对数组项进行排序的sort方法(可传入排序策略)

操作方法

并入新数组项的concat方法、截取掉部分数组项的slice方法、可在指定位置插入新数组项的splice方法

位置方法

用于检索目标元素在数组中的索引位置的indexOf方法和lastIndexOf方法

迭代方法

起检测作用的every和some方法,起过滤作用的filter方法和map方法(map方法是将每个数组元素执行后返回的新元素组成一个新数组),起执行作用的forEach方法。

迭代方法都有一个函数作为参数,方法起的作用不同,那么函数的返回值也不同。

缩小方法

reduce和reduceRight方法。迭代数组中的所有项,构建一个最终返回值。

【Date类型】
1.Date类型的构造函数不传参数时,自动获得当前日期和时间;如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数。
2.Date类型有两个重要方法:Date.parse()和Date.UTC(),它们都是用来返回一个表示日期的毫秒数。需要注意的是,这两个方法是属于构造函数这个对象的,而不是Date类型的实例对象,相当于java中类的静态方法。
3.在ECMAScript5中,支持Date.now(),用来返回当前时间的毫秒数。这个方法可以用来分析前台js代码的执行效率。
4.使用toDateString()和toTimeString()能以特定于的格式显示日期和时间。
5.Date类型还有一些方法可以直接获取和设置日期值中特定的部分。

【RegExp类型】
该类型用来支持正则表达式的。
1.三种模式:g表示全局模式,i表示不区分大小写,m表示多行模式
2.两种方式用来检测正则表达式。
test:检查字符串是否与给出的正则表达式模式相匹配,如果是则返回true,否则就返回false
varexpression=/pattern/flags;

if(expression.test(text)){...}
match:使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回
functionMatchDemo(){

vars="TheraininSpainfallsmainlyintheplain";

varre=/(a)in/ig;//创建正则表达式模式

varr=s.match(re);//尝试去匹配搜索字符串

document.write(r);//输出结果:ain,ain,ain,ain

}


【Function类型】
1.函数实际是对象,属于引用类型,函数名是指针(引用)。
2.函数可以用任意多个参数来调用,如果参数不够,那么缺少的参数就用undefined代替。
3.函数声明提升:解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可用。
4.函数的两个内部属性:this表示函数据以执行的环境对象,arguments表示函数实际参数的类数组对象。它本身又有自己的属性,length属性表示参数的数量,callee属性表示当前的函数(这一属性常用与递归)。
5.函数的属性:caller属性只能在函数内部调用,表示调用本函数的函数,在全局作用域中调用该函数会返回null;另外一个重要的属性是prototype属性,该属性指向函数的原型对象。在函数对象创建时该属性就会自动指向原型对象。
6.函数的方法:apply()、call(),这两个方法都是用来设置函数内部属性this从而扩展函数作用域的(让函数在指定的作用域中执行),只不过apply()扩展函数作用域时是以(类)数组方式接受函数的参数,而call()扩展函数作用域时需要将函数参数一一列举出来传递。另外,在ECMAScript5中还定义了bind()方法,它与前边两者的区别是它只是将函数的this值绑定到传给bind的参数,如varbindFun
=fun.bind(obj);只有在调用了绑定后返回的函数引用才会执行,即需要执行代码bindFun();
【三个包装类型Boolean类型、Number类型、String类型】
三个包装类型是和boolean、number、string三个基本类型相对应的。<SPANstyle="FONT-SIZE:18px">就像java中的int与Integer之间的关系。不过最大的区别是,隐式地创建了相应的包装类型。只是寿命很短,只存在于一行代码的执行瞬间(这个瞬间我们可以使用简单类型的字符串、数字,直接调用其包装类型的方法),然后立即就被销毁了。Boolean类型没啥用。
String类型有哪些方法呢?有字符方法(charAt/charCodeAt)、字符串操作方法(substring/slice/concat/trim)、字符串位置方法(indexOf/lastIndexOf)、大小写转换、模式匹配方法等
Number类型有哪些方法呢?有toFixed方法用来对小数进行四舍五入,有toExponential()方法进行指数表示,有toPrecision()方法来保留指定数目的有效数字。
【两个单体内置对象Global对象和Math对象】在所有的代码执行之前,作用域中就已经存在两个内置对象:Global和Math。在大多数ECMAScript的实现中都不能直接访问Global对象,不过在JavaScript中window对象担任有此角色。Math对象提供了很多属性和方法,辅助完成复杂的数学计算任务。在使用IE浏览器进行测试时,发现了ie中Global对象不是Object类型,而是DispHTMLWindow2的,感到奇怪,先存疑吧。
④类型识别问题
简单地说来,对于简单类型的判断可以使用typeof操作符,对于引用类型的判断,可以使用instanceof操作符来判断。http://blog.csdn.net/wangchenggong1988/article/details/8131579

第三、对JavaScript的预编译、作用域、闭包等概念有了初步的认识。
JavaScript代码的执行是有预编译和执行两个阶段的。在预编译阶段,对于使用var声明的变量都先赋以undefined,对于使用function关键字声明的函数对象,则是怎么声明,就怎么定义。在执行阶段,再对变量进行赋值。如果某行代码对已经使用function关键字声明过函数对象又使用var进行了赋值,那么执行到此处时,先前的函数定义就会被此处的定义覆盖。

[javascript]
viewplaincopyprint?

fun();//hi

varfun=function(){
alert("hello");
};

functionfun(){
alert("hi");
}
fun();//hello

	fun();//hi
varfun=function(){
alert("hello");
};

functionfun(){
alert("hi");
}
fun();//hello


作用域该如何理解?
作用域就是变量或函数能被访问的范围。说一个变量或函数拥有全局作用域,意思就是说,它能在全局范围内被访问。JavaScript中作用域只有两种:全局作用域和函数作用域,没有块作用域。
作用域链如何理解?
作用域链是在调用函数时才有的概念。函数在调用时,其内部可能还有对别的函数的调用,如何实现对不同层次的函数体内定义的变量进行有序地访问呢?这就出现了作用域链。
简单地说,不同层次的函数作用域都用一个对象来代表,把这些对象的指针按顺序罗列出来就是作用域链。
在搜索标识符时,就是从作用域的前端向后一级一级单向搜索,如果找到,就停止返回。

在此推荐一篇比较好的文章介绍了JavaScript的作用域和作用域链:/article/1210737.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: