一、JavaScript 闭包

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
add();
add();
add();
 
// 计数器为 3

实例解析:
变量add指定了函数自我调用的返回值,自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。
add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。计数器受匿名函数的作用域保护,只能通过 add 方法修改。

二、为什么需要prototype

新建一个对象的方法:

1.new Object:
var newObj = new Object;
newObj.name = "keti";
newObj.color = "red";
newObj.changeColor = function(color){
    newObj.color = color;
}

这种方法看上去很蠢,所以我们找到另一种方法:使用literal直接创建,看上去要优雅得多:

2.literal
var newObj = {
    name: "keti";
    color: "red";
    changeColor: function(color){
        newObj.color = color;
    }
}

使用literal来常见对象似乎很不错,比较直观,两个卷括号括起来就是个对象嘛,多清晰明了。但是如果你要创建一系列结构类似的对象,难道要这样一个个写吗?作为程序员我们的遵循的理念就是尽可能减少重复,也就是著名的DRY(Don’t Repeat Youself)。所以我们是无法容忍这么愚蠢的事情发生的,因此就有了构造函数,跟类有点相似,但我们这里不讨论类。构造函数就是建立一个模板,不绑定数据,只提供架构,你只需把相关数据填充到模板里就可以生成一个新的对象了:

3.构造函数
function NewObj(name,color){
    this.name = name;
    this.color = color;
    this.changeColor = function(c){
        this.color = c;
    }
}

var newObj1 = new NewObj(“keti”,“red”);
上面代码中,new是一个构造器,NewObj是我们创建好的模板,填入数据,赋给变量newObj1,ok,新的对象就这样生成了。

到此创建对象的方法似乎已经很不错了,但仔细观察我们还发现了新的问题:对于changeColor()这个方法事实上对所有instance来说是相同的,也就是说可以共享,不像name和color那样需要绑定给每个instance。而构造函数这种形式每次都会把自身的属性全部copy一份给每个instance,这就造成了不必要的浪费;并且,当我们想修改这个方法时,就必须重新生成所有的instance才能获得更新,比如说:

function NewObj(name,num){
    this.name = name;
    this.num = num;
    this.changNum = function(c){
        this.num = c;
    }
}
var newObj1 = new NewObj("kemi",10);
newObj1.changNum(100);
newObj1.num; //很明显是100

我现在想修改changNum()这个函数:

function NewObj(name,num){
    this.name = name;
    this.num = num;
    this.changNum = function(c){
        this.num = c*2;
    }
}
newObj1.changNum(100);
newObj1.num; //依然是100,也就是说这个对象并不受我们修改的模板影响到

怎么解决这个问题呢?有一个原型对象。原型对象里的属性和方法并不是像构造函数自身属性一样copy给每个instance,而是“引用”,也可以理解为给每个instance提供一个指向该原型对象的指针,这样每个instance就能找到原型对象里的属性,而很明显,这是一种共享,也就是说,当你修改了这个原型里的属性,那么所有共享该属性的instance都能获得这个修改。因此,原型恰好解决了上面提到的两个问题。

function NewObj(name,num){
    this.name = name;
    this.num = num;
}
NewObj.prototype.changNum = function(c){
        this.num = c;
        }
var newObj1 = new NewObj("kemi",10);
newObj1.changNum(100);
newObj1.num; //很明显是100
NewObj.prototype.changNum = function(c){
        this.num = c*2;
        }//我们重新修改一下这个方法
newObj1.changNum(100);
newObj1.num; //变成200了。

为什么一般情况下会把属性直接写在构造函数内,而方法通过prototype添加呢?这两种方式的区别上面其实已经有所展现了:大部分的instance的属性都是不同的,比如说name,因此在构造函数内通过this直接绑定给instance无疑是个好方案,而方法通常是通用的,使用prototype可以让每个instance共享同一个方法,而不用每个都copy一次,又能实现实时更新。

三、JS创建对象的三种方式

JS除了一些常用方法和类以外,允许我们自己定义对象,在JS中自定义对象有三种可用的语法格式,分别为:

  1. 调用系统的构造函数创建对象
  2. 自定义构造函数创建对象
  3. 字面量的方式创建对象

第一种语法:调用系统的构造函数创建对象(Object)

// 实例化对象
 var obj =new Object();
 // 给对象添加属性
 obj.name="张思锐";
 obj.age=35;
 obj.gender="男";
 // 给对象添加方法
 obj.eat=function(food){
         console.log(this.name+"正在吃"+food)	
 }
 
 // 查看对象属性
 console.log(obj.name);
 console.log(obj.age);
 console.log(obj.gender);
 // 调用对象方法
 obj.eat("蛋糕");

第二种语法: 自定义构造函数创建对象(function)

// 准备一个构造方法 
 function Person(pname,page){
           this.pname=pname;
           this.page=page;
           
           this.eat=function(food){
                   console.log(this.page+"岁的"+this.pname+"正在吃"+food);
           }	
   }
   
   var p1=new Person("张思锐",10);
   // 查看对象属性
   console.log(p1.pname);
   console.log(p1.page);
   // 调用对象方法
   p1.eat("油条");

第三种语法: 字面量的方式创建对象(JSON)

// JSON
 // var 对象名 ={属性名:属性值,属性名:属性值, … … ,方法名:方法声明,方法名:方法声明}
 var person ={
 name:“晓明”,
 gender:“男”,
 age:10,
 eat:function(food){
 console.log(this.age+“岁的”+this.gender+“孩儿”+this.name+“正在吃”+food)
 }
 };
// 查看对象属性
 // 调用对象方法
 console.log(person.name);
 console.log(person.gender);
 console.log(person.age);
 person.eat("馒头");