Truly
写精彩代码 品暇逸人生
posts - 92,comments - 313,trackbacks - 46
作者:Truly
日期:2007.8.3

在我前面一篇文章《
在JavaScript中使用面向对象》中我们介绍了MSDN的一篇文章《
使用面向对象的技术创建高级 Web 应用程序》,作者简单介绍了JavaScript面向对象的一些关键技术,但是作者在讲到闭包概念的时候犯了一个明显的错误:“正常情况下,无法从函数以外访问函数内的本地变量。函数退出之后,由于各种实际原因,该本地变量将永远消失”详见原文。事实上这段描述是错误的。

请先看如下代码:
<script>
function Test(abc)
{
this.g = function(){debugger;};
}

var p = new Test(2);
p.g();
</script>

如果你启用了IE的调试功能,并安装了脚本调试器,例如VS,那么你在程序提示调试的时候进入调试,此时你可以醒目的发现abc依然存在,并且完好的保存了正确的值,而并非永远消失。但是这也不是我本文要讨论的重点,只是希望大家以后能够多动手,多实践,像MS ASP.NET AJAX 团队的软件设计工程师都会犯这种错误,更何况诸位呢?

本文要讨论的是面向对象编程中常用的属性,但是在JavaScript中属性则无法像高级编程语言那样可以直接使用,看起来更像方法,这种实现方式也有人称之为闭包,但本文以属性相称。

属性是对私有变量的一种保护手段,同时提供了像public变量一样的使用效果,近代的高级编程语言例如C#和Java都支持了属性这一特点。

我们知道,函数的入口参数被声明为该函数的本地变量,而对于本地变量,像我前面《在JavaScript中使用面向对象》关于全局变量和局部变量中描述的那样,由于其作用域仅限于函数内部,所有无法在外部对其进行访问,例如p.abc不会返回p内部的abc变量。这一点跟高级编程语言完全一致,你无法在类外部访问其private变量,但是我们可以借助public方法来返回私有变量。所以高级编程语言如Java,C#等中属性的作用,就是保护私有变量。像C#这门语言,属性最终会由编译器编译为get_属性名()这样的方法,当我们使用某个属性时,实质上是调用一个方法。

使用面向对象的技术创建高级 Web 应用程序》的作者认为是由于方法的定义才使局部变量存活下来,这一点是不正确的,具体我们前面已经分析过了,如果你仍有疑问,那么再仔细研究下面的代码:
function Person(name, age) {
    
this.getName = function() {debuggerreturn 1; };
}
var o = new Person(1,2);
o.getName();    
// 进入调试后发现name=1
var t = new Person(2,4);
t.getName();    
// 进入调试后发现name=2
o.getName();    // 进入调试后发现name=1,并未受到其它实例的影响

对于这个问题,我起初也认为是因为变量有引用才没被销毁,最后证明局部变量在对象销毁前其内部的变量不会销毁。

同时那篇文章中另外一段也是不准确的:
注意,这些私有成员与我们期望从 C# 中产生的私有成员略有不同。在 C# 中,类的公用方法可以访问它的私有成员。但在 JavaScript 中,只能通过在其闭包内拥有这些私有成员的方法来访问私有成员(由于这些方法不同于普通的公用方法,它们通常被称为特权方法)。因此,在 Person 的公用方法中,仍然必须通过私有成员的特权访问器方法才能访问私有成员

关于这一点,他的表述相当模糊,事实上我们可以这样理解:

在C#中我们可以在类的任何方法中访问类的私有成员变量,
而在JavaScript中,只能使用在function方式中定义的方法对私有成员访问,而无法在prototype方式定义的方法中访问。

如果这样讲你还不能理解的话,那么还可以这样理解,在JavaScript中的私有变量无法在其声明函数外访问,例如:

function Person()
{
    
var ttt;
}

永远不能在{}外部试图访问ttt

现在我们更加深入的理解了变量作用域在JavaScript中的特点。

前面讲了高级编程语言中属性的种种好处,又研究了JavaScript对私有变量的保护,那么您对JavaScript中属性的实现应该非常清楚了,这里引用《使用面向对象的技术创建高级 Web 应用程序》文中的一段示例代码:

function Person(name, age) {
    
this.getName = function() { return name; };
    
this.setName = function(newName) { name = newName; };
    
this.getAge = function() { return age; };
    
this.setAge = function(newAge) { age = newAge; };
}

关于属性在实际中的应用及其优点,将在我下一篇文章介绍自定义事件中讲解。

后记:本来打算在这里讲述如何在JavaScript中实现面向对象中的一些特性,比如用“属性”这一现代编程概念体现的对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,即借助于get和set对私有成员的值进行读写。最后却演变成为一个白马是不是马的哲学讨论,真是汗颜。

posted on 2007-08-03 21:23 Truly 阅读(2390) 评论(16)  编辑 收藏 所属分类: JavaScript & Vbscript

FeedBack:
2007-08-03 21:38 | 木野狐(Neil Chen)      
这个不就是闭包么?
closure
  回复  引用  查看    
#3楼 [楼主]
2007-08-03 21:42 | <font color=red>Truly</font>      
这个就是闭包,但是闭包这样的词通常很少出现在面向对象中吧,也不知道你的地址想证明什么。
  回复  引用  查看    
2007-08-03 22:04 | 木野狐(Neil Chen)      
@Truly
没有要证明什么,只是举出一个例子。
  回复  引用  查看    
#5楼 [楼主]
2007-08-03 22:06 | <font color=red>Truly</font>      
哦,看来你也挺喜欢研究js,可以多切磋一下。
  回复  引用  查看    
2007-08-03 22:21 | 木野狐(Neil Chen)      
@Truly
hehe
  回复  引用  查看    
2007-08-04 01:39 | ※ABeen※      
有意思
  回复  引用  查看    
2007-08-04 14:09 | Truly      
在我发了这篇文章之后,发现还是有很多人没有理解为什么要封装为方法,似乎属性必须应该是obj.Property=这种方式,而不应该是obj.setProperty()这种方式,首先文中已经说明了能够像字段一样操纵属性是高级语言的编译器的功劳,在底层它们都是方法调用,其次他们没有理解什么是属性,认为属性A应该这样定义:
function myclass() { var a; this.A=a; }

这就是明显的一个错误,这段代码类似于C#中的:
Class myclass{
private int a;
public int A = a;
}

这跟我们期望的属性有明显区别,我们期望的是
Class myclass{
private int a;
public int A
{
get{...}
set{...}
};
}

但是在JavaScript中我们不能借助编译器将属性伪装成字段,所以必须使用方法调用,如我多次提到的,我们使用JavaScript的时候,就必须了解JavaScript的特性,JavaScript不是高级语言,不要用高级语言来硬套。
  回复  引用  查看    
2007-08-04 15:08 | Hurry [未注册用户]
这只能算是用方法来模拟属性,如果这是属性,那方法又是什么呢?
  回复  引用    
2007-08-04 17:18 | Truly      
属性是指一种特殊的方法,你称其方法也是可以的,这点主要是领悟力的问题。

我们看MSDN对属性的定义:

C# 编程指南
属性(C# 编程指南)

属性是这样的成员:它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称为“访问器”的特殊方法。这使得数据在可被轻松访问的同时,仍能提供方法的安全性和灵活性。



  回复  引用  查看    
2007-08-04 17:22 | Truly      
上面定义可详见:http://msdn2.microsoft.com/zh-cn/library/x9fsa0sw(VS.80).aspx

属性还由一点需要注意,的确,这个词容易混淆,我这里的属性沿用MSDN对Property的翻译,我们知道还有另外一种属性,就是普通定义的一些字段(或域,这样也是MSDN译法),也可以称其属性。
  回复  引用  查看    
2007-08-04 17:30 | Truly      
当然对于长期从事高级语言编程的人来讲,这个概念的确很难理解。但是对于使用c,c++等程序语言的人来讲,如果要去设计和实现Property,一样是通过类似方式实现。
  回复  引用  查看    
2008-01-10 21:04 | JS Lover [未注册用户]
奇怪了,为什么要抛弃obj.property = value的形式,我绝对支持这种形式。
安全?公开一个属性没什么安全问题啊! 例如 textObj.text = "aaaa" 的形式不是很能说我在改变对象文本的值吗?
  回复  引用    
2008-08-30 11:53 | 韩羽 [未注册用户]
1)"只能使用在function方式中定义的方法对私有成员访问,而无法在prototype方式定义的方法中访问"
这是错误的,是可以的。
2)function Person(name, age) {
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
this.getAge = function() { return age; };
this.setAge = function(newAge) { age = newAge; };
}
这种方式定义方法,在实例多个对象情况下,占用内存相对较多。应使用动态原型方法
  回复  引用    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-08-04 19:25 编辑过


相关链接: