模仿块级作用域
Javascript没有块级作用域的概念,意味着在块级语句中定义的变量,实际上是在包含函数中而非语句中创建的,看下面例子:
function a() {
for (var i = 0; i < 2; i++) {
alert(i);// 0,1
}
alert(i); //2
}
在c#中,上述 i的作用域仅在 for循环之内,在之外 调用该变量将会报错.但是 javascript中却不会,它从来不会告诉你是否多次声明
同一个变量,它只会对后续的声明视而不见,但是 会执行后续声明中的初始化.看下面例子:
function a() {
for (var i = 0; i < 2; i++) {
alert(i);// 0,1
}
var i = 10;
alert(i); //10
}
匿名函数可以 用来模仿块级作用域(私有作用域) 并且避免上述问题. 下面看个例子:
(function a() {
(function () {
for (var i = 0; i < 2; i++) {
alert(i);// 0,1
}
})();
alert(i); //导致一个错误
})();
整个重写了a()函数,在for循环中插入一个私有作用域,在匿名函数中定义的变量,在执行完毕后,将会被销毁.
因此,变量i只能在for循环中 被访问.
私有变量
严格来讲,javascript中 没有私有成员的概念所有对象属性都是公有的.倒是有私有变量的概念.
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问它们. 私有变量包括:参数,局部变量,
和在函数内部定义的其它函数.
我们可以利用闭包,创建 用于访问私有变量的公有方法.因为闭包可以通过 作用域链来访问 函数内部的所有变量.
把有权访问私有变量的公有方法 承做 特权方法 ,有两种在对象上创建特权方法的模式的方法:
1:
function object() {
var privateVarible = 10; //私有变量
function privateFunction() { //私有方法
return privateVarible;
};
//特权方法
this.publicMethod = function () {
privateVarible++;
return privateFunction();
}
}
var o = new object();
alert(o.privateVarible); //undefined
alert(o.privateFunction()); //报错
alert(o.publicMethod()); //11
2: 可以利用私有和特权成员,来隐藏那些不应该被直接修改的成员:代码如下:
function person(name) {
this.getName = function () { return name };
this.setName = function (value) { name = value };
}
var p = new person("gao");
alert(p.name); //undefined
alert(p.getName()); //gao
p.setName("newgao");
alert(p.getName()); //newgao
在构造函数中定义 特权方法,有一个不好的地方就是,每次实例化的时候,都会同样创建一组新方法, 那么就可以使用
静态私有变量来实现特权方法.
静态私有变量
通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法,基本模式如下:
(function () {
//私有变量和私有函数
var privateVarible = 10;
function privateFunction() {
return false;
}
//构造函数
myObject = function () { }; //没使用var因为 要创建一个全部变量,而不是局部变量,这个外界才能调到构造函数
//公有/特权方法
myObject.prototype.publicMethod = function () {
privateVarible++;
return privateFunction();
}
})();
var o = new myObject();
alert(o.publicMethod()); //false
这特权方法定义在 原型上,是典型的原型模式. 这样每个实例就可以共享这个 特权函数,而不必,没新建一个实例,
就实例化一个 特权函数对象.但是也有一个问题,就是 每个实例都共享函数和属性,那么每个实例就没有了自己独有的属性,看例子:
(function () {
var name = "";
Person = function (value) { //定义构造函数
name = value;
};
//定义特权方法
Person.prototype.getName = function () { return name; }; //闭包总是保存着包含作用域的引用
Person.prototype.setName = function (value) { name = value; };
})();
var p = new Person("gao");
alert(p.getName()); //gao
var p2 = new Person("gao2");
alert(p.getName()); //gao2
p.setName("gao3");
alert(p2.getName()); //gao3
无论是新实例化对象,修改已有对象的name属性,都会导致 所有实例的name属性发生变化.这就是原型模式的不足之处.