从一个例子误区来理解JavaScript是按照值传递参数

关键字:参数值的传递、基本类型值、引用类型值、执行环境、垃圾回收、作用域链、闭包

  • 本文理顺自己的理解:函数的参数传递引用类型值中的一个误区:局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按照引用传递的;
function addProp(obj){
obj.name='lily';
}
var person =new Object();
addProp(person);
console.log(person.name);//lily
  • 作为局部变量的函数参数obj的属性name值改变了全局变量person的属性

  • 理解误区需要的概念:参考W3C

    • 基本类型值:指的是简单的数据段,比如Boolean、Number、String、Null、undefined
    • 引用类型值:由多个值构成的对象
  • 复制基本类型值过程:会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上

    • var x=5; var x=y;之后x与y就没有任何关系了就是两个独立的个体任何一方的改变不会对另一方造成影响
  • 复制引用类型值的过程:复制过程会将存储在变量对象中的值复制一份放到新变量分配的空间中,不同的是这个值时一个指针(或有着固定大小的地址段),而这个指针指向存储在内存中的一个对象,此时被复制和复制的变量都是有一个指针指向内存中的对象

    • 引用类型值得复制可以理解给一个人取两个名字,两个变量都有一个隐含和共同对象的关系
    • object1-------->Object<-----------object2
  • 用我们现在的知识看例子,在addProp中形参obj在函数调用时作为对象的person复制给了obj,此时两者就有了一个共同的引用指向Object,此时形参和实参会通过引用影响两者的属性,比如函数内部obj.name='lily',此时根据引用类型的复制规则,实参person也是增加了一个属性name;

  • 而这和我们处理基本数据类型的参数传递和复制的认知似乎不太一样,不太一样的原因主要是我们对于参数只能按值传递中值得理解和作用域链的规则理解:

    • 作用域:函数内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数,这些环境之间的联系是线性、有次序的;每个环境都可以向上搜索作用域,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。(来至高程3)
  • 参数传递引用类型值时,进行的是值得复制,而并没有破坏作用域链的规则

  • 同时局部作用域中修改的对象会在全局作用域中反映出来,和参数只按照值传递也并不冲突,因为我们实参和形参是复制的地址,指向同一个对象的地址,这个地址才是高程3所讲到的值

  • 再用例子理解一遍:

var obj1 = {
  value:'111'
};
 
var obj2 = {
  value:'222'
};
 
function changeStuff(obj){
  obj.value = '333';//obj1和obj此时指向同一个对象,obj属性的概念必然影响obj1,按照对象复制原则
  obj = obj2;//obj的引用被初始化改成了引用另外一个对象
  return obj.value;
}
 
 
var foo = changeStuff(obj1);
 
console.log(foo);// '222' 参数obj指向了新的对象obj2
console.log(obj1.value);//'333'


  • 再看这个例子
var v1 = []
var v2 = {};
var v3 = {};
function foo(v1, v2, v3)
{
    v1 = [1];
    v2 = [2];
    v3 = {a:3}
}

foo(v1, v2, v3);
alert(v1); // 空白
alert(v2); // [object Object]
alert(v3.a); // undefined

  • 再来理解下高程3给的结论,看是否理解
  • 高程3给出的结论是:访问变量有按照值和按引用访问两种方式,而函数参数只能按值传递
  • 高程3给的另一个结论:在向参数传递引用类型的值时,会把这个值的内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部
  • 总结
    • 好了到此我算是把为何在参数传递引用类型值的时看似全局变量更改了局部变量这个错误的认知给改过来了,而文中开头的那句话我也在通过查资料了解到,参数是按引用传递这个概念在js中完全去忽略它,按照js正常的规则去理解js的内容即可,这个概念可能存在于java或者c++中,而因为时间有限将来再研究。
    • 其实在解说这个问题的过程中我刻意的避免用内存中的栈和堆等概念去解释而是用js的原则解释,大部分网上的理解都是从栈和堆解释整个过程,整个过程还涉及到以下概念:内存垃圾回收,执行环境,解除引用等概念,我想您先通过上述的理解之后再来进一步理解内存中的概念进而去回看一些在js进阶中碰到的作用域链和闭包非常的有帮助,最起码你会有一个清晰的角度知道你不理解是这个方向的概念不理解,找资料即可,没啥难得。
    • 啰嗦一句:再理一下我解决这个问题的思路,首先我把高程3关于p70-p71反复看了四遍,还是感觉有点模糊,不是那种很爽的通透感,我就google了一下,找到了内存的理解,原来这个理解的方向是内存,这个是我之前没有的概念,后面就是顺着内存这条线整体算是清楚了,为何没用内存去讲解除了训练我自己是否真正理解这个概念之外,还有就是内存看了几篇文章感觉还不能完全说透
  • 参考文献和关于内存将来要看的内容如下:
    • 直接讲解参数传递
    • 讲解内存,从评论看应该有漏洞
    • 参数传递引用类型值是在传递地址
    • MDN上关于内存管理的理解
    • 维基百科上关于共享传递,引用传递,按值传递解答
    • js call by sharing的分析
      原文链接:从一个例子误区来理解JavaScript是按照值传递参数

你可能感兴趣的:(从一个例子误区来理解JavaScript是按照值传递参数)