第一、了解了JavaScript的背景。
它最初是Netscape公司为了在客户端对表单进行简单验证,减轻对服务器的资源消耗而开发的一种客户端语言。而后出现了多个不同的版本,JavaScript的标准化问题提上了议事日程。1997年,ECMA-262标准诞生,它以JavaScript1.1为蓝本定义了一种名为ECMAScript(发音为“ek-ma-script”)的新脚本语言。
值得注意的是,ECMAScript与Web浏览器没有依赖关系,它提供了核心语言功能。Web浏览器只是ECMAScript实现可能的宿主环境之一。我们现在所说的JavaScript是ECMAScript在Web浏览器环境中扩展后的产物,扩展的内容有两部分,一部分是BOM(Browser Object Model浏览器对象模型,提供了与浏览器交互的方法和接口),另一部分是DOM(Document Object Model文档对象模型,提供了访问和操作网页内容的方法和接口)。
第二,对JavaScript中的数据类型有了比较清晰系统的认识。
常说,JavaScript是宽松数据类型的语言,这里的“宽松”,我理解的就是在预编译时不会对声明的变量进行类型检查,对变量一视同仁(使用function声明的函数对象稍有不同)。但是,在执行过程中对变量进行赋值时,就涉及到了变量的存储,这样就把数据值分成了两类,存储在栈内存中的简单数据类型(也成为“基本数据类型”)和存储在堆内存中的引用类型,简单数据类型按值访问,引用类型按引用访问,这一点和Java是一样的。
【简单数据类型】
简单数据类型有5种:undefined、null、number、boolean、string。
在变量声明但未赋值时,其类型即为undefined。null表示一个空对象指针。另外,得知undefined其实是由null类型派生出来的,在使用“==”对两者进行判断时是返回true的,但使用“===”进行逻辑判断时返回false。两者的相似点是,它们都是只有一个值的类型。区别在于,对一个变量显示赋值为undefined是没有意义的,但显示赋值为null却有意义,表示该变量准备用来存放引用类型。
number类型表示整数和浮点数。比较常用的有这样几个函数,isNaN()和Number()、parseInt()、parseFloat()。
isNaN()是用来判断是参数值是不是非数字(is Not a Number ?),如果参数值不能转换为数值,那么返回true,返回数字则表示参数值是数字或者能够转换为数值。后三个函数都是用来进行数值转换的。
boolean类型有true和false两个字面值;但所有类型都可以通过转型函数Boolean()转换为一个Boolean值。对于null和undefined类型,转为false;对于number类型,0和NaN会转为false,其余转为true;对于string,""转为false,非空字符串转为true;对于引用类型,都转为true。知道了这一点,我们就容易理解流控制语句中形如if(num)的做法了,这是因为在进行判断时会先自动执行相应的Boolean()转换。
string类型用于表示由零或多个16为Unicode字符组成的字符序列,即字符串。有三点需要注意:字符串类型采用单引号、双引号表示都行,但前后需要一致;调用字符串的length属性得到的值可能不准确(如果包含转义序列时);比较常用的是两个字符串转换函数toString()和String()。
【引用类型】
引用类型就是指对象,它是一组无序属性的集合,其属性可以包含简单数据类型、对象或者函数。该类型类似于Java中的类。Object类型是引用类型的基础类型,其他所有类型都从Object继承了基本的行为。就连我们熟悉的函数,在JavaScript中也是继承了Object类型。
①对象的属性
对象的属性可以分为两类:数据属性和访问器属性(我们常用的是数据属性)。属性本身的各种特征是由属性的特性的决定的。
下边是一个例子,可以用来更好地理解数据属性和访问器的特性。
//name、_age、sayHi三个属性都是数据属性 var person = { //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(){ return this._age+1; } }); alert(person._age);//在读取数据属性时,会返回其value特性值,结果是12 alert(person.age);//在读取访问器属性时,会调用其get特性指向的函数,结果返回13 //另外,对数据属性进行赋值时,改变的是该数据属性的value特性值;和对访问器属性进行赋值时,会调用其set特性指向的函数 //访问属性的特性调用此方法 var descriptor = Object.getOwnPropertyDescriptor(person,"age"); alert(descriptor.writable);
上边例子的Object.defineProperty可以定义为一个Object.defineProperties 。
var person = {}; Object.defineProperties(person,{ name:{ value:"wang" }, sayHi:{ value:new Function("alert('hi');") }, _age:{ writable:true, value:12 }, age:{ get:function(){ return this._age+1; } } });
我们可以从例子中看出调用Object.defineProperties 函数时,需要两个参数,第一个参数是一个表示对象的变量,第二个参数是一个采用对象字面量形式的对象。
②JavaScript面向对象的设计
特别需要注意的是,其实现方式与Java中的类不同。在没有类的情况下,JavaScript主要是通过构造函数(还有一种创建对象的方式是对象字面量)来创建对象的,是通过原型链来实现类型的继承的。
我觉得,原型链在面向对象的设计中最重要,以下边的代码为例来说明原型链的概念。
function Person(){
this.name = "zhang";
this.age = 1;
}
Person.prototype.sayHi = function(){
alert("hi~");
};
var p1 = new Person();
p1.name = "wang";
var p2 = new Person();
p2.job = "IT";
alert(p1.name);//wang
alert(p2.name);//zhang
p1.sayHi();//hi~
p2.sayHi();//hi~
通过下图可以了解原型链,虚线串连起来的红线就是一条原型链。之所以用虚线,是因为不能通过代码直接访问。
在访问对象属性时既然会其原型链进行搜索,那么如何判断一个对象的属性是存在与本地还是存在于原型链中呢?
原来在基础类型Object的原型对象中存在有hasOwnProperty属性(该属性是一个函数),其他对象都共享了该方法。调用对象的hasOwnProperty方法,将要判断的属性名作为参数传入,就能返回属性是否存在与对象本地。那么怎么判断属性是否存在于原型中呢?in操作符能判断出属性在原型链中是否能找到,无论该属性存在于实例中还是原型中。可以将in操作符与hasOwnProperty函数结合使用,即可判断出属性是否存在于原型中了。
//判断属性是否存在于原型中
function hasPrototypeProperty(object, name){
return (name in object) && (!object.hasOwnProperty(name));
}
理解了原型链,我们就容易理解new操作符的作用了。在例子中有var p1 = new Person();这样的语句使用了new操作符,结合指针关系图,我们可以认为,在执行这行代码时,是先隐式创建了一个空对象this,然后对this对象添加属性,最后再隐式地返回这个this对象给p1。
另外,如果构造函数的代码中已经显式地返回了对象这时用new操作符来调用改构造函数,那么返回的将不是隐式创建的新对象(这个新对象还存在于内存中),而是这个显式的对象。如果构造函数中的代码中显式地返回了简单类型的数据,则没有影响,即还是会将隐式创建的this对象返回的。如将Person()的代码改为
function Person(){
var o = {};
this.name = "zhang";
this.age = 1;
o.name = "li";
return o;
}
再执行var p1 = new Person(); 那么p1.name的值将为"li",而不是"zhang"。内存中会有一个对象,name属性值为"zhang",age属性值为1的对象。特别注意的是,这个时候执行var p1 = new Person(); 与执行var p1 = 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
var expression = / pattern / flags;
if(expression.test(text)){...}
match:使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回
function MatchDemo(){
var s = "The rain in Spain falls mainly in the plain";
var re = /(a)in/ig; // 创建正则表达式模式
var r = 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的参数,如var bindFun = fun.bind(obj); 只有在调用了绑定后返回的函数引用才会执行,即 需要执行代码bindFun();
【三个包装类型Boolean类型、Number类型、String类型】
三个包装类型是和boolean、number、string三个基本类型相对应的。<SPAN style="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进行了赋值,那么执行到此处时,先前的函数定义就会被此处的定义覆盖。
fun();//hi var fun = function(){ alert("hello"); }; function fun(){ alert("hi"); } fun();//hello
作用域该如何理解?
作用域就是变量或函数能被访问的范围。说一个变量或函数拥有全局作用域,意思就是说,它能在全局范围内被访问。JavaScript中作用域只有两种:全局作用域和函数作用域,没有块作用域。
作用域链如何理解?
作用域链是在调用函数时才有的概念。函数在调用时,其内部可能还有对别的函数的调用,如何实现对不同层次的函数体内定义的变量进行有序地访问呢?这就出现了作用域链。
简单地说,不同层次的函数作用域都用一个对象来代表,把这些对象的指针按顺序罗列出来就是作用域链。
在搜索标识符时,就是从作用域的前端向后一级一级单向搜索,如果找到,就停止返回。
在此推荐一篇比较好的文章介绍了JavaScript的作用域和作用域链:http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
第四、对DOM部分有了比较系统的了解,尤其是事件。
DOM(Document Object Model文档对象模型)将HTML文档(或XML文档)描绘成了一个层次化的节点树,并提供了相关的API,这样开发人员就可以通过JavaScript代码来操作这个节点树,从而能改变HTML文档的内容。
我们可以将HTML文档看成数据库,将DOM提供的操作节点树的API看成sql,后者能对前者进行增删查改。
节点是有类型之分的。常用的有元素节点、特性节点、文本节点、文档节点。JavaScript中的所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法(如nodeType、nodeName、nodeValue、parentNode、childNodes等),当然也有各自的特色方法。节点是有关系的,如父子关系、兄弟关系。
每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。特别注意的是,NodeList会在DOM结构发生变化时即时更新。
节点类型 | 相关的属性和方法 |
---|---|
元素节点 | nodeName与tagName属性值相同,在使用时最好将它们再转换为小写形式; attributes属性返回一个动态集合,有getNamedItem/removeNamedItem/setNamedItem/item方法可以配合使用来操作某一个元素节点的特性。不过,对元素节点的特性一般使用getAttribute进行访问,使用setAttribute进行设置(设置时,特性已存在则覆盖,不存在则新建),使用removeAttribute删除某一特性。 使用document.createElement来创建新元素; |
特性节点 | specified是特性一个属性,是一个布尔值,用来区别特性是在代码中指定的,还是默认的。 |
文本节点 | 使用document.createTextNode来创建文本节点; 核心属性是data与nodeValue值相同,它有一系列对应的方法来操作:appendData/deleteData/insertData/replaceData等; normalize和splitText方法分别能将相邻文本节点合并、按照指定位置分割文本。前者是用在元素节点上的,后者是用在文本节点上的。 |
文档节点 | 在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document是window对象的一个属性,因此可以将其作为全局对象来访问。 文档信息如title、head可以直接用document.title和document.head进行访问。 |
事件就是用户或浏览器自身执行的某种动作,JavaScript与HTML的交互是通过事件实现的。
1.事件流——页面中节点接收事件的顺序。两种事件流模型:从子到父的顺序叫“事件冒泡”,从父到子的顺序叫“事件捕获”;
2.事件类型
事件类型 | 具体的事件说明 |
---|---|
UI事件 | load---页面完全加载完在window上面触发; unload--页面完全卸载后在window上面触发; abort--在用户停止下载时,某元素嵌入的内容未加载完,则在该元素上面触发; errror--当JavaScript发生错误时在window上面触发; select--当用户选择文本框中的一或多个字符时触发。 resize--窗口或框架的大小变化时在window或框架上面触发; scroll--用户滚动带滚动条的元素中的内容时在该元素上面触发; |
焦点事件 | blur--在元素失去焦点时触发; focus--在元素获得焦点时触发; |
鼠标与滚轮事件 | click--用户单击鼠标或按下回车键时触发; dbclick--用户双击鼠标时触发;
mousedown--用户按下了任意鼠标按钮时触发; mouseup--在用户释放鼠标按钮时触发;
mouseenter--在鼠标光标从元素外部首次移动到元素范围之内时触发; mouseleave--在位于元素上方的鼠标光标移动到元素范围之外时触发; mousemove--在鼠标指针在元素内部移动时重复地触发; mouseout--在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发(又移入的另一个元素可能是前一个元素的子元素); mouseover--在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发; mousewheel--当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时触发; |
文本与键盘事件 | keydown--用户按下键盘上的任意键时触发; keypress--用户按下键盘上的字符键时触发;(这个有疑问,认为是只按下不释放是不会触发该事件的) keyup--用户释放键盘上的键时触发; textInput--在文本插入文本框之前会触发; |
3.跨浏览器的事件处理程序
sumbit事件与select事件
调用submit方法时不会触发submit事件,从而也不会执行submit事件处理程序。
调用select方法时则会触发select事件,进而会执行select事件处理程序。
handler参数表示一个事件处理程序,是一个函数对象。
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type, handler, false); } else if (element.attachEvent){ element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type, handler, false); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } }
4.事件对象
事件对象包含着所有与事件有关的信息,包含导致事件的元素、事件的类型等。如鼠标事件中鼠标的位置信息、键盘事件中键盘的键码等信息都是包含在event对象中的。
event对象所有浏览器都支持,但支持方式不同。event对象的获取:先试着在事件处理程序中直接获取event对象,如果为空,则返回window.event。
event对象常用的有两个方法:
preventDefault()----取消事件的默认行为,即先前绑定的事件处理程序将不起作用。这个方法调用的前提是将cancelable属性设置为true。
stopPropagation()----取消事件的进一步捕获或冒泡,即不再执行父节点的事件处理程序。
第五、对JavaScript与XML、JSON两种格式的数据之间的配合使用有了进一步的认识。
这块内容我已经总结到了自己的一篇博文中了。http://blog.csdn.net/wangchenggong1988/article/details/8099714
第六、对原生的AJAX有了更明确的认识。
Ajax是无需刷新页面就能够从服务器取得数据的一种方法。负责Ajax运作的核心对象是XMLHttpRequest(简称XHR)对象。
XHR对象的跨浏览器创建:
function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ if (typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i, len; for (i=0,len=versions.length; i < len; i++){ try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex){ //skip } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } }
XHR对象的用法:
常用的当然是使用Ajax异步发送请求了。
var xhr = createXHR(); xhr.onreadystatechange = function(event){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("get", "example.txt", true); xhr.send(null);
特别注意的是,调用open()方法并不会真正发送请求,而只是启动一个请求以备发送。只有在调用了send()方法后才发送请求。
XHR的readyState属性值表示请求/响应过程的当前活动阶段代号(4表示已经接收全部响应数据),它的值每发生一次变化时就会触发一次readystatechange事件。
请求进入后台后,服务器响应的信息都会自动填充到XHR对象的responseText/responseXML属性中。
【HTTP头部信息】
可以使用XHR获取和设置HTTP头部信息,方法有setRequestHeader()、getResponseHeader()、getAllResponseHeaders()。
可以在后台调用HttpServletRequest对象获取你自定义的头部字段。
第七、对一些没接触的东西有了初步的了解。
1.对JavaScript的垃圾收集机制的认识:垃圾收集器自动找出那些不再使用的变量,释放其内存。
2.了解了BOM部分的核心对象window的一些属性(location、navigator、screen、history)和方法。
3.了解了客户端检测和错误处理调试。用户代理检测就是根据navigator对象的userAgent属性值来解析判断客户所使用的浏览器的类型的。
4.对DOM的扩展和HTML脚本编程有了些许了解。
5.对表单脚本又熟悉了一些。
6.对Web Works做了了解。
还有一些东西没有去做了解,如Canvas绘图、E4X、Commet等。想着等到以后用到的时候再查阅吧。
好,就先总结到这里,希望能在梳理的同时也能给你带来点收获,有所帮助。让我们在技术的道路上共勉吧!