JS 笔记

javascript 代码运行分俩个阶段

预解析 —把所有函数定义(function () {})提前,所有的变量声明提前,变量的赋值不提前
执行 — 从上到下执行 (setTimeout,setInterval, ajax中的回掉函数,事件中的函数需要触发执行)

for 循环之闭包

所有输出结果都为3版本

1
2
3
4
5
6
7
8
9
10
var data = []; 
for(var i = 0;i < 3; i++){
data[i] = function(){
console.log(i); // 这里只是声明了一个 函数,并未调用,无法使用for里边的i。。。。到for外边调用时,此时i已经变为3
}
}
// for 循环完毕之后,i的值变为3(为全局变量)
data[0](); // 3 data[0]=function(){console.log(i) 此i为for循环之外的i}
data[1](); // 3
data[2](); // 3

输出结果依次为0,1,2版本

1
2
3
4
5
6
7
8
9
10
11
12
var data = [];
for(var i = 0; i<3; i++){
data[i] = (function(k) {
return function () {
console.log(k);
}
}) (i); // 此 i 为立即执行函数的的实际参数,,并把其值赋值给形式参数 k
}
// for 循环完毕之后,i的值变为3(为全局变量)
data[0](); // 0
data[1](); // 1
data[2](); // 2

初识闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
(function() {
/**闭包详解*/
function fun() {
var num = 10; // 让fun以外的函数 使用num(变量)的值 这样就可以创建闭包
console.log(num);
}

function outFun() { //本函数执行结果是返回inFun函数的函数体,即function inFun() {console.log(num);}
var num = 10;

function inFun() {
console.log(num);
}
return inFun; //(此句为闭包核心) 该行代码作为 本函数的执行结果 因为函数不调用是不会执行的
}

//赋值运算符(=) 是从右往左计算的
var demo = outFun(); // 本段代码就是 把调用outFun函数的结果(返回inFun函数的函数体) 赋值给demo
// outFun() 返回的是 function inFun() {console.log(num);}
// 等同于 var demo = function inFun() {console.log(num);}

var demo = function inFun() {
console.log(num);
} // 把一个函数赋值给一个变量,那么 该变量就成为一个函数

demo(); // 接下来就调用这个函数


/**
*如上 demo这个函数 使用outFun函数内的变量 这一过程就形成了一个闭包
*/

//其实每一个 函数都是 一个小闭包 (当函数引用全局变量的时候)
var key = 10;

function one() {
console.log(key);
}

/**闭包实例*/
function outerFun() {
var a = 0; ///每次调用 outerFun函数 a都会被清零;
function innerFun() {
a++;
alert(a);
}
return innerFun;//此处的innerFun为全局变量,永远保留在内存中,除非手动清理
}

var obj = outerFun();
obj(); obj(); // 1 2

var obj2 = outerFun();
obj2(); obj2(); // 1 2

/**闭包原理 :
* 如果想要使用某个函数内的变量,直接是无法使用的(因为有作用域的存在)。
* 但是我们可以曲线救国,,通过在该函数内内置一个函数,,然后再把内置函数return出来,
* 然后在外面调用这个内置函数
*/
})();

闭包优点:不会产生全局变量,实现属性私有化

闭包缺点:闭包中的数据会常驻内存,在不用的时候要删掉否则会导致内存溢出

闭包实例

闭包作用

  • 一个是前面提到的可以读取函数内部的变量
  • 另一个就是让这些变量的值始终保持在内存中。

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

使用闭包应该注意的地方

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

思考题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name; // 此处的this指向window
      };
    }
  };
  alert(object.getNameFunc()()); // My Object
// 上方代码全等于 var fn = object.getNameFunc() alert(window.fn())

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this; // 此处的this指向object,即that也指向this
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()()); // My Object

代码段一、
当一个函数作为函数而不是方法调用的时候,这个this关键字引用全局对象。容易令人混淆的是,当一个嵌套的函数(作为函数)在一个包含的函数中调用,而这个包含的函数是作为方法调用的,这也是成立的:this关键字在包含的函数中有一个值,但是它却(不太直观地)引用嵌套的函数体的内部的全局对象。
所以第一个打印出来的是”The Window”

代码段二、
由于this关键字不是在包含的函数中引用的,而是通过that=this这个调用的,所以这个this不是在闭包内的,因此这个this就不能调用函数体内的全局对象,而是他的局部对象object.name,所以第二个打印出来的是”My Object”

对象

基本数据类型

number string boolean null undefined (object 非基本数据类型)
基本数据类型不能自定义属性和方法,,而对象可以

声明对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obi = new Object();
var obj = {}; //字面量式; 推荐

//自定义对象的属性、方法

var obj = {};

obj.age = 55; //自定义属性

obj.age; //调用属性

obj.showAge = function () {alert(55)}; //自定义方法

obj.showAge(); //调用方法

封装对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function (){
function person(name, age) {
var obj={};
obj.name=name;
obj.age=age;
obj.showName= function () {
console.log(""+name);
}
obj.showAge= function () {
console.log(""+age);
}
return obj; // 最后一定要把这个对象返回
}

var xiaoming = person("小明",12); //利用person函数来创建一个小明对象
console.log(xiaoming.name); //调用xiaoming的name属性
console.log(xiaoming.age);
xiaoming.showAge(); //调用xiaoming的showAge()方法
}) ();