一、模仿块级作用域
JavaScript 没有块级作用域的概念,那么可以模拟像java中将很多变量私有化封装起来,保护数据,防止数据泄漏,封装细节,这样安全性和可控性更高
function box(count) { for (var i=0; i<count; i++) { //块级作用域(JS中没有这个东西) } alert(i); //i 不会因为离开了 for 块就失效 } box(2); //结果是2,还是可以访问的
function box(count) { for (var i=0; i<count; i++) { //块级作用域(JS中没有这个东西) } var i; //就算重新声明,也不会前面的值 alert(i); //i 不会因为离开了 for 块就失效 } box(2); //结果是2,还是可以访问的
以上两个例子,说明 JavaScript 没有块级语句的作用域,if () {} for () {}等没有作用域,如果有,出了这个范围 i 就应该被销毁了。就算重新声明同一个变量也不会改变它的值。
JavaScript 不会提醒你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域可避免这个问题。
二、模仿块级作用域(私有作用域)
/* (function () { //这里是块级作用域 })(); */ function box(count) { (function () { for (var i = 0; i<count; i++) { alert(i);//只能在这里i才是有效的才是能访问的 } })(); alert(i); //这里i已经出了作用域 报错,无法访问 } box(2);
使用了块级作用域(私有作用域)后,匿名函数中定义的任何变量,都会在执行结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大型项目中,多人开发的时候,过多的全局变量和函数很容易导致命名冲突,引起灾难性的后果。
如果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域。
(function () { var box = [1,2,3,4]; alert(box); })(); alert(box);//box 出来就不认识了
在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
三、私有变量
JavaScript 没有私有属性的概念;所有的对象属性都是公有的。不过,却有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。
function box() { var age = 100; //私有变量,外部无法访问,只能返回 }
而通过函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而利用这一点,可以创建用于访问私有变量的公有方法。
function Box(){ this.age = 100; //可以访问,公有的 this.run = function(){ //可以访问,公有的 return "运行中...."; } } var box = new Box(); alert(box.age); alert(box.run());
function Box() { var age = 100; //私有变量 function run() { //私有函数 return ‘运行中...‘; } this.get = function () { //对外公共的特权方法 return age + run(); }; } var box = new Box(); alert(box.get());
可以通过构造方法传参来访问私有变量。
function Person(value) { var user = value; //这句其实可以省略 this.getUser = function () { return user; }; this.setUser = function (value) { user = value; }; } var box = new Person("hua"); alert(box.getUser()); box.setUser(4); alert(box.getUser());
但是对象的方法,在多次调用的时候,会多次创建。
function Person(value) { var user = value; //这句其实可以省略 this.getUser = function () { return user; }; this.setUser = function (value) { user = value; }; } var box = new Person("hua"); alert(box.getUser()); var box2 = new Person("kkk"); alert(box.getUser());//结果还是hua,说明setUser方法没有共享
可以使用静态私有变量来避免这个问题。
四、静态私有变量
通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公共的特权方法。
(function () { var age = 100; //私有变量 function run() { return ‘运行中...‘; } //function Box(){} //构造方法,在函数里写构造函数不支持,因为私有作用域里的函数,外部无法访问,所以要做成全局的,如下 Box = function (value){ //全局构造方法 age = value; }; Box.prototype.getAge = function () { //原型方法,只生成一个地址 return age + run(); }; Box.prototype.setAge = function(value){ age = value; } })(); var box1 = new Box(23); alert(box1.getAge()); var box2 = new Box(43); alert(box2.getAge()); box2.setAge(76); alert(box1.getAge());//使用box2设置了age的值,再使用box1获取age的时候变成了box2设置的值,说明共享了
使用了 prototype 导致方法共享了,而 age也就变成静态属性了。(所谓静态属性,即共享于不同对象中的属性)。
五、模块模式
之前采用的都是构造函数的方式来创建私有变量和特权方法。那么对象字面量方式就采用模块模式来创建。
var box = function () { var age = 100; function run() { return ‘运行中...‘; } return { //直接返回对象 go : function () { return age + run(); } }; }(); //上面的直接返回对象的例子,也可以这么写: var box = function () { var age = 100; function run() { return ‘运行中...‘; } var obj = { //创建字面量对象 go : function () { return age + run(); } }; return obj; //返回这个对象 }();
字面量的对象声明,其实在设计模式中可以看作是一种单例模式,所谓单例模式,就是永远保持对象的一个实例。
function Desk() {}; var box = function () { var age = 100; function run() { return ‘运行中...‘; } var desk = new Desk(); //可以实例化特定的对象 desk.go = function () { return age + run(); }; return desk; }(); alert(box.go());
增强的模块模式,这种模式适合返回自定义对象,也就是构造函数。