JavaScript学习小结

通过对JavaScript高级程序设计一书的学习,我对这门脚本语言有了进一步的了解,特写下这篇博文,总结一下自己的收获。

第一、了解了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~
 


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

JavaScript学习小结_第1张图片

在访问对象属性时既然会其原型链进行搜索,那么如何判断一个对象的属性是存在与本地还是存在于原型链中呢?

原来在基础类型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()将数组转换为字符串,将传入的字符串作为分隔符,默认以逗号分割

栈方法

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

队列方法

可将数组看成队列类使用,提供了unshiftshift方法(poppush方法同上)

重排序方法

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

操作方法

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

位置方法

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

迭代方法

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

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

缩小方法

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

【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等。想着等到以后用到的时候再查阅吧。

 

好,就先总结到这里,希望能在梳理的同时也能给你带来点收获,有所帮助。让我们在技术的道路上共勉吧!

 

你可能感兴趣的:(JavaScript学习小结)