引用传参--面向对象的魅力
面向对象开发作为一种主流的软件开发方式,极大地改变了我们开发软件的方式。其遵从人类认识世界的普遍规矩,将现实中纷繁复杂的事物抽象为业务实体类,这就是由具体到抽象的认知过程;其积极响应软件工程的需求,其使我们开发的软件具有灵活性、扩展性、维护性的能力,极大地促进了软件开发的发展。
那么传递参数的时候,面向对象技术能给我们带来什么好处呢? 今天就以昨天碰到的问题作为引子,我们来简单的了解一下面向对象传参的魅力!
在web开发中,我们经常需要从父页面传递参数到子页面中,具体的代码如下,
function showModelDialog(xml) {
var url = "child.htm?parameter=123456";
var parameter = "123456";
window.formXml = xml;
window.showModalDialog(url, parameter);
}
代码中,我们使用showModelDialog来打开一个模式对话框,这个时候我们往往会传递一些参数过去,一般情况我们会有两种方式实现
1. url传递参数
2. 通过showModelDialog的第二个参数传递
那么这两种方式有什么区别呢?
1. 前者传递的数据简单类型单一,只能作为字符串传递;
2. 前者传递的字符串不能包括特殊的字符,同时字符串的长度不能太长;
3. 基于浏览器安全的考虑,包含某些字符串的参数不能传递(比如跨站脚本攻击);
所以很多的时候,我们会更乐意使用第二种方式来传递参数,但是这种方式又有什么特点呢?又怎么能体现面向对象技术传递参数的魅力呢?先别急,还是先来简单的了解一下我遇到的问题吧,我们的产品在父页面中通过复杂的业务逻辑形成了一个xml格式的数据源,然后将这个数据源传递给子页面来展现。我是通过第二种方式实现的。本来一直运行相安无事,但是昨天事业部的客户反馈打开子页面的时候抛出异常;经检查配置的数据没有什么异常,唯一的差别就是配置形成的xml数据源字符串的长度比较大,那么会不会是由于参数传递有长度限制,截断了部分的字符串,导致子页面解析数据源的时候出错?想到这里,我就逐渐字符个数来测试,终于我测出来这个长度限制就是4096个字符,我们可以通过以下我写的例子测试
//测试类
var PassParameterTester = {
//新的页面地址
url: "NewPage.htm"
,
//获取字符个数为4096的参数
getParameter4096: function () {
return this.getParameter(4097);
}
,
//获取参数的个数为4097的参数
getParameter4097: function () {
return this.getParameter(4098);
}
,
getParameter: function (count) {
return new Array(count).join("x");
}
,
passValueParameter4096: function () {
this.passValueParameter(this.getParameter4096());
}
,
passValueParameter4097: function () {
this.passValueParameter(this.getParameter4097());
}
,
//参数作为值类型进行传递
passValueParameter: function (parameter) {
this.showModelDialog(this.url, parameter.toString());
}
,
showModelDialog: function (url, parameter) {
window.showModalDialog(url, parameter);
}
};
我们调用PassParameterTester.passValueParameter4097(),通过以下图片中显示的长度,我们可以看到字符串被截断了。
图 1. 过长字符串经历传递被截断后的长度
那么现在我们该怎么做了,我们既不能限制客户配置产生的字符串的长度,同时也不能从数据库、或者文件中获取,除了形成数据源需要复杂的业务逻辑,同时子页面也需要反复的保存客户的临时数据。那我是最终怎么实现的呢?我们知道js对象可以在创建后动态的添加成员,所以我将xml字符串赋值给window对象的formXml属性,然后在子页面中通过传递过去的window来获取。
后来静下来仔细想想,难道我们只能使用window对象吗?window是引用类型变量,那是不是任何引用变量都可以呢?我们可以通过以下的例子进行验证
//测试类
var PassParameterTester = {
//新的页面地址
url: "NewPage.htm"
,
//获取字符个数为4096的参数
getParameter4096: function () {
return this.getParameter(4097);
}
,
//获取参数的个数为4097的参数
getParameter4097: function () {
return this.getParameter(4098);
}
,
getParameter: function (count) {
return new Array(count).join("x");
}
,
passReferenceParameter4096: function () {
this.passReferenceParameter(this.getParameter4096());
}
,
passReferenceParameter4097: function () {
this.passReferenceParameter(this.getParameter4097());
}
,
//以引用类型进行传递
passReferenceParameter: function (parameter) {
this.showModelDialog(this.url, { arg: parameter })
}
,
showModelDialog: function (url, parameter) {
window.showModalDialog(url, parameter);
}
};
图 2. 大于4096的字符串成功传递了
从图中我们看到,参数已经被正确的传递过去了!那么这又是为什么呢?这个大于4096的字符串怎么传递过去的呢?难道这就是地址传参吗,我们知道js里是没有地址传参这回事的,其参数都是传值的,就是java和C#一般的情况下参数也是传值的,但是我们在C#里可以通过in、out、ref来实现地址传参的。
我们都知道传值传参的时候,我们在函数中改变了参数的值,其对应的变量的值并不改变,值类型传参就是将变量保存的内容复制到函数的形参中,他们是两个不同的变量,值不过保存的内容相同不了,具体如图3
图 3. 值类型传参
引用变量保存的是一个地址,这个地址里保存的是变量的具体值,而引用类型作为参数的时候,是将变量保存的地址值赋值到参数变量里,这样他们都指向了同一个内容,这样我们改变参数的成员的话,那么相应的变量的成员也会改变。
图 4. 引用类型传值传参
如果我们在函数中为参数重新赋值,那么对应的变量是不是也改变呢?答案是显然的,传值传参是不可能改变外部变量的。
图 5. 引用类型内部赋值
同样是传值传参,引用类型和值类型传参的本质是一样的,都是将变量保存的东西直接复制给形参,其实这跟赋值操作是一样的。但是由于引用对象的地址是一个数字,所以传参的时候,不过引用类型的值具体是多么大的数据,我们都可以安全的传递。