<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>小强的博客</title><link>http://qihang.net/u/xiaoqiang/Blog/Default.aspx</link><ttl>60</ttl><description /><generator>SpaceBuilder v1.1</generator><dc:language>zh-CN</dc:language><item><title>JavaScript 技巧与高级特性</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/12/03/4214.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Wed, 03 Dec 2008 02:24:11 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4214</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4214</comments><slash:comments>0</slash:comments><description><![CDATA[<blockquote>随着 <a href="http://www.ibm.com/developerworks/cn/ajax/"><span style="color: #5c81a7;">Ajax</span></a> 应用的流行，JavaScript 语言得到了越来越多的关注。开发人员对 JavaScript 的使用也日益深入。 JavaScript 已经不再只是用来为页面添加一些花哨的效果，它已经成为构建 Ajax 应用的重要基石。 JavaScript 作为一种专门设计用来在浏览器中执行的动态语言，它有许多重要的特性，并且不同于传统的 Java 或 C++ 语言。熟悉这些特性可以帮助开发者更好的开发 Ajax 应用。本文章介绍了 JavaScript 语言中十三个比较重要的特性，包括 prototype、执行上下文、作用域链和闭包等。</blockquote>
<p></p>
<p><a name="null"><span>null 与 undefined</span></a></p>
<p>JavaScript 中一共有 5 种基本类型，分别是 String、Number、Boolean、Null 和 Undefined 。前 3 种都比较好理解，后面两种就稍微复杂一点。 Null 类型只有一个值，就是 null ； Undefined 类型也只有一个值，即 undefined 。 null 和 undefined 都可以作为字面量（literal）在 JavaScript 代码中直接使用。</p>
<p>null 与对象引用有关系，表示为空或不存在的对象引用。当声明一个变量却没有给它赋值的时候，它的值就是 undefined 。</p>
<p>undefined 的值会出现在如下情况： </p>
<ul>
<li>从一个对象中获取某个属性，如果该对象及其 <a href="http://www.qihang.net/u/Blog/#prototype"><span style="color: #5c81a7;">prototype 链</span></a> 中的对象都没有该属性的时候，该属性的值为 undefined 。 </li>
<li>一个 function 如果没有显式的通过 return 来返回值给其调用者的话，其返回值就是 undefined 。有一个特例就是在使用<a href="http://www.qihang.net/u/Blog/#new"><span style="color: #5c81a7;">new</span></a>的时候。 </li>
<li>JavaScript 中的 function 可以声明任意个形式参数，当该 function 实际被调用的时候，传入的参数的个数如果小于声明的形式参数，那么多余的形式参数的值为 undefined 。 </li>
</ul>
<p>&nbsp;</p>
<p>关于 null 和 undefined 有一些有趣的特性： </p>
<ul>
<li>如果对值为 null 的变量使用 typeof 操作符的话，得到的结果是 object ；而对 undefined 的值使用 typeof，得到的结果是 undefined 。如 typeof null === "object";typeof undefined === "undefined" </li>
<li>null == undefined，但是 null !== undefined </li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="boolean"><span>if ("" || 0)</span></a></p>
<p>对于 if 表达式，大家都不陌生。 JavaScript 中 if 后面紧跟的表达式的真假值判断与其它语言有所不同。具体请看<a href="http://www.qihang.net/u/Blog/#table1"><span style="color: #5c81a7;">表 1</span></a>。</p>
<p><br /><a name="table1"><strong>表 1. JavaScript 中的真假值</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<th>类型</th><th>真假值</th>
</tr>
<tr>
<th>Null</th>
<td>总是为假（false）</td>
</tr>
<tr>
<th>Undefined</th>
<td>总是为假（false）</td>
</tr>
<tr>
<th>Boolean</th>
<td>保持真假值不变</td>
</tr>
<tr>
<th>Number</th>
<td>+0，-0 或是 NaN 的时候为假，其它值为真</td>
</tr>
<tr>
<th>String</th>
<td>空字符串的时候为假，其它值为真</td>
</tr>
<tr>
<th>Object</th>
<td>总是为真（true）</td>
</tr>
</tbody>
</table>
<p>从<a href="http://www.qihang.net/u/Blog/#table1"><span style="color: #5c81a7;">表 1</span></a>中可以看到，在 JavaScript 中使得 if 判断为假的值可能有 null、undefined、false、+0、-0、NaN 和空字符串（""）。</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="equal"><span>== 与 ===</span></a></p>
<p>JavaScript 中有两个判断值是否相等的操作符，== 与 === 。两者相比，== 会做一定的类型转换；而 === 不做类型转换，所接受的相等条件更加严格。</p>
<p><a name="N1010B"><span><strong><span style="font-size: small; font-family: Arial;">=== 操作符的判断算法</span></strong></span></a></p>
<p>在使用 === 来判断两个值是否相等的时候，如判断x===y，会首先比较两个值的类型是否相等，如果不相等的话，直接返回 false 。接着根据 x 的类型有不同的判断逻辑。 </p>
<ul>
<li>如果 x 的类型是 Undefined 或 Null，则返回 true 。 </li>
<li>如果 x 的类型是 Number，只要 x 或 y 中有一个值为 NaN，就返回 false ；如果 x 和 y 的数字值相等，就返回 true ；如果 x 或 y 中有一个是 +0，另外一个是 -0，则返回 true 。 </li>
<li>如果 x 的类型是 String，当 x 和 y 的字符序列完全相同时返回 true，否则返回 false 。 </li>
<li>如果 x 的类型是 Boolean，当 x 和 y 同为 true 或 false 时返回 true，否则返回 false 。 </li>
<li>当 x 和 y 引用相同的对象时返回 true，否则返回 false 。 </li>
</ul>
<p>&nbsp;</p>
<p><a name="N1012A"><span><strong><span style="font-size: small; font-family: Arial;">== 操作符的判断算法</span></strong></span></a></p>
<p>在使用 == 来判断两个值是否相等的时候，如判断x==y，当 x 和 y 的类型一样的时候，判断逻辑与 === 一样；如果 x 和 y 的类型不一样，== 不是简单的返回 false，而是会做一定的类型转换。 </p>
<ul>
<li>如果 x 和 y 中有一个是 null，另外一个是 undefined 的话，返回 true 。如null == undefined。 </li>
<li>如果 x 和 y 中一个的类型是 String，另外一个的类型是 Number 的话，会将 String 类型的值转换成 Number 来比较。如3 == "3"。 </li>
<li>如果 x 和 y 中一个的类型是 Boolean 的话，会将 Boolean 类型的值转换成 Number 来比较。如true == 1、true == "1" </li>
<li>如果 x 和 y 中一个的类型是 String 或 Number，另外一个的类型是 Object 的话，会将 Object 类型的值转换成基本类型来比较。如[3,4] == "3,4" </li>
</ul>
<p>需要注意的是 == 操作符不一定是传递的，即从A == B, B == C并不能一定得出A == C。考虑下面的例子，var str1 = new String("Hello"); var str2 = new String("Hello"); str1 == "Hello"; str2 == "Hello"，但是str1 != str2。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="array"><span>Array</span></a></p>
<p>JavaScript 中的数组（Array）和通常的编程语言，如 Java 或是 C/C++ 中的有很大不同。在 JavaScript 中的对象就是一个无序的关联数组，而 Array 正是利用 JavaScript 中对象的这种特性来实现的。在 JavaScript 中，Array 其实就是一个对象，只不过它的属性名是整数，另外有许多额外的属性（如 length）和方法（如 splice）等方便地操作数组。</p>
<p><a name="N10173"><span><strong><span style="font-size: small; font-family: Arial;">创建数组</span></strong></span></a></p>
<p>创建一个 Array 对象有两种方式，一种是以数组字面量的方式，另外一种是使用 Array 构造器。数组字面量的方式通常为大家所熟知。如var array1 = [2, 3, 4];。使用 Array 构造器有两种方式，一种是var array2 = new Array(1, 2, 3);；另外一种是var array3 = Array(1, 2, 3);。这两种使用方式的是等价的。使用 Array 构造器的时候，除了以初始元素作为参数之后，也可以使用数组大小作为参数。如var array4 = new Array(3);用来创建一个初始大小为 3 的数组，其中每个元素都是 undefined 。</p>
<p><a name="N1018B"><span><strong><span style="font-size: small; font-family: Arial;">Array 的方法</span></strong></span></a></p>
<p>JavaScript 中的 Array 提供了很多方法。</p>
<ul>
<li>push和pop在数组的末尾进行操作，使得数组可以作为一个栈来使用。 </li>
<li>shift和unshift在数组的首部进行操作。 </li>
<li>slice(start, end)用来取得原始数组的子数组。其中参数start和end都可以是负数。如果是负数的话，实际使用的值是参数的原始值加上数组的长度。如var array = [2, 3, 4, 5]; array.slice(-2, -1);等价于array.slice(2, 3)。 </li>
<li>splice是最复杂的一个方法，它可以同时删除和添加元素。该方法的第一个参数表示要删除的元素的起始位置，第二个参数表示要删除的元素个数，其余的参数表示要添加的元素。如代码var array = [2, 3, 4, 5]; array.splice(1, 2, 6, 7, 8);执行之后，array中的元素为[2, 6, 7, 8, 5]。该方法的返回被删除的元素。 </li>
</ul>
<p><a name="N101DA"><span><strong><span style="font-size: small; font-family: Arial;">length</span></strong></span></a></p>
<p>JavaScript 中数组的 length 属性与其他语言中有很大的不同。在 Java 或是 C/C++ 语言中，数组的 length 属性都是用来表示数组中的元素个数。而 JavaScript 中，length 属性的值只是 Array 对象中最大的整数类型的属性的值加上 1 。当通过 [] 操作符来给 Array 对象增加元素的时候，如果 [] 中表达式的值可以转换为正整数，并且其值大于或等于 Array 对象当前的 length 的值的话，length 的值被设置成该值加上 1 。 length 属性也可以显式的设置。如果要设置的值比原来的 length 值小的话，该 Array 对象中所有大于或等于新值的整数键值的属性都会被删除。如<a href="http://www.qihang.net/u/Blog/#code1"><span style="color: #5c81a7;">代码清单 1</span></a>中所示。</p>
<p><br /><a name="code1"><strong>清单 1. Array 的 length 属性</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>var array = []; 
 array[0] = "a"; 
 array[100] = "b"; 
 array.length;     // 值为 101 
 array["3"] = "c"; 
 array.length = 4; // 值为 "b" 的第 101 个元素被删除</pre>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="arguments"><span>arguments</span></a></p>
<p>在 JavaScript 中，在一个 function 内部可以使用 arguments 对象。该对象中包含了 function 被调用时的实际参数的值。 arguments 对象虽然在功能上有些类似数组（<a href="http://www.qihang.net/u/Blog/#array"><span style="color: #5c81a7;">Array</span></a>），但是它不是数组。 arguments 对象与数组的类似体现在它有一个 length 属性，同时实际参数的值可以通过 [] 操作符来获取。但是 arguments 对象并没有数组可以使用的 push、pop、splice 等 function 。其原因是 arguments 对象的 prototype 指向的是 Object.prototype 而不是 Array.prototype 。</p>
<p><a name="N101FC"><span><strong><span style="font-size: small; font-family: Arial;">使用 arguments 模拟重载</span></strong></span></a></p>
<p>Java 和 C++ 语言都支持方法重载（overloading），即允许出现名称相同但是形式参数不同的方法；而 JavaScript 并不支持这种方式的重载。因为 JavaScript 中的 function 对象也是以属性的形式出现的，在一个对象中增加与已有 function 同名的新 function 时，旧的 function 对象会被覆盖。不过可以通过使用 arguments 来模拟重载，其实现机制是通过判断 arguments 中实际参数的个数和类型来执行不同的逻辑。如<a href="http://www.qihang.net/u/Blog/#code2"><span style="color: #5c81a7;">代码清单 2</span></a>中所示。</p>
<p><br /><a name="code2"><strong>清单 2. 使用 arguments 模拟重载示例</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function sayHello() { 
    switch (arguments.length) { 
        case 0: return "Hello"; 
        case 1: return "Hello, " + arguments[0]; 
        case 2: return (arguments[1] == "cn" ? " 你好，" : "Hello, ") + arguments[0]; 
   }; 
 } 

 sayHello();              // 结果是 "Hello" 
 sayHello("Alex");        // 结果是 "Hello, Alex" 
 sayHello("Alex", "cn");  // 结果是 " 你好，Alex"</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p><a name="N10211"><span><strong><span style="font-size: small; font-family: Arial;">arguments.callee</span></strong></span></a></p>
<p>callee 是 arguments 对象的一个属性，其值是当前正在执行的 function 对象。它的作用是使得匿名 function 可以被递归调用。下面以一段计算斐波那契序列（Fibonacci sequence）中第 N 个数的值的代码来演示 arguments.callee 的使用，见<a href="http://www.qihang.net/u/Blog/#code3"><span style="color: #5c81a7;">代码清单 3</span></a>。</p>
<p><br /><a name="code3"><strong>清单 3. arguments.callee 示例</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function fibonacci(num) { 
    return (function(num) { 
        if (typeof num !== "number") return -1; 
        num =  parseInt(num); 
        if (num &lt; 1) return -1; 
        if (num == 1 || num == 2) return 1; 
        return arguments.callee(num - 1) + arguments.callee(num - 2); 
    })(num); 
 } 

 fibonacci(100);</pre>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="prototype"><span>prototype 与继承</span></a></p>
<p>JavaScript 中的每个对象都有一个 prototype 属性，指向另外一个对象。使用对象字面量创建的对象的 prototype 指向的是Object.prototype，如var obj = {"name" : "Alex"};中创建的对象obj的 prototype 指向的就是Object.prototype。而使用 new 操作符创建的对象的 prototype 指向的是其构造器的 prototype 。如var users = new Array();中创建的对象users的 prototype 指向的是Array.prototype。由于一个对象 A 的 prototype 指向的是另外一个对象 B，而对象 B 自己的 prototype 又指向另外一个对象 C，这样就形成了一个链条，称为 prototype 链。这个链条会不断继续，一直到Object.prototype。Object.prototype对象的 prototype 值为 null，从而使得该链条终止。<a href="http://www.qihang.net/u/Blog/#fig-prototype-chain"><span style="color: #5c81a7;">图 1</span></a>中给出了 prototype 链的示意图。</p>
<p><br /><a name="fig-prototype-chain"><strong>图 1. JavaScript prototype 链示意图</strong></a><br /><img src="http://www.qihang.net/u/Blog/prototype-chain.jpg" alt="JavaScript prototype链示例图" width="572" height="559" /> </p>
<p>在<a href="http://www.qihang.net/u/Blog/#fig-prototype-chain"><span style="color: #5c81a7;">图 1</span></a>中，studentA是通过 new 操作符创建的，因此它的 prototype 指向其构造器Student的 prototype ；Student.prototype的值是通过 new 操作符创建的，其 prototype 指向构造器Person的 prototype 。studentA的 prototype 链在<a href="http://www.qihang.net/u/Blog/#fig-prototype-chain"><span style="color: #5c81a7;">图 1</span></a>中用虚线表示。</p>
<p>prototype 链在属性查找过程中会起作用。当在一个对象中查找某个特定名称的属性时，会首先检查该对象本身。如果找到的话，就返回该属性的值；如果找不到的话，会检查该对象的 prototype 指向的对象。如此下去直到找到该属性，或是当前对象的 prototype 为 null 。 prototype 链在设置属性的值时并不起作用。当设置一个对象中某个属性的值的时候，如果当前对象中存在这个属性，则更新其值；否则就在当前对象中创建该属性。</p>
<p>JavaScript 中并没有 Java 或 C++ 中类（class）的概念，而是通过 prototype 链来实现基于 prototype 的继承。在 Java 中，状态包含在对象实例中，方法包含在类中，继承只发生在结构和行为上。而在 JavaScript 中，状态和方法都包含在对象中，结构、行为和状态都是被继承的。这里需要注意的是 JavaScript 中的状态也是被继承的，也就是说，在构造器的 prototype 中的属性是被所有的实例共享的。如<a href="http://www.qihang.net/u/Blog/#code4"><span style="color: #5c81a7;">代码清单 4</span></a>中所示。</p>
<p><br /><a name="code4"><strong>清单 4. JavaScript 中状态被继承的示例</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function Student(name) { 
   this.name = name; 
 } 

 Student.prototype.selectedCourses = []; 

 Student.prototype.addCourse = function(course) { 
  this.selectedCourses.push(course); 
 } 

 Student.prototype.outputCourses = function() { 
  alert(this.name + " 选修的课程是：" + this.selectedCourses.join(",")); 
 } 

 var studentA = new Student("Alex"); 
 var studentB = new Student("Bob"); 

 studentA.addCourse(" 算法分析与设计 "); 
 studentB.addCourse(" 数据库原理 "); 
 studentA.outputCourses(); // 输出是“ Alex 选修的课程是算法分析与设计 , 数据库原理”
 studentB.outputCourses(); // 输出同上</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p><a href="http://www.qihang.net/u/Blog/#code4"><span style="color: #5c81a7;">代码清单 4</span></a>中的问题在于将selectedCourses作为 prototype 的属性之后，studentA和studentB两个实例共享了该属性，它们操作的实际是同样的数据。</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="this"><span>this</span></a></p>
<p>JavaScript 中的 this 一直是容易让人误用的，尤其对于熟悉 Java 的程序员来说，因为 JavaScript 中的 this 与 Java 中的 this 有很大不同。在一个 function 的执行过程中，如果变量的前面加上了 this 作为前缀的话，如this.myVal，对此变量的求值就从 this 所表示的对象开始。</p>
<p>this 的值取决于 function 被调用的方式，一共有四种，具体如下： </p>
<ul>
<li>如果一个 function 是一个对象的属性，该 funtion 被调用的时候，this 的值是这个对象。如果 function 调用的表达式包含句点（.）或是 []，this 的值是句点（.）或是 [] 之前的对象。如myObj.func和myObj["func"]中，func被调用时的 this 是myObj。 </li>
<li>如果一个 function 不是作为一个对象的属性，那么该 function 被调用的时候，this 的值是全局对象。当一个 function 中包含内部 function 的时候，如果不理解 this 的正确含义，很容易造成错误。这是由于内部 function 的 this 值与它外部的 function 的 this 值是不一样的。<a href="http://www.qihang.net/u/Blog/#code5"><span style="color: #5c81a7;">代码清单 5</span></a>中，在myObj的func中有个内部名为inner的 function，在inner被调用的时候，this 的值是全局对象，因此找不到名为myVal的变量。这个时候通常的解决办法是将外部 function 的 this 值保存在一个变量中（此处为self），在内部 function 中使用它来查找变量。 </li>
<li>如果在一个 function 之前使用 new 的话，会创建一个新的对象，该 funtion 也会被调用，而 this 的值是新创建的那个对象。如function User(name) {this.name = name}; var user1 = new User("Alex");中，通过调用new User("Alex")，会创建一个新的对象，以user1来引用，User这个 function 也会被调用，会在user1这个对象中设置名为name的属性，其值是Alex。 </li>
<li>可以通过 function 的 apply 和 call 方法来指定它被调用的时候的 this 的值。 apply 和 call 的第一个参数都是要指定的 this 的值，两者不同的是调用的实际参数在 apply 中是以数组的形式作为第二个参数传入的，而 call 中除了第一个参数之外的其它参数都是调用的实际参数。如func.apply(anotherObj, [arg1, arg2])中，func调用时候的 this 指的是anotherObj，两个参数分别是arg1和arg2。同样的功能用 call 来写则是func.call(anotherObj, arg1, arg2)。 </li>
</ul>
<p>&nbsp;</p>
<p><br /><a name="code5"><strong>清单 5. 内部 function 的 this 值</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>var myObj = { 
  myVal : "Hello World", 
  func : function() { 
     alert(typeof this.myVal);    // 结果为 string 
     var self = this; 
     function inner() { 
       alert(typeof this.myVal);  // 结果为 undefined 
       alert(typeof self.myVal);  // 结果为 string 
     }  
     inner(); 
  } 
 }; 

 myObj.func();</pre>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="new"><span>new</span></a></p>
<p>JavaScript 中并没有 Java 或是 C++ 中的类（class）的概念，而是采用构造器（constructor）的方式来创建对象。在 new 表达式中使用构造器就可以创建新的对象。由构造器创建出来的对象有一个隐含的引用指向该构造器的 prototype 。</p>
<p>所有的构造器都是对象，但并不是所有的对象都能成为构造器。能作为构造器的对象必须实现隐含的[[Construct]方法。如果 new 操作符后面的对象并不是构造器的话，会抛出 TypeError 异常。</p>
<p>new 操作符会影响 function 调用中 return 语句的行为。当 function 调用的时候有 new 作为前缀，如果 return 的结果不是一个对象，那么新创建的对象将会被返回。在<a href="http://www.qihang.net/u/Blog/#code6"><span style="color: #5c81a7;">代码清单 6</span></a>中，functionanotherUser中通过 return 语句返回了一个对象，因此u2引用的是返回的那个对象；而 functionuser并没有使用 return 语句，因此u1引用的是新创建的user对象。</p>
<p><br /><a name="code6"><strong>清单 6. new 操作符对 return 语句行为的影响</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function user(name) { 
    this.name = name; 
 } 

 function anotherUser(name) { 
    this.name = name; 
    return {"badName" : name}; 
 } 

 var u1 = new user("Alex"); 
 alert(typeof u1.name);      // 结果为 string 
 var u2 = new anotherUser("Alex"); 
 alert(typeof u2.name);      // 结果为 undefined 
 alert(typeof u2.badName);   // 结果为 string</pre>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="eval"><span>eval</span></a></p>
<p>JavaScript 中的 eval 可以用来解释执行一段 JavaScript 程序。当传给 eval 的参数的值是字符串的时候，该字符串会被当成一段 JavaScript 程序来执行。</p>
<p><a name="N10370"><span><strong><span style="font-size: small; font-family: Arial;">隐式的 eval</span></strong></span></a></p>
<p>除了显式的调用 eval 之外，JavaScript 中的有些 function 能接受字符形式的 JavaScript 代码并执行，这相当于隐式的调用了 eval 。这些 function 的典型代表是setTimeout和setInterval。具体请见<a href="http://www.qihang.net/u/Blog/#code7"><span style="color: #5c81a7;">代码清单 7</span></a>。由于 eval 的性能比较差，所以在使用setTimeout和setInterval等 function 的时候，最好传入 function 的引用，而不是字符串。</p>
<p><br /><a name="code7"><strong>清单 7. 隐式的 eval 示例</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>var obj = { 
    show1 : function() { alert(" 时间到！ "); }, 
    show2 : function() { alert("10 秒一次的提醒！ "); }; 
 }; 

 setTimeout(obj.show1, 1000); 
 setTimeout("obj.show1();", 2000); 

 setInterval(obj.show2, 10000); 
 setInterval("obj.show2();", 10000);</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p><a name="N10395"><span><strong><span style="font-size: small; font-family: Arial;">eval 的潜在危险</span></strong></span></a></p>
<p>在目前的 Ajax 应用中，JSON 是一种流行的浏览器端和服务器端处之间传输数据的格式。服务器端传过来的数据在浏览器端通过 JavaScript 的 eval 方法转换成可以直接使用的对象。然而，在浏览器端执行任意的 JavaScript 会带来潜在的安全风险，恶意的 JavaScript 代码可能会破坏应用。对于这个问题，有两种解决方式： </p>
<strong>带注释的 JSON（JSON comments filtering）和带前缀的 JSON（JSON prefixing）</strong> 这两种方法都是 Dojo 中用来避免 JSON 劫持（JSON hijacking）的。带注释的 JSON 指的是从服务器端返回的 JSON 数据都是带有注释的，浏览器端的 JavaScript 代码需要先去掉注释的标记，再通过 eval 来获得 JSON 数据。这种方法一度被广泛使用，后来被证明并不安全，还会引入其它的安全漏洞。带前缀的 JSON 是目前推荐使用的方法，这种方法的使用非常简单，只需要在从服务器端的 JSON 字符串之前加上{} &amp;&amp;，再调用 eval 。关于这两种方法的细节，请看<a href="http://www.qihang.net/u/Blog/#resources"><span style="color: #5c81a7;">参考资料</span></a>。 <strong>对 JSON 字符串进行语法检查</strong> 安全的 JSON 应该是不包含赋值和方法调用的。在 JSON 的 RFC 4627 中，给出了判断 JSON 字符串是否安全的方法，是通过两个正则表达式来实现的。具体见<a href="http://www.qihang.net/u/Blog/#code8"><span style="color: #5c81a7;">代码清单 8</span></a>。关于 RFC 4627 的细节，请看<a href="http://www.qihang.net/u/Blog/#resources"><span style="color: #5c81a7;">参考资料</span></a>。 
<p>&nbsp;</p>
<p><br /><a name="code8"><strong>清单 8. RFC 4627 中给出的检查 JSON 字符串的方法</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u 
\t]/.test( 
             text.replace(/"(\\.|[^"\\])*"/g, ''))) &amp;&amp; 
 eval('(' + text + ')');</pre>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="ecsc"><span>执行上下文（execution context）和作用域链（scope chain）</span></a></p>
<p>执行上下文（execution context）是 ECMAScript 规范（请看<a href="http://www.qihang.net/u/Blog/#resources"><span style="color: #5c81a7;">参考资料</span></a>）中用来描述 JavaScript 代码执行的抽象概念。所有的 JavaScript 代码都是在某个执行上下文中运行的。在当前执行上下文中调用 function 的时候，会进入一个新的执行上下文。当该 function 调用结束的时候，会返回到原来的执行上下文中。如果 function 调用过程中抛出异常，并没有被捕获的话，有可能从多个执行上下文中退出。在 function 调用过程，也可能调用其它的 function，从而进入新的执行上下文。由此形成一个执行上下文栈。</p>
<p>每个执行上下文都与一个作用域链（scope chain）关联起来。该作用域链用来在 function 执行时求标识符（Identifier）的值。在该链中包含多个对象。在对标识符进行求值的过程中，会从链首的对象开始，然后依次查找后面的对象，直到在某个对象中找到与标识符名称相同的属性。如&rdquo;<a href="http://www.qihang.net/u/Blog/#prototype"><span style="color: #5c81a7;">protype 链与继承&ldquo;</span></a>中所述，在每个对象中进行属性查找的时候，会使用该对象的 prototype 链。在一个执行上下文中，与其关联的作用域链只会被<a href="http://www.qihang.net/u/Blog/#with"><span style="color: #5c81a7;">with</span></a>语句和 catch 子句影响。</p>
<p>在进入一个新的执行上下文的时候，会按顺序执行下面的操作： </p>
<ul>
<li>创建激活（Activation）对象<br />激活对象是在进入新的执行上下文的时候被创建出来的，并与新的执行上下文关联起来。在初始化的时候，该对象包含一个名为<a href="http://www.qihang.net/u/Blog/#arguments"><span style="color: #5c81a7;">arguments</span></a>的属性。激活对象在变量初始化的时候也会被使用。 JavaScript 代码不能直接访问该对象，但是可以访问该对象里面的成员（如 arguments）。 </li>
<li>创建作用域链<br />接下来的操作是创建作用域链。每个 function 都有一个内部属性[[scope]]，它的值是一个包含多个对象的链。该属性的具体值与 function 的创建方式和在代码中的位置有很大关系（见&ldquo;<a href="http://www.qihang.net/u/Blog/#function"><span style="color: #5c81a7;">function 对象的创建方式</span></a>&rdquo;）。这个步骤中的主要操作是将上一步中创建的激活对象添加到 function 的[[scope]]属性对应的链的前面。 </li>
<li>变量初始化<br />该步骤对 function 中需要使用的变量进行初始化。初始化时使用的对象是第一步中所创建的激活对象，不过被称之为变量（Variable）对象。会被初始化的变量包括 function 调用时的实际参数、内部 function 和局部变量。在这个步骤中，对于局部变量，只是在变量对象中创建了同名的属性，但是属性的值为 undefined，只有在 function 执行过程中才会被真正赋值。 </li>
</ul>
<p>全局 JavaScript 代码是在全局执行上下文中运行的，该上下文的作用域链只包含一个全局对象。 </p>
<p>&nbsp;</p>
<p><a href="http://www.qihang.net/u/Blog/#fig-scope-chain"><span style="color: #5c81a7;">图 2</span></a>中给出了 function 执行过程中的作用域链的示意图，其中的虚线表示作用域链。</p>
<p><br /><a name="fig-scope-chain"><strong>图 2. 作用域链示意图</strong></a><br /><img src="http://www.qihang.net/u/Blog/scope-chain.jpg" alt="作用域链示意图" width="572" height="548" /> <br /></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="function"><span>function a() {}、var a = function() {} 与 var a = new Function()</span></a></p>
<p>在 JavaScript 中，function 对象的创建方式有三种：function 声明、function 表达式和使用 Function 构造器。通过这三种方法创建出来的 function 对象的[[scope]]属性的值会有所不同，从而影响 function 执行过程中的作用域链。下面具体讨论这三种情况。 </p>
<strong>function 声明</strong> function 声明的格式是function funcName() {}。使用 function 声明的 function 对象是在进入执行上下文时的变量初始化过程中创建的。该对象的[[scope]]属性的值是它被创建时的执行上下文对应的作用域链。 <strong>function 表达式</strong> function 表达式的格式是var funcName = function() {}。使用 function 表达式的 function 对象是在该表达式被执行的时候创建的。该对象的[[scope]]属性的值与使用 function 声明创建的对象一样。 <strong>Function 构造器</strong> 对于 Function 构造器，大家可能比较陌生。声明一个 function 时，通常使用前两种方式。该方式的格式是var funcName = new Function(p1, p2,..., pn, body)，其中 p1、p2 到 pn 表示的是该 function 的形式参数，body 是 function 的内容。使用该方式的 function 对象是在构造器被调用的时候创建的。该对象的[[scope]]属性的值总是一个只包含全局对象的作用域链。 
<p>function 对象的 length 属性可以用来获取声明 function 时候指定的形式参数的个数。如前所述，function 对象被调用时的实际参数是通过 arguments 来获取的。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="N10452"><span>with</span></a></p>
<p>with 语句的语法是with ( BLOCKED EXPRESSION Statement。 with 会把由 Expression 计算出来的对象添加到当前执行上下文的作用域链的前面，然后使用这个扩大的作用域链来执行语句 Statement，最后恢复作用域链。不管里面的语句是否正常退出，作用域链都会被恢复。</p>
<p>由于 with 语言会把额外的对象添加到作用域链的前面，使用 with 可能会影响性能并造成难以发现的错误。由于额外的对象在作用域链的前面，当执行到 with 里面的语句，需要对一个标识符求值的时候，会首先沿着该对象的 prototype 链查找。如果找不到，才会依次查找作用域链中原来的对象。因此，如果在 with 里面的语句中频繁引用不在额外对象的 prototype 链中的变量的话，查找的速度会比不使用 with 慢。具体见<a href="http://www.qihang.net/u/Blog/#code9"><span style="color: #5c81a7;">代码清单 9</span></a>。</p>
<p><br /><a name="code9"><strong>清单 9. with 的用法示例</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function A() { 
    this.a = "A"; 
 } 

 function B() { 
    this.b = "B"; 
 } 

 B.prototype = new A(); 

 function C() { 
    this.c = "C"; 
 } 

 C.prototype = new B(); 

 (function () { 
    var myVar = "Hello World"; 
    alert(typeof a);    // 结果是 "undefined" 
    var a = 1; 
    var obj = new C(); 
    with (obj) { 
        alert(typeof a); // 结果是 "string" 
        alert(myVar);    // 查找速度比较慢
    } 
    alert(typeof a);     // 结果是 "number" 
 })();</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>在代码中，首先通过 prototype 的方式实现了继承。在 with 中，执行alert(typeof a)需要查找变量 a，由于 obj 在作用域链的前面，而 obj 中也存在名为 a 的属性，因此 obj 中的 a 被找到。执行alert(myVar)需要查找变量 myVal，而 obj 中不存在名为 myVal 的属性，会继续查找作用域链中后面的对象，因此比不使用 with 的速度慢。需要注意的是最后一条语句alert(typeof a)，它不在 with 里面，因此查找到的 a 是之前声明的 number 型的变量。</p>
<p>&nbsp;</p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br /><img src="http://www.ibm.com/i/c.gif" border="0" alt="" width="8" height="6" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" align="right">
<tbody>
<tr align="right">
<td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" alt="" width="16" height="16" /><br /></td>
<td align="right"><a href="http://www.qihang.net/u/Blog/#main"><strong><span style="color: #5c81a7;">回页首</span></strong></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p><br /></p>
<p><a name="N1047E"><span>闭包</span></a></p>
<p>闭包（closure）是 JavaScript 中一个非常强大的功能。如果使用得当的话，可以使得代码更简洁，并实现在其它语言中很难实现的功能；而如果使用不当的话，则会导致难以调试的错误，也可能造成内存泄露。只有在充分理解闭包的基础上，才能正确的使用它。理解闭包需要首先理解 JavaScript 中的<a href="http://www.qihang.net/u/Blog/#prototype"><span style="color: #5c81a7;">prototype 链</span></a>、<a href="http://www.qihang.net/u/Blog/#ecsc"><span style="color: #5c81a7;">执行上下文和作用域链</span></a>等概念。</p>
<p>闭包指的是一个表达式（通常是一个 function），该表达式可以有自由的变量，并且运行环境能够正确的获取这些变量的值。 JavaScript 中闭包的产生是由于 JavaScript 中允许内部 function，也就是在一个 function 内部声明的 function 。内部 function 可以访问外部 function 中的局部变量、传入的参数和其它内部 function 。当内部 function 可以在包含它的外部 function 之外被引用时，就形成了一个闭包。这个时候，即便外部 function 已经执行完成，该内部 function 仍然可以被执行，并且其中所用到的外部 function 的局部变量、传入的参数等仍然保留外部 function 执行结束时的值。</p>
<p>下面通过一个例子来说明闭包的形成，见<a href="http://www.qihang.net/u/Blog/#code10"><span style="color: #5c81a7;">代码清单 10</span></a>。</p>
<p><br /><a name="code10"><strong>清单 10. JavaScript 闭包示例代码</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function addBy(first) { 
  function add(second) { 
    return first + second; 
  } 
  return add; 
 } 

 var func = addBy(10); 
 func(20);  // 结果为 30 
 var newFunc = addBy(30); 
 newFunc(20); // 结果为 50</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>在<a href="http://www.qihang.net/u/Blog/#code10"><span style="color: #5c81a7;">代码清单 10</span></a>中，外部 functionaddBy的内部 functionadd的引用被返回给addBy的调用者，同时add在方法体中使用了addBy的参数first。这样就形成了一个闭包。通过调用addBy(10)得到的 functionfunc，在其之后的执行过程中，都会保留创建的时候使用的first参数的值10。</p>
<p>下面分析<a href="http://www.qihang.net/u/Blog/#code10"><span style="color: #5c81a7;">代码清单 10</span></a>中执行的细节。首先addBy(10)被调用。由于addBy是在全局代码中声明的，因此被调用时候的执行上下文对应的作用域链只包含全局对象。在addBy的方法体中，声明了一个内部 functionadd。add的[[scope]]属性会在作用域链之前加上 functionaddBy的激活对象。该对象中包含了经过初始化的参数first，其值为10。至此，functionfunc的[[scope]]属性的值是包含两个对象。当func被调用的时候，会进入一个新的执行上下文，而此时的作用域链的前面加上了 functionadd调用时的激活对象。该对象中包含了经过初始化的参数second，其值为20。在func的执行过程中，需要对两个标识符first和second求值的时候，会使用之前提到的包含三个对象的作用域链。从而可以正确的求值。</p>
<p>在 JavaScript 中，正确的使用闭包可以简化代码。下面举几个例子来说明。</p>
<p><a name="N10523"><span><strong><span style="font-size: small; font-family: Arial;">避免名称空间冲突</span></strong></span></a></p>
<p>在多人协作开发应用，或是使用第三方开发的 JavaScript 库的时候，一个通常会遇到的问题是名称空间冲突。比如第三方的 JavaScript 库在全局对象中声明了一个属性叫test，如果在自己的代码中也会声明同样名称的属性的话，当两者一同使用的时候，后加载的属性值会替换之前的值，从而造成错误。</p>
<p>这个时候典型的做法是只在全局对象中保存一个对象，所有的功能都通过引用此对象来完成。完成功能所需要的内部状态都封装在一个闭包中。如<a href="http://www.qihang.net/u/Blog/#code11"><span style="color: #5c81a7;">代码清单 11</span></a>所示。</p>
<p><br /><a name="code11"><strong>清单 11. 使用闭包避免名称空间冲突</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>(function() { 
  if (typeof MyCode === "undefined") { 
    var defaultName = "Alex"; 
    MyCode = { 
      "sayHello" : function(name) { 
        alert("Hello, " + (name || defaultName)); 
      } 
    }; 
  } 
 })(); 

 MyCode.sayHello();  // 输出为 Hello, Alex 
 MyCode.sayHello("Bob"); // 输出为 Hello, Bob</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>代码中通过创建一个匿名 function 并立即执行来生成一个闭包。在闭包中，通过修改全局对象MyCode来添加所需的功能。内部状态之一的属性defaultName被封装在闭包中，不能被闭包之外的代码所引用，也不会引发命名冲突。</p>
<p><a name="N1054A"><span><strong><span style="font-size: small; font-family: Arial;">保存状态</span></strong></span></a></p>
<p>在 JavaScript 代码运行过程中，不可避免的需要保存一些内部状态。通过使用闭包，可以将内部状态封装在一个 function 内部，使得代码更加简洁。如<a href="http://www.qihang.net/u/Blog/#code12"><span style="color: #5c81a7;">代码清单 12</span></a>所示。</p>
<p><br /><a name="code12"><strong>清单 12. 使用闭包保存状态</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>var getNextId = (function() { 
  var id = 1; 
  return function() { 
    return id++; 
  } 
 })(); 

 getNextId();  // 输出 1 
 getNextId();  // 输出 2 
 getNextId();  // 输出 3</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>代码中的getNextId的功能是生成惟一的 ID，因此它需要维护当前的 ID 这样一个状态。通过使用闭包，不需要在全局对象中添加一个新的属性，该属性由闭包来维护。闭包之外的代码也不能访问或修改getNextId的内部状态。</p>
<p><a name="N1056A"><span><strong><span style="font-size: small; font-family: Arial;">折叠调用参数</span></strong></span></a></p>
<p>在 JavaScript 中，有些 function，如setTimeout和setInterval，只接受一个 function 作为参数。在有些情况下，这些 function 的执行是需要额外的参数的。这个时候可以通过使用闭包，将原始 function 的参数进行折叠，得到一个没有参数的新 function 。如<a href="http://www.qihang.net/u/Blog/#code13"><span style="color: #5c81a7;">代码清单 13</span></a>所示。</p>
<p><br /><a name="code13"><strong>清单 13. 使用闭包折叠调用参数</strong></a></p>
<table cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<pre>function doSomething(a, b, c) { 
  alert(a + b + c); 
 } 

 function fold(a, b, c) { 
  return function() { 
    doSomething(a, b, c); 
  } 
 } 

 var newFunc = fold("Hello", " ", "World"); 
 setTimeout(newFunc, 1000); // 输出为 Hello World</pre>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>代码中的doSomething需要三个参数来完成其功能。如果直接将doSomething传给setTimeout的话，三个参数的值都是 undefined 。fold将三个参数的值保存在激活对象，并添加在作用域链中。这样即便返回的 function 是没有参数的，它仍然可以获得这三个参数的值。</p>
<p>关于闭包的更多内容，请参见<a href="http://www.qihang.net/u/Blog/#resources"><span style="color: #5c81a7;">参考资料</span></a>。</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4214" width="1" height="1">]]></description></item><item><title>不要用DropDownList.SelectedItem.Text进行赋值进行自动选择</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/10/22/4146.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Wed, 22 Oct 2008 07:46:35 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4146</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4146</comments><slash:comments>0</slash:comments><description><![CDATA[<p>这个方法会改变量前选择项的TEXT.而值确不动</p>
<p>例.有一个下拉框.里面有两个选择.一个是 是 一个是 否</p>
<p>我在页面一加载的时候将下拉列表的SelectedItem.Text="否",想让他自动 选择Index为1的否选项.</p>
<p>而我在页面看到的效果是..两个否.选项.</p>
<p>解决办法</p>
<p>if (ddlNationality2.Items.FindByText(t.Nationality)!=null)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; ddlNationality2.Items.Count; i++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddlNationality2.Items<img src="/Utility/Emoticons/emotion-55.gif" alt="Idea" />.Selected=false;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ddlNationality2.Items<img src="/Utility/Emoticons/emotion-55.gif" alt="Idea" />.Text == t.Nationality)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddlNationality2.Items<img src="/Utility/Emoticons/emotion-55.gif" alt="Idea" />.Selected = true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4146" width="1" height="1">]]></description></item><item><title>七个受用一生的心理寓言 </title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/10/09/4137.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Thu, 09 Oct 2008 04:41:03 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4137</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4137</comments><slash:comments>0</slash:comments><description><![CDATA[<p><br />（一）成长的寓言：做一棵永远成长的苹果树 <br /><br />一棵苹果树，终于结果了。 <br />第一年，它结了10个苹果，9个被拿走，自己得到1个。对此，苹果树愤愤不平，于是自断经脉，拒绝成长。第二年，它结了5个苹果，4个被拿走，自己得到1个。&ldquo;哈哈，去年我得到了10％，今年得到20％！翻了一番。&rdquo;这棵苹果树心理平衡了。 <br />但是，它还可以这样：继续成长。譬如，第二年，它结了100个果子，被拿走90个，自己得到10个。 <br />很可能，它被拿走99个，自己得到1个。但没关系，它还可以继续成长，第三年结1000个果子&hellip;&hellip; <br />其实，得到多少果子不是最重要的。最重要的是，苹果树在成长！等苹果树长成参天大树的时候，那些曾阻碍它成长的力量都会微弱到可以忽略。真的，不要太在乎果子，成长是最重要的。 <br /><br />【心理点评】你是不是一个已自断经脉的打工族？ <br />刚开始工作的时候，你才华横溢，意气风发，相信&ldquo;天生我才必有用&rdquo;。但现实很快敲了你几个闷棍，或许，你为单位做了大贡献没人重视；或许，只得到口头重视但却得不到实惠；或许&hellip;&hellip;总之，你觉得就像那棵苹果树，结出的果子自己只享受到了很小一部分，与你的期望相差甚远。 <br />于是，你愤怒、你懊恼、你牢骚满腹&hellip;&hellip;最终，你决定不再那么努力，让自己的所做去匹配自己的所得。几年过去后，你一反省，发现现在的你，已经没有刚工作时的激情和才华了。 <br />&ldquo;老了，成熟了。&rdquo;我们习惯这样自嘲。但实质是，你已停止成长了。 <br />这样的故事，在我们身边比比皆是。 <br />之所以犯这种错误，是因为我们忘记生命是一个历程，是一个整体，我们觉得自己已经成长过了，现在是到该结果子的时候了。我们太过于在乎一时的得失，而忘记了成长才是最重要的。 <br />好在，这不是金庸小说里的自断经脉。我们随时可以放弃这样做，继续走向成长之路。 <br />切记：如果你是一个打工族，遇到了不懂管理、野蛮管理或错误管理的上司或企业文化，那么，提醒自己一下，千万不要因为激愤和满腹牢骚而自断经脉。不论遇到什么事情，都要做一棵永远成长的苹果树，因为你的成长永远比每个月拿多少钱重要。 <br /><br />（二）动机的寓言：孩子在为谁而玩 <br /><br />一群孩子在一位老人家门前嬉闹，叫声连天。几天过去，老人难以忍受。 <br />于是，他出来给了每个孩子25美分，对他们说：&ldquo;你们让这儿变得很热闹，我觉得自己年轻了不少，这点钱表示谢意。&rdquo; <br />孩子们很高兴，第二天仍然来了，一如既往地嬉闹。老人再出来，给了每个孩子15美分。他解释说，自己没有收入，只能少给一些。15美分也还可以吧，孩子仍然兴高采烈地走了。 <br />第三天，老人只给了每个孩子5美分。 <br />孩子们勃然大怒，&ldquo;一天才5美分，知不知道我们多辛苦！&rdquo;他们向老人发誓，他们再也不会为他玩了！ <br /><br />【心理点评】你在为谁而&ldquo;玩&rdquo; <br />这个寓言是苹果树寓言的更深一层的答案：苹果树为什么会自断经脉，因为它不是为自己而&ldquo;玩&rdquo;。 <br />人的动机分两种：内部动机和外部动机。如果按照内部动机去行动，我们就是自己的主人。如果驱使我们的是外部动机，我们就会被外部因素所左右，成为它的奴隶。 <br />在这个寓言中，老人的算计很简单，他将孩子们的内部动机&ldquo;为自己快乐而玩&rdquo;变成了外部动机&ldquo;为得到美分而玩&rdquo;，而他操纵着美分这个外部因素，所以也操纵了孩子们的行为。寓言中的老人，像不像是你的老板、上司？而美分，像不像是你的工资、奖金等各种各样的外部奖励？ <br />如将外部评价当作参考坐标，我们的情绪就很容易出现波动。因为，外部因素我们控制不了，它很容易偏离我们的内部期望，让我们不满，让我们牢骚满腹。不满和牢骚等负性情绪让我们痛苦，为了减少痛苦，我们就只好降低内部期望，最常见的方法就是减少工作的努力程度。 <br />一个人之所以会形成外部评价体系，最主要的原因是父母喜欢控制他。父母太喜欢使用口头奖惩、物质奖惩等控制孩子，而不去理会孩子自己的动机。久而久之，孩子就忘记了自己的原初动机，做什么都很在乎外部的评价。上学时，他忘记了学习的原初动机&mdash;&mdash;&mdash;好奇心和学习的快乐；工作后，他又忘记了工作的原初动机&mdash;&mdash;&mdash;成长的快乐，上司的评价和收入的起伏成了他工作的最大快乐和痛苦的源头。 <br />切记：外部评价系统经常是一种家族遗传，但你完全可以打破它，从现在开始培育自己的内部评价体系，让学习和工作变成&ldquo;为自己而玩&rdquo;。 <br /><br />（三）规划的寓言：把一张纸折叠51次 <br /><br />想象一下，你手里有一张足够大的白纸。现在，你的任务是，把它折叠51次。那么，它有多高？ <br />一个冰箱？一层楼？或者一栋摩天大厦那么高？不是，差太多了，这个厚度超过了地球和太阳之间的距离。 <br /><br />【心理点评】 <br />到现在，我拿这个寓言问过十几个人了，只有两个人说，这可能是一个想象不到的高度，而其他人想到的最高的高度也就是一栋摩天大厦那么高。 <br />折叠51次的高度如此恐怖，但如果仅仅是将51张白纸叠在一起呢？ <br />这个对比让不少人感到震撼。因为没有方向、缺乏规划的人生，就像是将51张白纸简单叠在一起。今天做做这个，明天做做那个，每次努力之间并没有一个联系。这样一来，哪怕每个工作都做得非常出色，它们对你的整个人生来说也不过是简单的叠加而已。 <br />当然，人生比这个寓言更复杂一些。有些人，一生认定一个简单的方向而坚定地做下去，他们的人生最后达到了别人不可企及的高度。譬如，我一个朋友的人生方向是英语，他花了十数年努力，仅单词的记忆量就达到了十几万之多，在这一点上达到了一般人无法企及的高度。 <br />也有些人，他们的人生方向也很明确，譬如开公司做老板，这样，他们就需要很多技能&mdash;&mdash;&mdash;专业技能、管理技能、沟通技能、决策技能等等。他们可能会在一开始尝试做做这个，又尝试做做那个，没有一样是特别精通的，但最后，开公司做老板的这个方向将以前的这些看似零散的努力统合到一起，这也是一种复杂的人生折叠，而不是简单的叠加。 <br />切记：看得见的力量比看不见的力量更有用。 <br />现在，流行从看不见的地方寻找答案，譬如潜能开发，譬如成功学，以为我们的人生要靠一些奇迹才能得救。但是，在我看来，东莞恒缘心理咨询中心的咨询师毛正强说得更正确，&ldquo;通过规划利用好现有的能力远比挖掘所谓的潜能更重要。&rdquo; <br /><br />（四）逃避的寓言：小猫逃开影子的招数 <br /><br />&ldquo;影子真讨厌！&rdquo;小猫汤姆和托比都这样想，&ldquo;我们一定要摆脱它。&rdquo; <br />然而，无论走到哪里，汤姆和托比发现，只要一出现阳光，它们就会看到令它们抓狂的自己的影子。 <br />不过，汤姆和托比最后终于都找到了各自的解决办法。汤姆的方法是，永远闭着眼睛。托比的办法则是，永远待在其他东西的阴影里。 <br /><br />【心理点评】 <br />这个寓言说明，一个小的心理问题是如何变成更大的心理问题的。 <br />可以说，一切心理问题都源自对事实的扭曲。什么事实呢？主要就是那些令我们痛苦的负性事件。 <br />因为痛苦的体验，我们不愿意去面对这个负性事件。但是，一旦发生过，这样的负性事件就注定要伴随我们一生，我们能做的，最多不过是将它们压抑到潜意识中去，这就是所谓的忘记。 <br />但是，它们在潜意识中仍然会一如既往地发挥作用。并且，哪怕我们对事实遗忘得再厉害，这些事实所伴随的痛苦仍然会袭击我们，让我们莫名其妙地伤心难过，而且无法抑制。这种疼痛让我们进一步努力去逃避。 <br />发展到最后，通常的解决办法就是这两个：要么，我们像小猫汤姆一样，彻底扭曲自己的体验，对生命中所有重要的负性事实都视而不见；要么，我们像小猫托比一样，干脆投靠痛苦，把自己的所有事情都搞得非常糟糕，既然一切都那么糟糕，那个让自己最伤心的原初事件就不是那么疼了。 <br />白云心理医院的咨询师李凌说，99％的吸毒者有过痛苦的遭遇。他们之所以吸毒，是为了让自己逃避这些痛苦。这就像是躲进阴影里，痛苦的事实是一个魔鬼，为了躲避这个魔鬼，干脆把自己卖给更大的魔鬼。 <br />还有很多酗酒的成人，他们有过一个酗酒而暴虐的老爸，挨过老爸的不少折磨。为了忘记这个痛苦，他们学会了同样的方法。 <br />除了这些看得见的错误方法外，我们人类还发明了无数种形形色色的方法去逃避痛苦，弗洛伊德将这些方式称为心理防御机制。太痛苦的时候，这些防御机制是必要的，但糟糕的是，如果心理防御机制对事实扭曲得太厉害，它会带出更多的心理问题，譬如强迫症、社交焦虑症、多重人格，甚至精神分裂症等。 <br />真正抵达健康的方法只有一个&mdash;&mdash;&mdash;直面痛苦。直面痛苦的人会从痛苦中得到许多意想不到的收获，它们最终会变成当事人的生命财富。 <br />切记：阴影和光明一样，都是人生的财富。 <br />一个最重要的心理规律是，无论多么痛苦的事情，你都是逃不掉的。你只能去勇敢地面对它，化解它，超越它，最后和它达成和解。如果你自己暂时缺乏力量，你可以寻找帮助，寻找亲友的帮助，或寻找专业的帮助，让你信任的人陪着你一起去面对这些痛苦的事情。 <br />美国心理学家罗杰斯曾是最孤独的人，但当他面对这个事实并化解后，他成了真正的人际关系大师；美国心理学家弗兰克有一个暴虐而酗酒的继父和一个糟糕的母亲，但当他挑战这个事实并最终从心中原谅了父母后，他成了治疗这方面问题的专家；日本心理学家森田正马曾是严重的神经症患者，但他通过挑战这个事实并最终发明出了森田疗法&hellip;&hellip;他们生命中最痛苦的事实最后都变成了他们最重要的财富。你，一样也可以做到。 <br /><br />（五）行动的寓言&mdash;&mdash;&mdash;螃蟹、猫头鹰和蝙蝠 <br /><br />螃蟹、猫头鹰和蝙蝠去上恶习补习班。数年过后，它们都顺利毕业并获得博士学位。不过，螃蟹仍横行，猫头鹰仍白天睡觉晚上活动，蝙蝠仍倒悬。 <br /><br />【心理点评】 <br />这是黄永玉大师的一个寓言故事，它的寓意很简单：行动比知识重要。 <br />用到心理健康中，这个寓言也发人深省。 <br />心理学的知识堪称博大精深。但是，再多再好的心理学知识也不能自动帮助一个人变得更健康。其实，我知道的一些学过多年心理学的人士，他们学心理学的目的之一就是要治自己，但学了这么多年以后，他们的问题依旧。 <br />之所以出现这种情况，一个很重要的原因是，他们没有身体力行，那样知识就只是遥远的知识，知识并没有化成他们自己的生命体验。 <br />我的一个喜欢心理学的朋友，曾被多名心理学人士认为不敏感，不适合学心理学。但事实证明，这种揣测并不正确。他是不够敏感，但他有一个非常大的优点：知道一个好知识，就立即在自己的生命中去执行。这样一来，那些遥远的知识就变成了真切的生命体验，他不必&ldquo;懂&rdquo;太多，就可以帮助自己，并帮助很多人。 <br />如果说，高敏感度是一种天才素质，那么高行动力是更重要的天才素质。 <br />这个寓言还可以引申出另一种含义：不要太指望神秘的心理治疗的魔力。最重要的力量永远在你自己的身上，奥秘的知识、玄妙的潜能开发、炫目的成功学等等，都远不如你自己身上已有的力量重要。我们习惯去外面寻找答案，去别人那里寻找力量，结果忘记了力量就在自己身上。 <br />切记：别人的知识不能自动地拯救你。 <br />如果一些连珠的妙语打动了你，如果一些文字或新信条启发了你。那么，这些别人的文字和经验都只是一个开始，更重要的是，你把你以为好的知识真正运用到你自己的生命中去。 <br />犹太哲学家马丁&middot;布伯的这句话，我一直认为是最重要的： <br />你必须自己开始。假如你自己不以积极的爱去深入生存，假如你不以自己的方式去为自己揭示生存的意义，那么对你来说，生存就将依然是没有意义的。 <br /><br />（六）放弃的寓言：蜜蜂与鲜花 <br /><br />玫瑰花枯萎了，蜜蜂仍拼命吮吸，因为它以前从这朵花上吮吸过甜蜜。但是，现在在这朵花上，蜜蜂吮吸的是毒汁。 <br />蜜蜂知道这一点，因为毒汁苦涩，与以前的味道是天壤之别。于是，蜜蜂愤不过，它吸一口就抬起头来向整个世界抱怨，为什么味道变了？！ <br />终于有一天，不知道是什么原因，蜜蜂振动翅膀，飞高了一点。这时，它发现，枯萎的玫瑰花周围，处处是鲜花。 <br /><br />【心理点评】 <br />这是关于爱情的寓言，是一位年轻的语文老师的真实感悟。 <br />有一段时间，她失恋了，很痛苦，一直想约我聊聊，希望我的心理学知识能给她一些帮助。我们一直约时间，但快两个月过去了，两人的时间总不能碰巧凑在一起。 <br />最后一次约她，她说：&ldquo;谢谢！不用了，我想明白了。&rdquo; <br />原来，她刚从九寨沟回来。失恋的痛苦仍在纠缠她，让她神情恍惚，不能享受九寨沟的美丽。不经意的时候，她留意到一只小蜜蜂正在一朵鲜花上采蜜。那一刹那间，她脑子里电闪雷鸣般地出现了一句话：&ldquo;枯萎的鲜花上，蜜蜂只能吮吸到毒汁。&rdquo; <br />当然，大自然中的小蜜蜂不会这么做，只有人类才这么傻，她这句话里的蜜蜂当然指她自己。这一刹那，她顿悟出了放弃的道理。以前，她想让我帮她走出来，但翅膀其实就长在她自己身上，她想飞就能飞。 <br />放弃并不容易，爱情中的放弃尤其令人痛苦。因为，爱情是对我们幼小时候的亲子关系的复制。幼小的孩子，无论从哪个方面看，都离不开爸爸妈妈。如果爸爸妈妈完全否定他，那对他来说就意味着死亡，这是终极的伤害和恐惧。我们多多少少都曾体验过被爸爸妈妈否定的痛苦和恐惧，所以，当爱情&mdash;&mdash;&mdash;这个亲子关系的复制品再一次让我们体验这种痛苦和恐惧时，我们的情绪很容易变得非常糟糕。 <br />不过，爱情和亲子关系相比，有一个巨大的差别：小时候，我们无能为力，一切都是父母说了算；但现在，我们长大了，我们有力量自己去选择自己的命运。可以说，童年时，我们是没有翅膀的小蜜蜂，但现在，我们有了一双强有力的翅膀了。 <br />但是，当深深地陷入爱情时，我们会回归童年，我们会忘记自己有一双可以飞翔的翅膀。等我们自己悟出这一点后，爱情就不再会是对亲子关系的自动复制，我们的爱情就获得了自由，就有了放弃的力量。 <br />切记：爱情是两个人的事情，两个完全平等的、有独立人格的人的事情。你可以努力，但不是说，你努力了就一定会有效果，因为另一个人，你并不能左右。 <br />所以，无论你多么在乎一次爱情，如果另一个人坚决要离开你，请尊重他的选择。 <br />并且，还要记得，你不再是童年，只能听凭痛苦的折磨。你已成人，你有一双强有力的翅膀，你完全可以飞出一个已经变成毒药的关系。 <br /><br />（七）亲密的寓言：独一无二的玫瑰 <br /><br />小王子有一个小小的星球，星球上忽然绽放了一朵娇艳的玫瑰花。以前，这个星球上只有一些无名的小花，小王子从来没有见过这么美丽的花，他爱上这朵玫瑰，细心地呵护她。 <br />那一段日子，他以为，这是一朵人世间唯一的花，只有他的星球上才有，其他的地方都不存在。 <br />然而，等他来到地球上，发现仅仅一个花园里就有5000朵完全一样的这种花朵。这时，他才知道，他有的只是一朵普通的花。 <br />一开始，这个发现，让小王子非常伤心。但最后，小王子明白，尽管世界上有无数朵玫瑰花，但他的星球上那朵，仍然是独一无二的，因为那朵玫瑰花，他浇灌过，给她罩过花罩，用屏风保护过，除过她身上的毛虫，还倾听过她的怨艾和自诩，聆听过她的沉默&hellip;&hellip;一句话，他驯服了她，她也驯服了他，她是他独一无二的玫瑰。 <br />&ldquo;正因为你为你的玫瑰花费了时间，这才使你的玫瑰变得如此重要。&rdquo;一只被小王子驯服的狐狸对他说。 <br /><br />【心理点评】 <br />这是法国名著《小王子》中一个有名的寓言故事，我曾读过十数遍，但仍然是直到2005年才明白这一点。 <br />面对着5000朵玫瑰花，小王子说：&ldquo;你们很美，但你们是空虚的，没有人能为你们去死。&rdquo; <br />只有倾注了爱，亲密关系才有意义。但是，现在我们越来越流行空虚的&ldquo;亲密关系&rdquo;，最典型的就是因网络而泛滥的一夜情。 <br />我们急着去拥有。仿佛是，每多拥有过一朵玫瑰，自己的生命价值就多了一分。网络时代，拥有过数十名情人，已不再是太罕见的事情。但我所了解的这些滥情者，没有一个是不空虚的。他们并不享受关系，他们只享受征服。 <br />&ldquo;征服欲望越强的人，对于关系的亲密度越没有兴趣。&rdquo;广州白云心理医院的咨询师荣玮龄说，&ldquo;没有拥有前，他们会想尽一切办法拉近关系的距离。但一旦拥有后，他们会迅速丧失对这个亲密关系的兴趣。征服欲望越强，丧失的速度越快。&rdquo; <br />对于这样的人，一个玫瑰园比起一朵独一无二的玫瑰花来，更有吸引力。 <br />然而，关系的美，正在乎两人的投入程度和被驯服程度。当两个人都自然而然地去投入，自然而然地被驯服后，关系就会变成人生养料，让一个人的生命变得更充盈、更美好。 <br />但是，无论多么亲密。小王子仍是小王子，玫瑰仍是玫瑰，他们仍然是两个个体。如果玫瑰不让小王子旅行，或者小王子旅行时非将玫瑰花带在身上，两者一定要黏在一起，关系就不再是享受，而会变成一个累赘。 <br />切记：一个既亲密而又相互独立的关系，胜于一千个一般的关系。这样的关系，会把我们从不可救药的孤独感中拯救出来，是我们生命中最重要的一种救赎。 <br />如果不曾体验过，你就无法知道这种关系的美。</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4137" width="1" height="1">]]></description></item><item><title>改变人生的几句话</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/08/14/4120.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Thu, 14 Aug 2008 07:44:17 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4120</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4120</comments><slash:comments>0</slash:comments><description><![CDATA[<p>第一句话是：优秀是一种习惯。 </p>
<p>　这句话是古希腊哲学家亚里士多德说的。如果说优秀是一种习惯，那么懒惰也是一种习惯。人出生的时候，除了脾气会因为天性而有所不同，其他的东西基本都是后天形成的，是家庭影响和教育的结果。所以，我们的一言一行都是日积月累养成的习惯。我们有的人形成了很好的习惯，有的人形成了很坏的习惯。所以我们从现在起就要把优秀变成一种习惯，使我们的优秀行为习以为常，变成我们的第二天性。让我们习惯性地去创造性思考，习惯性地去认真做事情，习惯性地对别人友好，习惯性地欣赏大自然。 </p>
<p>注解：要会&ldquo;装&rdquo;，要持续的、不间断的&ldquo;装&rdquo;，装久了就成了真的了，就成了习惯了，比如准时到会，每次都按时到会，你装装看，你装30年看看，装的时间长了就形成了习惯。：） 　　</p>
<p>&nbsp;</p>
<p>第二句话是：生命是一种过程。 　</p>
<p>　事情的结果尽管重要，但是做事情的过程更加重要，因为结果好了我们会更加快乐，但过程使我们的生命充实。 　　</p>
<p>&nbsp;&nbsp; 人的生命最后的结果一定是死亡，我们不能因此说我们的生命没有意义。世界上很少有永恒。大学生谈恋爱，每天都在信誓旦旦地说我会爱你一辈子，这实际上是不真实的。统计数据表明，大学生谈恋爱的100对里有90对最后会分手，最后结婚了的还有一半会离婚。你说爱情能永恒吗？所以最真实的说法是：&ldquo;我今天，此时此刻正在真心地爱着你。&rdquo;明天也许你会失恋，失恋后我们会体验到失恋的痛苦。这种体验也是丰富你生命的一个过程。 </p>
<p>注解：生命本身其实是没有任何意义的，只是你自己赋予你的生命一种你希望实现的意义，因此享受生命的过程就是一种意义所在。 　　</p>
<p>&nbsp;</p>
<p>第三句话是：两点之间最短的距离并不一定是直线。 　　</p>
<p>在人与人的关系以及做事情的过程中，我们很难直截了当就把事情做好。我们有时需要等待，有时需要合作，有时需要技巧。我们做事情会碰到很多困难和障碍，有时候我们并不一定要硬挺、硬冲，我们可以选择有困难绕过去，有障碍绕过去，也许这样做事情更加顺利。大家想一想，我们和别人说话还得想想哪句话更好听呢。尤其在中国这个比较复杂的社会中，大家要学会想办法谅解别人，要让人觉得你这个人很成熟，很不错，你才能把事情做成。 </p>
<p>注解：如果你在考数学试题，一定要答两点之间直线段最短，如果你在走路，从A到B，明明可以直接过去，但所以人都不走，你最好别走，因为有陷阱。在中国办事情，直线性思维在很多地方要碰壁，这是中国特色的中国处事智慧。 　　</p>
<p>第四句话是：只有知道如何停止的人才知道如何加快速度。 　　</p>
<p>我在滑雪的时候，最大的体会就是停不下来。我刚开始学滑雪时没有请教练，看着别人滑雪，觉得很容易，不就是从山顶滑到山下吗？于是我穿上滑雪板，哧溜一下就滑下去了，结果我从山顶滑到山下，实际上是滚到山下，摔了很多个跟斗。我发现根本就不知道怎么停止、怎么保持平衡。最后我反复练习怎么在雪地上、斜坡上停下来。练了一个星期，我终于学会了在任何坡上停止、滑行、再停止。这个时候我就发现自己会滑雪了，就敢从山顶高速地往山坡下冲。因为我知道只要我想停，一转身就能停下来。只要你能停下来，你就不会撞上树、撞上石头、撞上人，你就不会被撞死。因此，只有知道如何停止的人，才知道如何高速前进。 注解：用汽车来比喻，宝马可以上200公里，奇瑞却只能上120公里，为什么？发动机估计不相上下，差距在刹车系统，上了200公里刹不了车，呵呵，我的天！ 　　</p>
<p>&nbsp;</p>
<p>第五句话是：放弃是一种智慧，缺陷是一种恩惠。 　　</p>
<p>当你拥有六个苹果的时候，千万不要把它们都吃掉，因为你把六个苹果全都吃掉，你也只吃到了六个苹果，只吃到了一种味道，那就是苹果的味道。如果你把六个苹果中的五个拿出来给别人吃，尽管表面上你丢了五个苹果，但实际上你却得到了其他五个人的友情和好感。以后你还能得到更多，当别人有了别的水果的时候，也一定会和你分享，你会从这个人手里得到一个橘子，那个人手里得到一个梨，最后你可能就得到了六种不同的水果，六种不同的味道，六种不同的颜色，六个人的友谊。人一定要学会用你拥有的东西去换取对你来说更加重要和丰富的东西。所以说，放弃是一种智慧。 注解：我的个人原则是：每一次放弃都必须是一次升华，否则就不要放弃；每一次选择都必须是一次升华，否则不要选择。 　　</p>
<p>做人最大的乐趣在于通过奋斗去获得我们想要的东西，所以有缺点意味着我们可以进一步完美，有匮乏之处意味着我们可以进一步努力。美国有一部电视片，讲的是一位富翁给后代留下了用不尽的遗产，结果他的后代全都变成了吸毒的、自杀的、进监狱的，或者精神病患者。为什么会这样呢？因为这位富翁给自己后代留下的钱太多了，以致他们不需要劳动就可以继承一大笔财产。继承一大笔财富，就几乎什么都能买到。所以，当一个人什么都不缺的时候，他的生存空间就被剥夺掉了。如果我们每天早上醒过来，感到自己今天缺点儿什么，感到自己还需要更加完美，感到自己还有追求，那是一件多么值得高兴的事情啊！</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4120" width="1" height="1">]]></description></item><item><title>你是爱我还是需要我?</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/08/04/4105.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Mon, 04 Aug 2008 07:16:08 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4105</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4105</comments><slash:comments>3</slash:comments><description><![CDATA[<p style="text-indent: 2em;">著名的心理学家弗格姆(E.Fromm)在他的名著(爱的艺术)中有这么一句名言：不成熟的爱是「因为我需要你,所以我爱你」，而成熟的爱是「因为我爱你,所以我需要你」。</p>
<p style="text-indent: 2em;">一个人付出的爱是不是成熟，从他最原始的动机与表达得到验证。</p>
<p style="text-indent: 2em;">如果是基于需要(例如：因为我孤单、寂寞，所以我需要你陪我与安慰)，那么他所说的爱其实不是真爱，而是一种条件的需索却假冒爱的名义。</p>
<p style="text-indent: 2em;">这假爱的鉴别非常容易，就是当他的需求已得到满足。 (例如：因为你的陪伴安慰，他已不再感到寂寞)，便会对你的存在觉得多余与厌烦。</p>
<p style="text-indent: 2em;">所以，当你的情人打电话给你，诉说他见不着你的日子真是茶不思饭不想，而求你赶过去给他看看的时候，你且慢高兴.因为说穿了，他只是要你去给他下饭罢了！他只是需要你，那里是爱你呢！</p>
<p style="text-indent: 2em;">而真正的爱是无条件的自由付出，所谓需要，其实只是一种邀请：他需要一个人和他共同完成这桩爱的事实，所以他对你提出邀请了。</p>
<p style="text-indent: 2em;">而这样的真爱也很容易鉴别，就是当你对他的邀请婉拒甚至只是沉吟的时候，他立刻就能尊重你的意愿而停步，而不会死追活缠，非要你答应才甘心。</p>
<p style="text-indent: 2em;">何以故？只因他并不是荏弱的人格需要你去支持，而是秉其人格的独立坚强，愿邀你分享他生命的美好时光。</p>
<p style="text-indent: 2em;">因此我们说爱的第一要义就是自由，这一方面是指爱的付出应当基于自由意志，而别无潜在的阴暗动机。</p>
<p style="text-indent: 2em;">一方面则是指对对方人格自由的充分尊重，而不在付出的爱上面附带有渴想、期望、要求乃至逼迫的压力。</p>
<p style="text-indent: 2em;">而真的相爱是一种愉悦甜美的经验，而不是互相剥削的债务。</p>
<p style="text-indent: 2em;">但许多情人的相处却总是从无私的愉悦始，而以沉重的负担终。</p>
<p style="text-indent: 2em;">情人总忍不住想用对方的束缚来保障自己的安全，却不知只会带来更多的烦忧。</p>
<p style="text-indent: 2em;">而一个愿意对方完全自由的人，又有谁舍得离弃？</p>
<p style="text-indent: 2em;">只是道理虽然简明，当事到临头，总是不容易做到罢了！</p>
<p style="text-indent: 2em;">有人问：『你为什么喜欢一个人？ 』</p>
<p style="text-indent: 2em;">我只能够说出为什么不喜欢一个人，却说不出为什么喜欢个人。</p>
<p style="text-indent: 2em;">喜欢一个人，是一种感觉。</p>
<p style="text-indent: 2em;">不喜欢一个人，却是事实。</p>
<p style="text-indent: 2em;">事实容易解释，感觉却难以言喻。</p>
<p style="text-indent: 2em;">爱情是忽然有一个人，我们觉得一见如故，很想靠近他，我们的内分泌忽然起了翻天覆地的变化，很想拥抱他。</p>
<p style="text-indent: 2em;">以后，无论快乐或哀愁，我们也想不起当初为什么爱他。</p>
<p style="text-indent: 2em;">只有当我们不爱一个人时，才会找出不爱他的原因，因为我们开始挑剔。</p>
<p style="text-indent: 2em;">任何一个人，只要你去挑剔，一定找得出缺点。</p>
<p style="text-indent: 2em;">越去挑剔，缺点越多，我们便可以说出为什么不喜欢他。</p>
<p style="text-indent: 2em;">我们想买一件衣服时，即使发现他有小小瑕疵，埋怨几句，也肯将就，因为只有这一件，而且我们太喜欢它了，瑕不掩瑜嘛！</p>
<p style="text-indent: 2em;">假使我们根本不想买那件衣服，它的小小瑕疵便是致命伤。</p>
<p style="text-indent: 2em;">我们更会努力地找出其他缺点，譬如质料不够挺，颜色太鲜艳，向售货员证实，我们不是随便来逛逛的，我有认真考虑过的呀！</p>
<p style="text-indent: 2em;">分手可以有很多原因，结合却只有一个原因，原因就是：不需要原因！</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4105" width="1" height="1">]]></description></item><item><title>游戏接力</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/08/01/4104.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Fri, 01 Aug 2008 12:38:16 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4104</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4104</comments><slash:comments>0</slash:comments><description><![CDATA[<p><strong>(从&nbsp;衣明志 处接到)</strong> </p>
<p><strong>游戏的规则</strong>：<br />A. 被点到的要在自己的博客里写下自己的答案，然后去掉一个你最不喜欢的问题再加上一个你的问题，仍然组成20个问题，传给其他8个人。列出8个需要回答问题的人的名字，还要通知对方"你被点名了"，<br />B. 这8个人要在自己的博客里注明是从哪里接到的，并且再传给其他8个人，让游戏继续下去，不得回传。被点到名字的人将会得到大家的祝福，并且所有美好的愿望都会在不久的将来实现。 </p>
<p><strong>1. 喜欢的季节和颜色</strong> </p>
<p>夏天 蓝色,白色&nbsp; </p>
<p><strong>2. 最近在听的，有能说出你心事的歌么，写句歌词给我？</strong> </p>
<p>《几分》 </p>
<p>&nbsp;没有值不值得</p>
<p>当失去了有什么好争</p>
<p>就在转身之后</p>
<p>我的不舍有多么的真</p>
<p><strong>3. 异性的什么是最吸引你的？为什么？</strong> </p>
<p>爱心、气质、内涵 </p>
<p>因为这样的女人我想娶~~</p>
<p><strong>4. 你最希望从朋友（不包括爱人）那里得到的是什么？</strong> </p>
<p>平安的消息，我很挂念远方的朋友。 </p>
<p><strong>5. 说说你去过的最难忘的一个地方？</strong> </p>
<p>芝罘岛海边&nbsp;</p>
<p><strong>6. 推荐一家你认为好吃又实惠的美食店。</strong> </p>
<p>马文杰驴肉火烧&nbsp; </p>
<p><strong>7. 你的星座是什么？比较欣赏哪个星座？</strong> </p>
<p>白羊座</p>
<p>双鱼座&nbsp;</p>
<p><strong>8. 遇到喜欢的人，你是勇敢表白还是默默关注？ </strong></p>
<p>如果是我是单身我会表白</p>
<p><strong>9. 说出点你名的人的3个优点（不可删除题）</strong> </p>
<p>脑门大（聪明）&nbsp;</p>
<p>不挑食</p>
<p>不张扬&nbsp;</p>
<p><strong>10. 什么事情会让你觉得很幸福？</strong> </p>
<p>因为我的一点小成就，老爸夸奖我&nbsp; </p>
<p><strong>11. 你最想回到哪一年重新开始？</strong> </p>
<p>2006年8月12号？ </p>
<p><strong>12. 计划过自己的将来么，有的话说说。</strong> </p>
<p>计划过，人品像衣明志一样好，技术像衣明志一样牛，娶swx做老婆 </p>
<p><strong></strong></p>
<p><strong>13. 你对你的近况满意吗？有什么需要改变？</strong> </p>
<p><strong></strong>不满意。人际关系和技术有待完善 </p>
<p><strong>14. 有想过以后家里会养一个或几个宠物吗？会选择养那种呢？&rdquo;</strong> </p>
<p>想过，不大点的猫儿，狗儿，猪，我要养一只与众不同的猪，哈哈&nbsp; </p>
<p><strong>15. 你最讨厌的一件事是什么？</strong> </p>
<p>打cs时，有人说我作弊，目前我还不知道如何开作弊器，我鄙视作弊的人还嚷嚷着说比人人作弊</p>
<p><strong>16. 最喜欢的旅游地？</strong> </p>
<p>威海</p>
<p><strong>17.</strong> <strong>在过去的时光里，你为自己最感到骄傲的一件事情是什么？</strong> </p>
<p>排除阻力，选择了我感兴趣的职业&nbsp;</p>
<p><strong>18.你有没为自己的目标奋斗过？</strong> </p>
<p>一直在努力</p>
<p><strong>19. 此时此刻的一个小愿望? </strong></p>
<p>希望她得到真正幸福</p>
<p><strong>20. 你希望有个怎样的人生?</strong> </p>
<p>丰富多彩的</p>
<p>游戏接力： </p>
<p>========================================================================================================== </p>
<p>增加的题目是：爱情与事业那个重要，你如何看待？ </p>
<p>删除的题目是：<strong>19. 此时此刻的一个小愿望?&nbsp;</strong> </p>
<p>点名： </p>
<p>alex, alisx ,hucher ,phenge ,静度雨季,Catherin,心随心动，vc ，藏马啸天</p>
<p>注:以上朋友我都不认识，我是新手，认识一下了~~</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4104" width="1" height="1">]]></description></item><item><title>阅读器关闭时 FieldCount 的尝试无效异常问题解决</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/08/01/4103.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Fri, 01 Aug 2008 08:01:11 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4103</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4103</comments><slash:comments>2</slash:comments><description><![CDATA[<p>今天写了个方法，做了个测试，方法如下：</p>
<p>调用方法报错:阅读器关闭时 FieldCount 的尝试无效 ,查了相关资料原因:SqlDataReader关闭后又尝试了读取操作引起，根据该提示 我找出以下代码的两个错误:</p>
<p>1:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }//在没有读取前已经关闭连接，从而导致无法读取</p>
<p>2： using (SqlConnection con = new SqlConnection(CONNECTIONSTR))，这段错误是，连接对象在此范围外自动失效，从而导致 SqlDataReader无法读取</p>
<p>错误代码:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// 执行查询语句，返回SqlDataReader<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="sql"&gt;sql语句&lt;/param&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="sp"&gt;参数数组&lt;/param&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static SqlDataReader GetReaderBySql(string sql, SqlParameter[] sp)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (SqlConnection con = new SqlConnection(CONNECTIONSTR))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Open();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommand cmd = new SqlCommand(sql, con);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(sp!=null&amp;&amp;sp.Length&gt;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (SqlParameter var in sp)//利用循环设置命令对象的参数<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.Parameters.Add(var);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlDataReader sdr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return sdr;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (SqlException e)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new Exception(e.Message);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>修改后的代码:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlConnection con = new SqlConnection(CONNECTIONSTR);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; con.Open();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlCommand cmd = new SqlCommand(sql, con);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(sp!=null&amp;&amp;sp.Length&gt;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (SqlParameter var in sp)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd.Parameters.Add(var);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SqlDataReader sdr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return sdr;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (SqlException e)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new Exception(e.Message);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4103" width="1" height="1">]]></description></item><item><title>.Net程序如何防止被注入(整站通用)</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/08/01/4099.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Fri, 01 Aug 2008 07:07:56 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4099</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4099</comments><slash:comments>2</slash:comments><description><![CDATA[<p><span style="font-size: small; font-family: 宋体;">防止<span>sql</span>注入，通常一个一个文件修改不仅麻烦而且还有漏掉的危险，下面我说一上如何从整个系统防止注入。做到以下三步，相信的程序将会比较安全了，而且对整个网站的维护也将会变的简单。</span></p>
<p><strong><span style="font-family: 宋体; mso-bidi-font-family: 宋体;"><span style="font-size: small;">一、数据验证类：<span>parameterCheck.cs</span></span></span></strong></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; public class parameterCheck{ public static bool isEmail</span>（<span>string emailString</span>）<span>{ return System.Text.RegularExpressions.Regex.IsMatch</span>（<span>emailString</span>，<span> "['\\w_-]+</span>（<span>\\.['\\w_-]+</span>）<span>*@['\\w_-]+</span>（<span>\\.['\\w_-]+</span>）<span>*\\.[a-zA-Z]{2</span>，<span>4}"</span>）；<span>} public static bool isInt</span>（<span>string intString</span>）<span>{ return System.Text.RegularExpressions.Regex.IsMatch</span>（<span>intString </span>，<span>"^</span>（<span>\\d{5}-\\d{4}</span>）（<span>\\d{5}</span>）<span>$"</span>）；<span>} public static bool isUSZip</span>（<span>string zipString</span>）<span>{ return System.Text.RegularExpressions.Regex.IsMatch</span>（<span>zipString </span>，<span>"^-[0-9]+$^[0-9]+$"</span>）；<span>}</span></font></span></p>
<p><span style="font-size: small;"><strong><span style="font-family: 宋体; mso-bidi-font-family: 宋体;">&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family: 宋体; mso-bidi-font-family: 宋体;">二、<span>Web.config</span></span></strong></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; </span>在你的<span>Web.config</span>文件中，在<span>&lt;appSettings&gt;</span>下面增加一个标签：如下</font></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; &lt;appSettings&gt; &lt;add key="safeParameters" value="OrderID-int32</span>，<span>CustomerEmail-email</span>，<span>ShippingZipcode-USzip" /&gt; &lt;/appSettings&gt;</span></font></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; </span>其中<span>key</span>是<span>&lt;saveParameters&gt;</span>后面的值为<span>"OrderId-int32"</span>等，其中<span>"-"</span>前面表示参数的名称比如：<span>OrderId</span>，后面的<span>int32</span>表示数据类型。</font></span></p>
<p><span style="font-size: small;"><strong><span style="font-family: 宋体; mso-bidi-font-family: 宋体;">&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family: 宋体; mso-bidi-font-family: 宋体;">三、<span>Global.asax</span></span></strong></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; </span>在<span>Global.asax</span>中增加下面一段：</font></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; protected void Application_BeginRequest</span>（<span>Object sender</span>，<span> EventArgs e</span>）<span>{ String[] safeParameters = System.Configuration.ConfigurationSettings.AppSettings["safeParameters"].ToString</span>（）。<span>Split</span>（<span>'</span>，<span>'</span>）；<span>for</span>（<span>int i= 0 </span>；<span>i &lt; safeParameters.Length</span>；<span> i++</span>）<span>{ String parameterName = safeParameters<img src="/Utility/Emoticons/emotion-55.gif" alt="Idea" />.Split</span>（<span>'-'</span>）<span>[0]</span>；<span>String parameterType = safeParameters<img src="/Utility/Emoticons/emotion-55.gif" alt="Idea" />.Split</span>（<span>'-'</span>）<span>[1]</span>；<span>isValidParameter</span>（<span>parameterName</span>，<span> parameterType</span>）；<span>}</span></font></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; public void isValidParameter</span>（<span>string parameterName</span>，<span> string parameterType</span>）<span>{ string parameterValue = Request.QueryString[parameterName]</span>；<span>if</span>（<span>parameterValue == null</span>）<span> return</span>；</font></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; if</span>（<span>parameterType.Equals</span>（<span>"int32"</span>））<span>{ if</span>（！<span>parameterCheck.isInt</span>（<span>parameterValue</span>））<span> Response.Redirect</span>（<span>"parameterError.aspx"</span>）；<span>} else if </span>（<span>parameterType.Equals</span>（<span>"double"</span>））<span>{ if</span>（！<span>parameterCheck.isDouble</span>（<span>parameterValue</span>））<span> Response.Redirect</span>（<span>"parameterError.aspx"</span>）；<span>} else if </span>（<span>parameterType.Equals</span>（<span>"USzip"</span>））<span>{ if</span>（！<span>parameterCheck.isUSZip</span>（<span>parameterValue</span>））<span> Response.Redirect</span>（<span>"parameterError.aspx"</span>）；<span>} else if </span>（<span>parameterType.Equals</span>（<span>"email"</span>））<span>{ if</span>（！<span>parameterCheck.isEmail</span>（<span>parameterValue</span>））<span> Response.Redirect</span>（<span>"parameterError.aspx"</span>）；<span>}</span></font></span></p>
<p><span style="font-size: small;"><font face="宋体"><span>&nbsp;&nbsp;&nbsp; </span>以后需要修改的时候我们只需要修改以上三个文件，对整个系统的维护将会大大提高效率，当然你可以根据自己的需要增加其它的变量参数和数据类型</font></span></p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4099" width="1" height="1">]]></description></item><item><title>男人最让女人感动的一句粗话</title><link>http://qihang.net/u/xiaoqiang/Blog/archive/2008/08/01/4098.aspx</link><author>小强</author><dc:creator>小强</dc:creator><pubDate>Fri, 01 Aug 2008 02:40:38 GMT</pubDate><guid isPermaLink="False">fda5cd7f-5239-462c-9103-941409ef089a:Weblog:4098</guid><comments>http://qihang.net/u/Blog/ShortLink.aspx?UserDomainName=xiaoqiang&amp;PostID=4098</comments><slash:comments>0</slash:comments><description><![CDATA[<p><span style="font-size: x-small;">赵先生一早起来就头痛的要死......<br />因为他前一天晚上喝的烂醉回家!<br />他强迫自己把疲惫不堪的眼睛睁开。<br />睁开眼后竟然看到床头上放了一杯水跟几颗头痛药，然后坐起身后又看到了他的衣服已 经烫好、叠好在床边。<br />因为一起床就看到这几样反常的事，所以他决定要起身看一看房子其它的地方有没有什 么奇怪的事<br />他把几颗头痛药吃了。<br />吃的时候突然发现药下有一张纸条，<br />纸条上写着......<br />&ldquo;亲爱的，我出去买菜了，<br />你的早餐我已经做好放在餐桌上~<br />趁热吃吧~爱你喔~&rdquo;<br />赵先生一头雾水的走进了厨房，<br />然后就真的看到了热腾腾的早餐在桌上还有当天的早报。<br />他看着坐在餐桌吃早餐的儿子问......<br />&ldquo;儿子啊~昨天到底发生了什么事？&rdquo;<br />赵先生的儿子回答<br />&ldquo;嗯......你凌晨三点跌跌撞撞、大吼大叫的回了家~<br />把几个家俱给打坏踹坏~<br />然后又很聪明的在走廊上撞了墙壁几下、送给自己一个黑眼圈!<br />赵先生越来越不明白的又问了儿子<br />&ldquo;那为什么家里给打扫的那么干净然后你妈又给我做了热腾腾的早餐给我吃呢?!&rdquo;<br />儿子恍然大悟的说<br />&ldquo;喔~你是在问那个喔~~~<br />妈昨天看到你醉死的回家，<br />一肚子火的把你拉到房间里，<br />然后想把你脏衣服换掉，<br />结果在脱你裤子的时候你骂了她一句<br />你他妈滚远一点~我已经结婚了~'</span> </p><img src="http://qihang.net/utility/AggView.ashx?ViewObject=7&PostID=4098" width="1" height="1">]]></description></item></channel></rss>