JavaScript 回忆录

作者:禅楼望月(http://www.cnblogs.com/yaoyinglong

1. 基本类型和应用类型

image

1.1 复制变量值的不同

值类型的变量在复制变量值后互不影响,因为值类型本身保存的就是等号后面的实实在在的值。所以它将自己的值复制给对方

image

引用类型则不然,因为它保存的是等号后面的Object对象在对内存中的地址。所以当它给别的变量赋值时,也是将自己仅有的Object对象地址赋值过去。所以在赋值以后前后两个变量都保存的是同一个Object对象在对内存中的地址。

image

1.2 传递参数

image

image

所以下面这段代码:

imageimage

而下面这段代码:

image

这是为什么呢?

我们来分析一下这段代码。首先定义了一个person对象,为它动态添加了一个name属性并赋值为“angel”,然后调用changePersonName函数,将person作为实参传递给changePersonName函数,person是引用类型,所以按引用类型的赋值规则是将person对象在内存中的地址传递过去。此时changePersonName函数的形参p的值是person对象在内存中的地址。但是刚进入该函数,p=new Object();这段代码,让p发生了翻天覆地的变化。它将p的值换掉了,原来p存的是person在内存中的地址。它和外部的person都指向这块地址,但是经过这段代码之后,它切断了指向person的指向,指向到了一个新的Object实例在内存中的地址。此时的p和外围的person没有任何关系了。所以改变它不会对外围的person起到任何作用。

1.4 类型检测

typeof:typeof一般只能返回如下几个结果(6种):number,boolean,string,function,object,undefined。

在JavaScript中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回"object"。因此引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。

另外,更重的一点是 instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型。

2. 执行环境和作用域

2.1 执行环境

总所周知,函数能够访问

         ①声明在当前函数作用域“之外”的变量;

         ②全局变量;

         ③声明在函数内部的变量以及;

         ④通过参数传进来的变量;

         ⑤指向“容器对象”的"this"变量。

以上所有这些变量为我们的函数形成了一个“环境”,该“环境”定义了哪些变量和它们的值是可以被当前函数访问的。一部分“环境”是随着函数的定义而定义的,其他一些是函数访问的时候才定义的。

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境中有一个变量对象与之对应,该环境中所有变量和函数都保存在这里。虽然编程是不能使用,但是在解析器在处理数据是会用到。

所有全局变量和函数都作为window对象的属性和方法创建。某个执行环境的所有代码执行完毕,该环境也就被销毁了。

imageimage

作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。(这对理解闭包很重要)

2.2 JS没有块级作用域

JS中的局部块只有函数(function)可以创建,其他语句不能如if(){}、for(){}、with(){}等等即使有大括号,但是在这些大括号里面创建的变量不是它自己的布局变量。而是属于上一级函数的变量。因此就具有了延长作用域链。

image003

3. 闭包

闭包的表现形式:多是在一个函数里面创建另一个函数

image

该内部函数被返回了,但是它在外部被调用的时候任然能访问到包含它的函数的变量。这是因为当函数第一次被调用的时候会创建一个执行环境和相应的作用域链,然后将作用域链赋值给它的一个内部属性(Scope。然后使用this、arguments和其他命名参数的值来初始化函数的活动对象。自己的活动对象句第一位,外部函数的活动对象居第二位,……

当内部函数被外部函数返回时,它的作用域链包含外部函数的活动对象和全局变量对象。更为重要的是虽然外部函数执行完毕,但是它的活动对象也不会被销毁(一般情况下当函数执行完毕后它的局部活动对象就会被销毁),因为返回来的内部函数的作用域链任然在引用这些活动对象。即,当外部函数执行完毕,它的作用域链会被销毁,但是活动对象不会被销毁,直到返回来的内部函数也被销毁后,外部函数的活动对象才会被销毁。

3.1 this

image

image

由于闭包的执行环境是全局的。所以在闭包中访问不到object的作用域链。才造成打印出来的是“The Window”,要避免这种情况即,让闭包能访问到object的作用域链,在匿名函数中申明以变量即可,将this付给这个变量对象。

image

在这里that的作用域链的前端是object。所以它首先访问到的是object的name。

image

注:这里的this指的是在function当做函数来使用的时候,但是当函数被当作构造器使用时,this指的是该对象。

[+]view code
function person(){
    this.name="name";
    this.age="age";
}
person();
alert(name);//打印的结果为:“name”

这是因为person是全局函数,因此在它中的this指的就是包含它的对象,即window。这时,name和age变成为全局变量了。而当person被当作构造函数来使用的话,this就代表被构造出来的对象:

[+]view code

image

由此我们可以得出:this指的是包含该函数的对象。对于全局函数来说,包含它的是window;对于构造器和对象中的方法来说包含它的是将被构造出来的对象;对闭包来说,包含它的对象也是window。

3.2 私有变量和私有函数

JS中在函数中定义的变量都可以认为是私有的变量。

image

其中Person中的name和ss都是私有的,实例化Personimage002后也不能访问这两个变量。

但是下面用this添加的变量在实例化后可以直接访问。

image

所以可以将一些不必让外部访问的变量用var来声明,无需用this来添加属性。外部要访问这些私有变量可以为其设置一个特权函数(即一个匿名函数):

image

3.3 闭包与变量

闭包的实现就是以来作用域链,但是作用域链的本质是指向变量对象的指针列表。所以,闭包只能取到外部函数中任何变量对象的最后一个值。

image001

打印出来的都是10.

虽然在image002[6]中,外部函数已经执行完毕,一般情况下它的局部变量对象会被销毁,但是由于被返回的匿名函数依然引用这它的活动变量i,所以外部活动变量i任然存在

当后面每次调用返回来的匿名函数image003[8]时:相当与在执行function(){return i;}函数,它首先会在自己的活动变量中寻找i,没找到,然后在它的外部函数中寻找活动变量i,但是此时由于image004[4]执行了image005语句,使得i的值为10.所以内部匿名函数也就返回了10.每次调用内部匿名函数都会执行同样的上述流程。

那么怎么来避免这种情况呢?

首先来看一个一直未用过的技巧:

image006

不能在函数中直接写匿名函数,除非将匿名函数赋值给一个变量或者用括号括起来,但是在函数外面可以直接写。image007

在这里ss不在代表的是匿名函数了,而是该匿名函数被执行所返回的东西,所以ss在这里已经被赋值位“立即执行”了。所以匿名函数可以在后面增加括号来传递参数立即执行。

所以我们在外部函数中让内部匿名函数立即执行:这里又有两种情况:

· 1.result中包含的是值。

image008

在这里我们将result数组的内容改变了,它每一项中不在保存由image009返回来的匿名函数或者闭包。而是内部匿名函数执行后的值。

· 2.result中包含的是闭包

image

我们来看一下这个函数的执行过程,当执行image011时,“result[i]=”后面的闭包由一个立刻被执行的匿名函数代替。该匿名函数同样返货一个闭包。而该闭包返回的是匿名函数的活动对象,而不是image012的活动对象,而是image013的活动对象。image012[1]的作用域链及所有活动对象当然包括i,在image012[2]执行完毕后被销毁。而该匿名函数的活动对象sum是通过每执行一个i++,将此时的i的快照赋值给了sum,注意,这里i是值类型并且JS中传参是按值传递的,所以i++不会影像到前一个sum。

所以result[i]=image014,该闭包在自己的活动对象中找不到sum,便在它的包含函数image015中找sum,此时的image015[1]中的sum已经在image016的for语句时赋了值。而每次的i++中image017之间没有任何关系。所以,sum也就不会被替换了。

image018

4. Arrary

image

创建数组有两种方法:

①使用Array构造函数:

       var colors=new Array();或者var colors=new Array(20);(这里是创建一个包含20个项的数组)或者var colors=new Array("red","blue");(这里是创建了一个包含 "red"和"blue"2个字符串的数组 )当然也可以将new去掉。

②使用数组字面量表示法:

image

image打印:10781166

注意:数组的length属性不是只读的,可以修改它来控制数组的长度。没有赋值的项为undefined。也可以利用该属性方便的在数组的末尾添加新项:

image打印:11033389

这是因为,数组的最后一项的索引为length-1,则在数组末尾再加一项,它的索引变为length。

4.1 重排序

两个方法:

①reverse

用途:反转一个数组,返回值为反转后的数组。

23472955 打印:23620626

②sort

默认情况下是按照升序排列的。返回排序后的数组。

23701980打印:23719952

23863410 打印:23875282

由此可见sort默认的比较是按照字符串来比较的。之所以将5排在10和15之后,对于字符串来说,“5”排在“1”之后。

为了克服这种不便,sort还可以接受一个比较函数作为参数,以便我们指定那个值位于那个值的前面。该比较函数接受两个参数,如果第一个参数位于第二个参数的前面则返回一个负数,如果两个参数相等则返回0,如果第一个参数位于第二个参数的后面则返回一个整数。编写一个比较函数来排序上面这个数组:

image打印:24460426

如果数组的每一项都是数值的话则可以用如下更简便的比较函数:

image

5. 绑定上下文

[+]view code

最后一句代码,我们期望的接货时弹出:result=300,但是事与愿违,弹出的结果却是:

image

为什么会这样呢?因为当把theAccumulator.getResult传递给printResult的时候没有为theAccumulator.getResult设置上下文,要知道“JavaScript函数并不与定义它们时的作用域绑定,它们对执行它们时的作用于的要求并不严格。”所以在theAccumulator.getResult函数中返回this.total,这时,在没有明确的作用域限制的情况下this指的是全局作用域。而在全局作用域中并没有total变量。所以才出现了上述情况。只要在全局作用域中增加total的定义:var total=100;,即代码更改为:

[+]view code

image

将printResult函数变为如下即可:

image

还可以这样:

image

apply、call

强大之处:扩充函数赖以运行的作用域。

image

image

6. 面向对象

6.1 创建对象

在JS中创建对象的方法很多,但是每一种方法都有它自身的缺点,这里给出一个使用最广泛,认可度最高的一种方法:

[+]view code

image

正如上面所演示的,使用构造函数定义实例属性,使用原型定义方法和共享的属性。这样每个实例都会有自己的一份实例属性的副本,同时也共享着对方法的引用,最大限度的节省了内存。

6.2 继承

JS中继承的实现方式也很多,这里同样也介绍一种最常用的方法。

使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例方法的继承。这样继承在原型上定义方法实现函数的复用,又能保证每个实例都有自己的属性。

[+]view code

image

7. Ajax

AJAX 就是运用Javascript 在后台悄悄帮你去跟服务器要资料,最后再由Javascript 或DOM 来帮你呈现结果,因为所有动作都是由Javascript 代劳,所以省去了网页重载的麻烦,使用者也感受不到等待的痛苦。

7.1 XMLHttpRequest对象简介

XMLHttpRequest对象是Ajax应用程序的中心。

• XMLHttpRequest对象最初是作为IE5中的一个ActiveX控件出现的,随后Mozilla 1.0、Netscape7、Safari1.2和Opera7.60都将它纳入自身。

• XMLHttpRequest对象在IE浏览器和非IE浏览器中创建的方法不同。

• XMLHttpRequest对象的作用在于,允许用脚本程序通过HTTP连接到服务器,而不是通过HTTP请求响应模型与服务器通信。

简而言之:它可以异步从服务器端获取txt或者xml数据

• 开发一个AJAX功能需要开发服务端和客户端两块程序。以一个显示服务端时间为例。首先开发一个GetDate1.action,输出当前时间。在HTML页面中放一个按钮,在按钮的onclick中创建XMLHTTP向GetDate1.action发送请求,获得返回的数据并且显示到界面上。代码见备注。

• 也可以在xmlhttp.open中向服务器传递参数:xmlhttp.open("POST", "GetDate1.action?id=1&name=哈哈", true),如果传递给服务器的请求里有中文,则需要使用Javascript函数encodeURI来进行URL编码。

7.2 创建XMLHttpRequest对象

同步使用XMLHttpRequest对象

按照下面模式,可以同步地XMLHttpRequest对象:

  1. 创建对象; - new  (叫助手过来)
  2. 创建请求; - open (告诉他要去做的事情)
  3. 发送请求; - send (去吧)

一、先来创建XMLHttpRequest对象

  – 在IE、Firefox、safari和Opera中创建该对象的JavaScript代码为:var xhr = new XMLHttpRequest();

  – 在IE5/6中代码为:var xmlRequest = new ActiveXObject(“Microsoft.XMLHTTP”);

注意,JavaScript区分大小写。

[+]view code

二、为XMLHttpRequest对象设置请求参数

   1.设置参数:
xhr.open("GET", "GetAreasByAjax.action?isAjax=1", true);

     2.POST方式:
xhr.open("POST", "GetAreasByAjax.jsp", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    3.设置浏览器不使用缓存
xhr.setRequestHeader("If-Modified-Since", "0");

三、发送请求:(分别对应GET和POST)

    xhr.send(null);//GET方式

xhr.send("isAjax=1&na=123");//POST方式

异步使用XMLHttpRequest对象

异步使用XMLHttpRequest对象时,必须使用:onreadystatechange事件。

使用模式应该是:

  1. 创建该对象;-new
  2. 设置readystatechange事件触发一个回调函数; -onreadystatechagne
  3. 打开请求;-open
  4. 发送请求;-send
  5. 回调函数中检查readyState属性,看数据是否准备就绪(是否等于4)。

如果没有准备好,隔一段时间再次检查。因为数据没有下载完时,我们无法使用它的属性和方法。

如果已经准备好,就继续往下执行;

7.3 编写回调函数

1.在xhr.send之前添加设置回调函数代码:

xhr.onreadystatechange = watching;

2.回调函数

[+]view code

7.4 XMLHttpRequest对象常用属性

image

readyState属性指出了XMLHttpRequest对象在发送/接收数据过程中所处的几个状态。XMLHttpRequest对象会经历5种不同的状态。

– 0:未初始化。对象已经创建,但还未初始化,即还没调用open方法;

– 1:已打开。对象已经创建并初始化,但还未调用send方法;

– 2:已发送。已经调用send 方法,但该对象正在等待状态码和头的返回;

– 3:正在接收。已经接收了部分数据,但还不能使用该对象的属性和方法,因为状态和响应头不完整;

– 4:已加载。所有数据接收完

7.5 XMLHttpRequest对象的方法

image

7.6 实例-get

Js生成XMLHttpRequest

[+]view code

点击按钮执行Ajax调用:

[+]view code

7.7 实例-post

[+]view code
原文地址:https://www.cnblogs.com/yaoyinglong/p/JavaScript.html