<javaScript>谈谈JavaScript中的变量、指针和引用
<p>1、变量<br>我们可能产生这样一个疑问:编程语言中的变量到底是什么意思呢?<br>事实上,当我们定义了一个变量a时,就是在存储器中指定了一组存储单元,并将这组存储单元命名为a。变量a的值实际上描述的是这组存储单元中存放的具体信息。<br>例如,在JS中<br><br></p><div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> a;
a</span>=<span style="color: rgba(128, 0, 128, 1)">10</span>;</pre>
</div>
<p> </p>
<p><br><br>第一个语句在存储器中指定了一组存储单元,并命名为a;<br>第二个语句在这组存储单元中存储了数字10。<br>变量a的值为10实际上是说存储单元组a存储的信息是10。<br>假使我们再次对a进行复制操作:<br><br></p>
<div class="cnblogs_code">
<pre>a=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">hello</span><span style="color: rgba(128, 0, 0, 1)">"</span>;</pre>
</div>
<p> </p>
<p><br>这样a的值变成字符串”hello”。这很容易理解,我们将存储单元组a中存储的信息改为字符串”hello”,显然原先的数字10将被覆盖。<br><br>2、指针<br>假使我们在另一个变量b中存储变量a在存储器的地址,会发生什么?<br>我们很容易想到,直接访问b变量,得到的并不是变量a的值,而是变量a在存储器中的地址,变量b便被称为指针。<br>这样一个问题产生了:如何通过变量b访问到变量a的值呢?<br>在C语言中,常用的是用*,比如:<br><br></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">int</span> c=<span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">,b;
</span><span style="color: rgba(0, 0, 255, 1)">int</span> *p;<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">p是指向int类型的指针</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
p</span>=&c;<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> &c获取变量c的地址,然后赋值给变量p,这样p存储的是变量c的地址,即p是指向c的指针</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
b</span>=*p;<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> *p访问p指向的对象,然后将值赋值给b</span><span style="color: rgba(0, 128, 0, 1)">*/</span></pre>
</div>
<p> </p>
<p><br><br>在JS中,并没有指针这种变量类型,但指针的应用却无处不在。比如:<br><br></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> o1={b:<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 0, 255, 1)">var</span> o2={b:<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">};
o1</span>===o2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">false</span>
o1==o2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">false</span></pre>
</div>
<p> </p>
<p><br><br>这里o1和o2都是相同的对象,为什么不相等呢?这就需要深入理解JavaScript中的引用类型和指针。<br>首先,我们需要明白:<br><br>给o1和o2赋值,并不是o1地址中存储对象{b:1},o2地址中也存储对象{b:1}。<br><br>其次,我们要明白实际发生的操作:<br><br>var o1={b:1}实现了在堆内存中创建了一个对象{b:1},o1则存储了该对象在堆内存中的地址,即o1是一个指针,指向{b:1};<br>同理,var o2={b:1}也在堆内存中创建了一个对象{b:1},o2存储了该对象在堆内存中的地址,即o2也是一个指针,指向{b:1};<br>并且,由于两个相同的对象{b:1}是先后创建,在堆内存中也不是存储在相同的地址。<br><br>然后,我们还需要知道:<br><br>在JavaScript中,引用类型(对象、数组、正则、Date、函数)的比较,实际上是比较指针是否指向存储器中的同一段地址,只有指向同样的地址才能相等。<br><br>显然,o1这个指针指向堆内存中创建的第一个对象{b:1};<br>o2指针则指向堆内存中创建的第二个对象{b:1};<br>但两个对象相对独立,并不是同一个对象,故o1和o2并没有指向同样的堆内存地址,故而并不相等。<br>我们再看常见的应用:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> o={a:<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">};
o.__proto__</span>===Object.prototype;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">true</span></pre>
</div>
<p> </p>
<p><br><br>对象o的构造函数是Object,Object有一个prototype属性,并且prototype是一个指针,他指向存储器中的一个对象,此对象将会被由构造函数创建的对象实例所共享。<br>作为Object的实例,o也有一个指针__proto__,它也指向Object的prototype属性指向的对象。<br>这里的全等返回true,则清楚地表明了两者指向同样的堆内存地址,即指向的是同一个对象。<br>我们如果想主动让两个引用类型指向同样的对象,如何操作呢?<br><br></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> obj1={b:<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 0, 255, 1)">var</span> obj2=<span style="color: rgba(0, 0, 0, 1)">obj1;
obj1</span>===obj2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">true</span>
obj1==obj2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">true</span></pre>
</div>
<p> </p>
<p><br><br>可以看到,对于引用类型,直接使用’=’赋值实际上就是使两者指向同一个对象。<br>故而,我们猜测,如果通过obj1修改了对象的值,obj2再次访问时将看到修改后的对象:<br><br></p>
<div class="cnblogs_code">
<pre>obj1.name=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">ls</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
obj1;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">{b: 1, name: "ls"}</span>
obj2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">{b: 1, name: "ls"}</span></pre>
</div>
<p> </p>
<p><br><br>的确如此。作为对比:<br><br></p>
<div class="cnblogs_code">
<pre>o1.name=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">ls</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
o1;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">{b: 1, name: "ls"}</span>
o2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">{b: 1}</span></pre>
</div>
<p> </p>
<p><br><br>那么,对于基本类型呢?<br><br></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> s1=<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">var</span> s2=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">;
s1</span>===s2;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">true</span></pre>
</div>
<p> </p>
<p><br><br>在JS中,对于基本类型,只需其值相等,则两个变量就相等。<br><br>3、引用<br>首先,我们要深入理解引用类型的值。<br>前面我们看到,obj1和obj2指向堆内存中存储的同一个对象。当我们访问obj1和obj2时,都会返回同一个对象。可以说:obj1的值和obj2的值相同。<br>对于o1和o2,他们指向堆内存中不同地址的两个{b:1}对象,o1和o2拥有不同的值。<br><br>因此,对于引用类型,我们所说的值,指的是保存在内存中的对象。如果是同一对象,则值相同,不同对象则值不同。<br><br>在JS中,传递参数都是按值传递的。比如:<br><br></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> a1=<span style="color: rgba(128, 0, 128, 1)">1</span>,b1=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">;
function add(a,b){
a</span>++<span style="color: rgba(0, 0, 0, 1)">;
b</span>--<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">return</span> a+<span style="color: rgba(0, 0, 0, 1)">b;
};
add(a1,b1);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3</span>
a1;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1</span>
b1;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">2</span></pre>
</div>
<p> </p>
<p><br><br>这里,函数add中的形参a、b分别得到变量a1、b1的值的拷贝,这便是按值传递。<br>在add函数执行环境中对a、b操作不会影响到全局变量a1、b1。<br>再看引用类型:<br><br></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"> function setName(obj){
obj.name</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Nicholas</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
obj</span>=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Object();
obj.name</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Greg</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> person=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Object();
setName(person);
alert(person.name);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">"Nicholas"</span>
</pre>
</div>
<p> </p>
<p><br>执行setName(person)时,person指向的内存中的地址便被传入obj,使得obj也指向同样的内存地址,即同一个对象。这里的按值传递,传递的是内存地址。<br>如果通过obj修改该对象,外部访问person便也能体现出来。<br>我们可能有一个疑问,既然是指向同一个对象,为什么不是按引用传递呢?<br>首先,我们看到函数内部对obj重新进行了赋值,使得obj指向新创建的对象。如果是按引用传递,那么外部person便也会指向新创建的对象。实际上,person还是指向原先的对象。<br><br>对于引用类型的按值传递,其实可以更加通俗地理解:<br><br>1、实参将指向的内存地址传递给形参 ,按值传递的值指的是内存地址;<br>2、形参修改了它和实参共同指向的对象后,外部的实参会反映出来;<br>3、但形参始终无法修改实参指向的内存地址,即如果将形参指向新的对象,实参并不会指向新的对象。<br><br>基于以上3点,我们就不难理解上面代码运行的结果了。<br><br></p><br><br>
来源:https://www.cnblogs.com/isAndyWu/p/11480836.html
頁:
[1]