Giter VIP home page Giter VIP logo

Comments (148)

MrGoodBye avatar MrGoodBye commented on May 2, 2024 278

@mqyqingfeng 我对于参数传递方式的学习路径就是:

在你这学的...

但是在了解到这个知识点之前,我大致也明白参数传递的形式.

关键点:

运算符=就是创建或修改变量在内存中的指向.
初始化变量时为创建,重新赋值即为修改.

首先一个非常简单的例子:

var a = {b: 1};// a = {b: 1}
var c = a;// c = {b: 1}
a = 2;// 重新赋值a
console.log(c);// {b: 1}

接着是上一段代码在内存中的分布:

a, c {b: 1}

然后一步一步执行代码:

  1. 创建变量a指向对象{b: 1};
  2. 创建变量c指向对象{b: 1};
  3. a重新指向常量区的2;
常量区
a 2
c {b: 1}

所以c从始至终都是指向对象{b: 1}.

var value = 1;
function foo(v) {
    v = 2;
    console.log(v); //2
}
foo(value);
console.log(value) // 1

将案例一等价替换:

var value = 1;
function foo() {
    var v = value; // 创建变量v指向value所指向的值
    v = 2;// v重新指向另外的值
    console.log(v); //2
}
foo(value);
console.log(value) // 1,value从始至终都未改变指向.

案例三也可以这样替换.

接着分析案例二:

修改一下我的第一个例子:

var a = {b: 1};// a = {b: 1}
var c = a;// c = {b: 1}
a.b = 2;// 重新赋值对象a中的属性b
console.log(c);// {b: 2},// c也随着修改,从

在内存中的分布:

常量区
a,c [[Object]]
b 1

执行完a.b = 2后:

常量区
a,c [[Object]]
b 2

那么a,c从始至终都未改变指向,只是b改变了而已
第一张内存分布图将{b: 1}放入堆中,是为了大家更方便抓住重点

所以案例二等量替换为

var obj = {
   value: 1
};
function foo() {
   var o = obj;
   o.value = 2;// 变量value改变了指向,而o并未改变
   console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2

from blog.

wamich avatar wamich commented on May 2, 2024 204

哈哈,本来没看懂,基于axdhxyzx的观点,觉得反而更理解mqyqingfeng的意思了。我试着说下类比的理解:

A、变量名变量值的关系好比快捷方式真实文件的关系
B、值类型类比为文件 引用类型类比为文件夹

文中的第三种传递方式
//1、2
var obj = {value: 1};  
//4
function foo(o) {
    //5
    o = 2;
    console.log(o); 
}
//3
foo(obj); 
console.log(obj.value) 

1.创建文件夹“{value: 1}”
2.创建一个快捷方式obj
3.实参:步骤2创建的快捷方式
4.形参:创建o快捷方式,但o不指向obj指向的文件夹,却指向了快捷方式obj本身(快捷方式的快捷方式叫高阶快捷方式?哈哈,应该就是就是共享传递的意思吧)
5.修改o快捷方式的指向,改为指向文件“2”

from blog.

marty203 avatar marty203 commented on May 2, 2024 196

我个人认为你的理解有误, 红宝书说 ECMAScript 中所有函数的参数都是按值传递的, 这是没错的. 关键在于如何理解值传递和引用类型, 这个概念我很早在C#上深入研究一番(在<C#本质论>的指导下). 而 JavaScript 的引擎是 C++ 实现的, 所以在这一块概念上C# 与 C++ 大致一样.

C# 的数据类型分为 2 种: 值类型和引用类型, 而方法参数的传递方式也分为 2 种: 值传递和引用传递, 这里要强调的是数据类型和方法参数的传递方式没有半毛钱关系. 这两者排列组合后得到4种情况:

  1. 方法参数类型是值类型, 用值传递;
  2. 方法参数类型是引用类型, 用值传递;
  3. 方法参数类型是值类型, 用引用传递;
  4. 方法参数类型是引用类型, 用引用传递.

ECMAScript 如何实现方法参数用引用传递, 我实际使用中没用到过, 这里不敢妄言, 但是你在"引用传递"中举的例子, 很明显是错误的, 它只是方法参数是引用类型, 但是用的是值传递方式, 这也印证了红宝书上说的那句话.

下面我先说说 C# 里的这4种情况.

首先, 弄清楚方法参数传递方式. C# 区分值传递和引用传递很方便, 方法参数前加ref (out修饰符这里不讨论)就是引用传递, 什么都不加就是值传递. 我们都知道方法参数有实参和形参之说, 而参数传递方式说的就是从实参给形参复制的过程. 值传递就是把实参在内存栈中的数据传递给形参, 然后你在方法内部就可以使用形参了, 而引用传递是把实参的内存栈的地址编号传递给形参.

其次, 弄清楚数据类型, 值类型就是内存中某个地址直接保存了值, 比如 int i = 10; ( js 对应写法: var i = 10;), 运行时会在内存的栈中分配一个地址 001, 并在这个地方保存 10. 而引用类型则需要在内存中某个地址先保存实际的对象实例, 然后在内存的另一个地址保存指向那个对象实例的指针, 比如 MyClass obj = new MyClass { value = 10 }; (js对应写法: var obj = { value: 10 };), 运行时首先在内存的托管堆中保存一个 MyClass 的实例对象, 它的属性 value=10, 再到内存的栈中分配一个地址 002, 并在这里保存在托管堆中那个对象的内存地址(我们可以把这个内存地址简化理解成指向对象实例的指针). 这就是值类型和引用类型的区别.

回过来再看你的例子, 第一个是"按值传递", 这个例子符合方法参数是值类型并用值传递这种情况, value是值类型, 它在内存栈中的地址001保存了1这个数值, 在 foo(value); 这句, value 是实参, 而 foo 函数声明中的 v 是形参, js 引擎在内存栈中为形参 v 分配了一个地址002, 其中也保存了 1 这个值, 这时修改 v 的值, 是修改内存地址 002 里的值, 而地址 001 里的值没变, 所以在 foo 函数执行完, 再打印 value 时, 依然是1.

接下来看第二个"引用传递", 我认为这个说法是错误的, 正确的说法应该是引用类型并用值传递. obj是引用类型, 它需要在内存堆中(js引擎可能不存在托管的概念, 所以这里称为内存堆)分配一个内存地址012, 保存了它的一个对象(属性value和其值1, 这句说的不严谨, 不过不影响对本例的分析), 并在内存栈中分配了一个地址011, 这个地址保存了012(就是那个内存堆的地址, 可以理解为指针). 在foo(obj);这句, obj是实参, 而foo函数声明中的o是形参, js引擎在内存栈中为形参o分配了一个地址013, 其中也保存了012这个值, 012其实并不是像前一个例子中说的1那样的数值, 而是一个内存地址, 所以如果你打印o这个形参, 它不会把012这个值打印出来, 而是把012内存地址里保存的实例对象给打印出来. 到这里就很清楚了, 如果你修改了012指向的那个对象的属性value的值, 那么当你在打印obj这个实参时, 它的obj.value会打印出2, 而不是1.

你的第三个例子"共享传递", "共享传递"这个概念我不是很清楚, 但我觉得你举的这个例子依然是值传递, 唯一与C#不同的是, C#的变量类型定义后不能改变, 而JS的变量类型是可以随意改变的, 因此这个例子无法跟C#中的值传递来类比. 再来分析你这个例子, 首先obj实例化一个对象, 有一个属性value, 值为1, 在内存中就是现在内存堆中分配一个内存空间, 其地址为022, 保存了一个对象(包括它的属性value和值1), 然后再到内存栈中分配一个内存地址021, 保存了内存地址022这个值. 在foo(obj);这句, obj是实参, 而o是形参, 这时在内存栈中给形参o分配了一个地址023, 也保存022这个值(如果在o=2;之前打印o, 将输出undefined, 这里是由于在foo函数作用域内对变量o进行赋值操作, 因此在这个作用域内使用了局部变量o覆盖了形参o, 而局部变量o在使用时没有声明, 所以js引擎会把它的声明提升到作用域最顶部, 因此在赋值语句之前打印, 会输出undefined, 声明提升这个概念暂时也不深入展开感谢@daizengyu123 的指正, 这里因为调用foo函数时给形参o赋值了, 所以在调用o = 2;之前打印, 会输出对象{value: 1}), 而在foo函数中, 又给形参o重新赋值2, 由于2是Number类型, 这是值类型, 因此不用在内存堆中存储数据, 直接在内存栈中即可, 这句赋值语句, 相当于把内存地址023中的值022改为2, 而并没有修改内存地址021(也就是变量obj)的值, 所以在调用foo函数之后再打印obj.value时, 仍然打印出1. 这里如果把o = 2;这句替换为o = { value = 5, other = "abc" };也是同理.

最后补充一下C#中的引用类型的值传递和引用类型的引用传递的对比. 简单来说, 引用类型的值传递, 在方法内部如果对形参重新赋值, 哪怕是同一个类的对象, 在赋值后修改对象的属性, 实参的对应的属性值都不会改变, 同时实参指向的对象也不变, 而形参在重新赋值后已经指向一个新的对象了; 而引用类型的引用传递, 在方法内部如果对形参重新赋值, 那么实参也跟着重新赋值, 实参最初所指向的那个对象将不被任何变量所指向.

from blog.

sunsl516 avatar sunsl516 commented on May 2, 2024 83

例子一:

var value = 1;
function foo(v) {
    v = 2;
    console.log(v); //2
}
foo(value);
console.log(value) // 1

内存分布如下:

改变前:

栈内存堆内存
value1
v1
改变后:
栈内存堆内存
value1
v2

例子二:

var obj = {
value: 1
};
function foo(o) {
    o.value = 2;
    console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2

内存分布如下:

改变前:

栈内存堆内存
obj,o 指针地址{value: 1}
改变后:
栈内存堆内存
obj,o 指针地址{value: 2}

例子三:

var obj = {
value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

内存分布如下:

改变前:

栈内存堆内存
obj,o 指针地址{value: 1}
改变后:
栈内存堆内存
obj 指针地址{value: 1}
o 2

以上简要帮博主做个补充,这样就很明确了吧。如有不正之处欢迎指出。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 73

感谢建议,我们俩的学习经历不一样,我也来讲讲我的学习过程。

如果是只有按值传递,作为一个没有接触栈堆的初学者,我不明白为什么在第一个例子中,原值没有被修改,而第二个例子中,原值就被修改了,难道结果不应该是原值都没有被修改吗?

于是我去查找资料,这才接触了原来还有按引用传递,所以当时的我认为当值是引用类型的时候,其实是按引用传递的。

后来看了高程,发现函数参数都是按值传递,一度开始质疑高程是写错了,直到后来接触了call by sharing 的概念,这才恍然大悟,才想明白 按值传递拷贝了原值,按共享传递拷贝了引用,都是拷贝值,所以可以理解成都是按值传递。

所以我赞同高程的说法,但到我理解高程这句话的时候,其实是经历了看山是山,看山不是山,再到看山是山的一个过程,这篇文章为什么要这么写其实就是根据我的经历而来,在我的学习过程中,理解共享传递正是我从”看山不是山“到”看山是山“的转折点。

所以还是大家的经历不一样,看待文章的角度也不一样。为了不让大家误解,我觉得应该修改一下文章。感谢你的回复,以后多多交流哈~ o( ̄▽ ̄)d

from blog.

lynn1824 avatar lynn1824 commented on May 2, 2024 43

按值传递没有错
javascript中数据类型分为基本类型与引用类型;
基本类型值存储于栈内存中,传递的就是当前值,修改不会影响原有变量的值;
引用类型值其实也存于栈内存中,只是它的值是指向堆内存当中实际值的一个地址;索引引用传递传的值是栈内存当中的引用地址,当改变时,改变了堆内存当中的实际值;

from blog.

marty203 avatar marty203 commented on May 2, 2024 38

这么说吧, 不管是你前面写的文章, 还是你后面回复我的评论, 我觉得我都是能看懂的, 正如你所说的"应该也是一个意思吧".

可是如果是给初学者来看, "共享传递"这个概念该如何理解? 尤其是没有在内存堆栈这个层面说明参数传递方式的话, 初学者会不会产生误解? 我当年初学入门时, 就是因为对数据的引用类型和方法参数的引用传递没分清楚, 所以才查找书籍中的相关理论和在程序代码中进行实证的, 最终才完全搞清楚两者之间的区别.

如果只有按值传递这一种传参方式, 我们就完全没必要去讲解参数传递方式了, 只要讲清楚数据的值类型和引用类型就可以了, 毕竟值类型的值传递和引用类型的值传递在内存栈上的拷贝方式是完全相同的, 唯一差别就在于值类型和引用类型的差别了.

最后, 我说明一下, 看了你回复我的评论, 我觉得你的理解没有问题(前一条我说你理解有误, 我承认这是不对的). 只是说在JavaScript动态类型的基础上, 把值传递引申出一个"共享传递"概念, 是否会对初学者在这块理解上引起混乱, 你可以稍微考虑一下. 至此, 我对你的论述基本认同.

from blog.

cbbfcd avatar cbbfcd commented on May 2, 2024 31

干脆就叫拷贝传递,不管是基本数据类型还是对象类型的,都是拷贝。前者拷贝值,后者拷贝引用。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 20

哈哈,@axdhxyzx 感谢回复这么长的内容给我,我也来说下我的看法。

首先,第二个例子肯定不是真正的引用传递,这个我是知道的,毕竟我都说了ECMAScript中所有函数的参数都是按值传递的,而第二个例子就是用 JS 写的,怎么可能会是引用传递呢?我写这篇文章的思路是当值是引用类型的是时候,它可能是引用传递,因为它有着类似引用传递的表现,但是通过第三个例子,我又证明第二个例子其实不是引用传递,然后引申出第三种传递方式,按共享传递。所以虽然我写了三个例子,但是只有按值传递和按共享传递两种方式,这个在文章的最后我也讲了:“所以第二个和第三个例子其实都是按共享传递。” 不过这个地方估计让很多人都误解了,这是我的错。

其次,按共享传递依然是按值传递,我也是这样认为的呐,很多人还认为按引用传递也是按值传递,只是值是指针而已,这个说法也对,只是我们把所有的情况都归到按值传递上,看似统一了,但是如果我们要分析具体的情况时,一句按值传递可不好让人清晰的明白问题呐,所以才有了按引用传递和按共享传递的概念的出现。

最后,按共享传递的例子,如你所说, (以下可能有点不严谨,达意即可) 021 是这个对象,022是指针,023 也保存了 022 这个值,这跟文章中加粗的那一句 按共享传递是传递对象的引用的副本应该是一个意思吧,而且因为拷贝副本也是一种值的拷贝,所以你认为这也是一种值传递,这跟文章的倒数第二句 但是因为拷贝副本也是一种值的拷贝,所以在高程中也直接认为是按值传递了应该也是一个意思吧。

欢迎讨论哈~

from blog.

BuptStEve avatar BuptStEve commented on May 2, 2024 12

其实传递的不就是引用的值么...

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 10

@jawil 哈哈,请献上自己的膝盖~😂😂😂
@wamich 不收他膝盖的话,他有1024邀请码,(๑•̀ㅂ•́)و✧

from blog.

keminu avatar keminu commented on May 2, 2024 10

@mqyqingfeng
JavaScript中没有指针,所以在JavaScript中不存在变量指向另一个变量的引用,所有变量引用指向的都是值。函数在执行的过程中,形参对实参做一次复制,是使用值复制还是使用引用复制,则由值的类型决定。
直接举第三个例子来说明:

var obj = {
    value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

变量obj引用指向值 { value: 1 },foo 执行时形参对实参做一次复制使得形参o引用也是指向值 { value: 1 },这里注意o并不是指向obj;接着执行o = 2,相当于更新了o的引用,使o引用指向常量2,但是其实我们并没有改变原来的指向值 { value: 1 },所以obj的引用指向值是没有变的。如果执行的是o.value = 2,相当于是改变了原来的指向值 { value: 1 },变成了{ value: 2 },所以obj的引用指向最后的更新值 { value: 2 }。
以上从另外一个角度解释了下参数传递。

from blog.

shenchaoran avatar shenchaoran commented on May 2, 2024 5

看这篇文章很容易理解的:JavaScript变量——栈内存or堆内存

简单来说,就是

  • 基本类型存在栈里
  • 引用类型存在堆里,而引用变量的指针存在栈里

对于深浅拷贝

  • 基本类型全部都是深拷贝
  • 引用类型具体看是不是递归地拷贝基本类型的键值对

对于函数参数传值/址

  • 基本类型传的是值
  • 引用类型传的是址

所以,

  • 例一中两者不一样的原因是,基本类型的深拷贝导致这就是两个内存数据
  • 例二中是按址传值,在函数中改变堆内存中的基本数据类型,相当于重新开辟了一块栈内存
  • 例三中还是按址传值,形参和实参是两个都指向相同堆内存的指针,在函数中改变了形参的指针,而实参的指针不变,所以不影响原始值。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 4

@sunsl516 关于堆栈,我也只知道一点点……不过你启发了我,堆栈可以作为一个新课题进行研究~ o( ̄▽ ̄)d

from blog.

imaxing avatar imaxing commented on May 2, 2024 4

有几个疑问, 刚刚看到一个前辈说都是拷贝引用

干脆就叫拷贝传递,不管是基本数据类型还是对象类型的,都是拷贝。前者拷贝值,后者拷贝引用。

  1. 那么既然是拷贝引用, 在 foo 函数内修改 o.value 的话应该是修改的 obj 的拷贝值, 为什么全局中的 obj.value 也发生了变化呢?
  2. 修改 o.value 才会去按照引用地址去修改原值, 直接修改 o = 2 就不会修改原值了?
  3. 修改 o = 2的话难道修改的只是函数内部的形参吗?
  4. 这个形参被赋给的值是 obj 的拷贝才不会去修改原始值吗?
  5. 引用类型拷贝的话意思是再栈内存中又会多一个位置指向了拷贝的值的引用地址吗?
    麻烦指点下

--- 更新下, 感谢大大, 现在上面问题都明白了

  1. 修改的拷贝值是因为拷贝过来的是引用地址, 而不是重新 copy 一份存放在堆内存中, 所以修改的话仍然修改的是同一个地址的值
  2. o = 2 的是就把 o 与 obj 的拷贝引用切断了, 重新为o 在栈内存中开辟了一块内存, 所以不会在影响原始值
  3. 原因同上
  4. 同上
  5. 仍然同上

以上问题都是因为我没有理解引用拷贝的真正意义理解了引用类型是拷贝引用地址以上问题都迎刃而解了, 再次感谢大大

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 3

@wamich 形象的比喻!!!o( ̄▽ ̄)d

from blog.

 avatar commented on May 2, 2024 3

我愣是把所有评论看完了...........................................

from blog.

Latube avatar Latube commented on May 2, 2024 2

@DaVinciSecret 没毛病啊,因为是值传递,函数内部的v1v2v3指向了新的对象,函数外部 v1v2v3的指向不会发生变化

from blog.

HoroXu avatar HoroXu commented on May 2, 2024 2

我觉得 就是 一个 复制值 一个复制指针。看的都晕了

from blog.

liangtongxie avatar liangtongxie commented on May 2, 2024 1

@mqyqingfeng ,谢谢分享。

1、其实函数传参就是相当于给形参赋值,

第三个例子, foo(obj) 这里执行的时候, 形参部分相当于 o = obj ;
这样理解的话,和外面的赋值操作没什么区别,感觉也好理解按值传递。

2、“ 按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!”

按引用传递:

   var obj = {
           age:23
      }
     var a  = obj;
     var b  = obj;

上面的a 和 b 按 @axdhxyzx 说法也是存“引用的副本”(没理解错的话),即obj实例对象的存放地址吧?

关于用共享传递这个概念,还是感觉绕了路。

欢迎讨论。

from blog.

daizengyu123 avatar daizengyu123 commented on May 2, 2024 1

@axdhxyzx 讲的很明白了,不过还是有个小问题。你提到在第三个例子中如果在o=2;之前打印o, 将输出undefined。其实这里的局部变量o是有值的,不会为undefined。

from blog.

liubiggun avatar liubiggun commented on May 2, 2024 1

有点像是c里面的指针传递呀

from blog.

Idealv avatar Idealv commented on May 2, 2024 1

@joy-yu 内存空间详解这个应该能解决你的问题。

from blog.

ZhengLiJing avatar ZhengLiJing commented on May 2, 2024 1

这个深入系列让我受益匪浅,感谢作者大大,上次网易电话面试被虐了,发誓要深入学习JS,不能光停留在表面。这个问题从头读到尾,感触良多,结合的学习历程,画了幅图,希望能有所启发。
2018-01-27_160943

from blog.

binginsist avatar binginsist commented on May 2, 2024 1
var obj = {
        value: 1
};
function foo(o) {
        //在初始化时o=undefined
        o = {value: 4};
        console.log(o);//{value:4}
 }
foo(obj);
console.log(obj) //{value:1}

共享传递的概念不是很清楚,高程里的按值传递说的很准确,说下自己关于第三个代码的理解
1.在初始化时,因为有形参所以在内存里开辟一块空间此时o=undefined
2.当执行函数时foo(obj),将obj的引用地址的值给o,所以o指向001(假设此时obj的引用地址是001),此时o和obj指向同一个对象
3.执行o = {value: 4};,将 {value: 4}的引用地址002给o,此时o指向002(ps:这种情况下是修改o的指向,上面的第二个代码是修改堆里的对象是不一样的)
4.最后obj还是指向001,o指向002

from blog.

flymie avatar flymie commented on May 2, 2024 1

这是书中的片段:

当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。

var obj = {
    value: 1
};
function foo(o) {
    o.value = 2;  // 修改指针指向的对象。因为引用的是同一个对象,所以obj.value也变了
    o = 2; //修改指针对象。抱歉,你只是副本!我obj还是obj。
}
foo(obj);
console.log(obj.value) // 2

还可以这样理解?

from blog.

 avatar commented on May 2, 2024 1

ECMAScript 中所有函数的参数都是按值传递的

我其实是支持这句话的,为啥呢?因为函数参数的传递分两种,值传递以及引用传递。但是你会发现引用传递其实是传递内存地址,比如C语言的引用传递

#include <stdio.h>

void plusplus(int *a) {
    (*a)++;
}

int main() {
    int a = 1;

    printf("%d\n", a);

    plusplus(&a);

    printf("%d\n", a);

    return 0;
}

其实函数传参相当于隐性赋值,至于是不是引用传递还是值传递,你就把在函数里面把形参打印出来,看看是否是值还是内存地址。

from blog.

zhoubhin avatar zhoubhin commented on May 2, 2024 1

本质上都是值传递,对于基本类型,传递的是值本身,对于引用类型,传递的是引用地址。

from blog.

DavidQinqianWu avatar DavidQinqianWu commented on May 2, 2024 1

@mqyqingfeng
JavaScript中没有指针,所以在JavaScript中不存在变量指向另一个变量的引用,所有变量引用指向的都是值。函数在执行的过程中,形参对实参做一次复制,是使用值复制还是使用引用复制,则由值的类型决定。
直接举第三个例子来说明:

var obj = {
    value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

变量obj引用指向值 { value: 1 },foo 执行时形参对实参做一次复制使得形参o引用也是指向值 { value: 1 },这里注意o并不是指向obj;接着执行o = 2,相当于更新了o的引用,使o引用指向常量2,但是其实我们并没有改变原来的指向值 { value: 1 },所以obj的引用指向值是没有变的。如果执行的是o.value = 2,相当于是改变了原来的指向值 { value: 1 },变成了{ value: 2 },所以obj的引用指向最后的更新值 { value: 2 }。
以上从另外一个角度解释了下参数传递。

十分感谢这个回答,目前为止 最简单易懂

from blog.

sunsl516 avatar sunsl516 commented on May 2, 2024

博主写的真好。让我之前困惑很久的问题终于得到了解答,感谢博主的无私分享。提个小意见,仅供参考,文中开头可以先普及下堆栈的概念,说明下js中普通类型和引用类型分别是以什么方式存储在内存中的,最好画个图说明,这样在接下来的讲解中会容易很多,初学者也能看得懂。

from blog.

jawil avatar jawil commented on May 2, 2024

计算机果然到处都是相通的,这类比我服,请收下@mqyqingfeng的膝盖。@wamich

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@MrGoodBye 哈哈,感谢分享,是非常重要的补充,o( ̄▽ ̄)d

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@liangtongxie 仁者见仁哈~

from blog.

liangtongxie avatar liangtongxie commented on May 2, 2024

@mqyqingfeng 嗯嗯。握手。

from blog.

sunsl516 avatar sunsl516 commented on May 2, 2024

@MrGoodBye 这个常量区这个概念有吗。我查了挺多资料都没看到呢。欢迎指点。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@sunsl516 非常感谢补充~ 大家都来帮我补充,真是太感动了…… (ಥ_ಥ)

from blog.

Flying-Eagle2 avatar Flying-Eagle2 commented on May 2, 2024

第三个案例较难理解,我看完大家讨论的问题才搞懂,额,我是个初学者

from blog.

a1029563229 avatar a1029563229 commented on May 2, 2024

@sunsl516 @mqyqingfeng 大佬们受我一拜

from blog.

marty203 avatar marty203 commented on May 2, 2024

@daizengyu123 你说的对, 我想错了, 而且也没有实例验证. 因为在调用foo方法时给形参o传值了, 所以在重新赋值为2之前, 是有值的, 不是undefined. 如果我原评论没有修改的话, 其他的朋友请参照这一条. 截图如下:
_20170629111113

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@cbbfcd 哈哈~ 你道出了本质,就是这样

from blog.

ppmiao0628 avatar ppmiao0628 commented on May 2, 2024

ECMAScript中规定,所有函数的参数都是按值传递的,我也觉得第二个例子不应该给一个##引用传递
的标题,这样会让新手产生概念的误解,当然,您在末尾提到了本质是按值传递的。但是我还是觉得第二个例子的标题容易带偏新手的理解。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@ppmiao0628 说的非常在理~ o( ̄▽ ̄)d

from blog.

 avatar commented on May 2, 2024

@mqyqingfeng

// 第一种情况
var aa = {value: 1};
var bb = aa;
bb = 2;
console.log(aa); // {value: 1};
// 第二种情况
var aa = {value: 1};
var bb = aa;
bb.value = 2;
console.log(aa); // {value: 2};

第二和第三个例子跟我上面写的不是一样吗?
我上面写的就是引用复制啊,求解答。

from blog.

LeanNetwork avatar LeanNetwork commented on May 2, 2024

振聋发聩的感觉。赞

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@keminu 感谢补充~~ o( ̄▽ ̄)d

from blog.

Idealv avatar Idealv commented on May 2, 2024

大概明白了,就是传入基本数据类型是值传递。传入对象的话,是拷贝对象的引用(也就是对象的属性,那是不是可以理解为给对象添加属性,其实是添加了引用,引用指向属性对应的值?),在函数中修改传入的对象,不会影响原对象。但修改引用的话原对象的引用也会发生变化,因为引用的是同一个。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@IdealVillage 给对象添加属性,是通过引用找到了对象,再给这个对象添加的属性。修改引用的话,原对象的引用并不会发生变化,这就像:

var a = {value: 1};
var b = a;
b = 1;
console.log(a) // {value: 1}

不过我知道你肯定已经明白了这个意思,问题应该是来自 "引用"这个词,当做名词的话,它应该是指找到对象的“小纸条“,当做动词的话,应该是表示指向的意思,你说的”引用“应该是指指向的对象。

不过没有关系啦,明白就好~

from blog.

iiicon avatar iiicon commented on May 2, 2024

其实应该就是另一个值和另一个指针,只不过两个指针指向同一个对象,不知道理解得有问题没有。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@iiicon 没明白你说的意思哈……

from blog.

iiicon avatar iiicon commented on May 2, 2024

那可能我没理解到位😞😞😞

from blog.

joy-yu avatar joy-yu commented on May 2, 2024

这里在堆栈上有个疑问。
对象内的属性从堆栈角度理解是怎么存储的?

var a = 1;
var obj = {
  b: 2,
  c: { d: 4}
};

这里比较明确的是:
a存放在内存,值为1。
obj存放在内存,值为地址xx,该地址指向内存。

那么 obj 里面的属性呢?b、c 是否在内都存有值。
内b存储地址 yy,该地址指向内存,该内存里存储值2。
内c存储地址 zz,该地址指向内存,该内存里存储地址 ww,循环往复?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@joy-yu 你问倒我了,我对于堆栈没有研究过,现在还很难回答你这个问题…… 我会记下来,以后会再次回复你~

from blog.

cposture avatar cposture commented on May 2, 2024

@joy-yu 我是学习C/C++的,你的问题相当于C++领域的对象模型(普通对象在内存是怎么存储的,更复杂一点的,继承父类的子类又是怎么存储的),每一种JS实现的对象存储方式应该不一样的吧。

from blog.

joy-yu avatar joy-yu commented on May 2, 2024

@cposture 复杂的就先不考虑了,如果是像我上面写的那种普通对象存储,阁下有什么见解么

from blog.

zhang89 avatar zhang89 commented on May 2, 2024

可不可以这样理解:

function foo(v){
    //some code
}
foo(value)

foo(value)实际执行中是这样的

var v = value
//some code

如果这样理解,很好解释上面的例子
首先已知

var obj = {
    value: 1
};
var aaa=obj
aaa.value = 2
console.log(obj)//{value: 2}

在例二中:

var obj = {
    value: 1
};
function foo(o) {
    o.value = 2;
    console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2

foo(obj)的执行步骤为:

var o = obj
o.value = 2;
console.log(o.value); //2

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@ZhengLiJing 感谢分享~ 图画的非常好哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Saltfish3721 没有问题呀,现在想想或许使用这种方式会解释的更好呢~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@imaxing 好惭愧,我都没能及时回复……你应该感谢爱钻研的自己😀 ,与你共勉~

from blog.

imaxing avatar imaxing commented on May 2, 2024

from blog.

 avatar commented on May 2, 2024

[感觉这种参数传递可以用这个图表示
red cozwpiqmzl 63jqs1
至于这个代码
mzh11 e g d rme1 09qr
可以用这个图解释
yyigm x9j p gj 9 kgq
因此,其实就只有按值和按引用传递两种而已~

from blog.

MagicHacker avatar MagicHacker commented on May 2, 2024

引用是变量名,传递引用类型值的时候,按值传递传的是指针。按引用传递传递的就是一个变量。

from blog.

bravepg avatar bravepg commented on May 2, 2024

感觉分享,文本提到了共享传递、引用地址的拷贝等概念也许可以通过另一种方式理解:

共享传递时,我们可以把形参看做由两部分组成,一部分是形参本身、一部分是形参属性所指向的地址。形参本身是按值传递的,但形参属性所指向的地址却和实参属性地址一样,可以看作按引用传递。

var a = {
    x: 1
}
function foo(obj) {
    obj = {
        x: 2
    }
}
foo(a);
console.log(a);

最后输出的结果a仍然是{x:1}。这是因为形参本身是按值传递的,修改形参本身不会造成实参的修改,如果我们把上面的代码改成如下这样子

var a = {
    x:1
}
function foo (obj) {
    obj.x = 2;
}
foo(a);
console.log(a);

会发现a被修改为了{x:2},这是因为形参属性所指向的地址与实参所指向的地址一样,修改的形参的属性就同时修改了实参的属性。
所以,只需要记住五种基本类型按值传递,对于对象而言,直接修改形参对实参没有效果,而修改形参的属性却可以同时修改实参的属性。

from blog.

vanishcode avatar vanishcode commented on May 2, 2024

感觉就是类似

int swap(int *x, int *y)

{

	int temp;

	temp = *x; *x = *y; *y = temp;

	return temp;

}

void main()

{

	int a = 1, b = 2;

	int *p1 = &a;

	int *p2 = &b;

	swap(p1, p2)

}

这个吧 😂

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@xjh776 @MagicHacker @gaopeng0108 十分感谢大家的分享哈~ 🙇

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@vanishcode 哈哈,我只会 javascript …… o((⊙﹏⊙))o.

from blog.

ezioaudit avatar ezioaudit commented on May 2, 2024

博主和评论区的大神都非常棒,学习

from blog.

yh927 avatar yh927 commented on May 2, 2024

看了作者的所有系列,深有感悟,谢谢作者无私的奉献,希望多贡献一些精华。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@binginsist 正是这样的呀~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@flymie 正是如此~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@yh927 会一直坚持把四个系列全部写完~

from blog.

langxiaohui123 avatar langxiaohui123 commented on May 2, 2024

@flymie 看完了个人感觉你这个更容易让人理解!!个人感觉复杂的不是纠结按值传递还是按引用传递,重要的是理解传递的数据结构(也就是内存堆栈)这个才是根本,不知道对不对?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@langxiaohui123 对的 😀

from blog.

shaolonger avatar shaolonger commented on May 2, 2024

大家的讨论好激烈,看了一下主要是围绕高程里“ECMAScript 中所有函数的参数都是按值传递的”这句话展开的。

讲一下个人鄙解:我觉得这里“按值传递”的说法没毛病,大家都知道,在JS里有“基本”和“引用”2种数据类型,“基本”的为基本数据(number/string/boolean/undefined/null),“引用”的为一个指向堆(heap)里某个对象的内存地址。

所以在传参的时候,不管“基本”/“引用”数据,都是将它们的复制给形参,从本质来讲并没有“引用传递”或“共享传递”之类的说法,两者传递的方式都是值复制,不同的只是值读写。

稍微改动楼主的例子:

var obj = { // 在堆内存中创建一个对象{value:1},将其内存地址赋值给obj(假设为0x00123456)
    value: 0
};
function foo(o) { // 执行foo(obj)时,传入obj,将obj的值(0x00123456)复制给形参o
    o.value = 1; // 通过o的值(0x00123456)找到堆中的{value:1}对象,找到value属性并修改为1
    o = 2; // 将2复制给o,替代原值0x00123456
    console.log(o);  // 2
}
foo(obj); // 执行过程如上注释
console.log(obj.value) // 1

不知我的解释是否正确?
然而我认为高程里为了说明“对象是按值传递”所举的例子及说明不太正确,看看高程里4.1.3一节的例子:

function setName (obj) {
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name);

以下是我对该例子的理解:

function setName (obj) { // 传入person,将person的值(0x00654321)复制给形参obj
    obj.name = "Nicholas"; // 通过obj的值(0x00654321)找到{},创建"name"属性并将字符串"Nicholas"赋值过去
    obj = new Object(); // 在堆里创建一个空对象,并将其内存地址(假如为0x00666666)赋值给obj
    obj.name = "Greg"; // 通过obj的值(0x0066666)找到{},创建"name"属性并将字符串"Greg"赋值过去
}
var person = new Object(); // person的值是一个内存地址(假设为0x00654321),指向堆内存中的一个空对象{}
setName(person); // 执行过程如上注释
alert(person.name); // "Nicholas"

高程里对该例子的原文说明:

如果 person 是按引用传递的,那么 person 就会自动被修改为指向其 name 属性值为 "Greg" 的新对象。

这句话的意思是:如果person与obj之间是引用传递,则setName里对obj = new Object()的操作也会自动影响person???这明显不对吧,new Object()赋值给obj后明显obj的指针值已经发生改变了。

再看高程4.1.3里的原文:

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者用
ECMAScript 的概念来说,就是 arguments 对象中的一个元素)。在向参数传递引用类型的值时,会把
这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。

这段话后半部分“局部变量的变化会反应在函数的外部”也不对吧?

var obj = {
    value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

就以楼主第三个例子来讲,上面局部变量o的变化并没有反映在函数的外部呀???
所以我认为,高程这部分的内容并不严谨,我认为正确来讲应该是:

if (实参为引用类型 && 局部变量(形参)的值能通过内存地址搜索到) {
    局部变量.引用类型的方法 -> 函数外部的变化
} else {
    无影响
}

以上仅为个人当前学习的理解,如有不对请大家不吝指出,谢谢。

from blog.

thomaszhou63 avatar thomaszhou63 commented on May 2, 2024

首先先感谢博主的分享,但是在看你文章之前,我对值传递和引用传递有自己的理解。

// 值类型
 var a = 100
var b = a
a = 200
console.log(b) //100 修改a的值,b不变
//引用类型
            var a = { age: 20 }
            var b = a
            b.age = 21
            console.log(a.age) //21  修改b.age,a.age跟着变化

一个是引用传递,一个值传递,可以发现,如果我改变被引用赋值的值的内部的值(类比函数传递的参数,然后在函数内部修改该变量),其实依旧会修改原始值,但是毕竟是赋值操作啊

//第三种情况
            var a = { age: 20 }
            var b = a
            b.age = 21
            console.log(a.age) //21  修改b.age,a.age跟着变化

            console.log(b);//  { age: 20 }
            b = 2;
            console.log(b); // 2
            console.log(a); //  { age: 20 }
``
其实你说的第三种情况,就是覆盖了整体的值,那自然而然就不再是引用类型,而仅仅是是一个值类型,那也就是说和原始的值不相关了

from blog.

lushuhao avatar lushuhao commented on May 2, 2024

将函数的形参、实参,降级为赋值更好理解一点

2018-09-07 12 58 292018-09-07 13 05 09

形参等于创建一个局部变量,对象是引用传递,给局部变量重新赋值,不会影响到全局变量

修改局部变量,修改的是位于堆上的内存,栈上的引用并没有改变,局部变量与全局变量还是指向同一个

2018-09-07 13 15 012018-09-07 13 16 39

from blog.

lin-xii avatar lin-xii commented on May 2, 2024

感谢, 获益匪浅.

from blog.

zzmikl avatar zzmikl commented on May 2, 2024

image
我的理解是其实就是一个 首地址传递的问题 始终传递的都是对象的首地址 只是在传递的过程中 存在一些对象首地址与重新赋值的问题

from blog.

qianbaiduhai avatar qianbaiduhai commented on May 2, 2024

传递的是基本类型的就是复制过来的,obj.value是基本类型,那么就是复制过来的,修改不会对obj产生影响,假如这个变成传递obj,因为obj是引用类型,那么传递的是obj的地址,地址也是值,所以红宝书说的没错,就是这个意思吧

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024

@mqyqingfeng 我对于参数传递方式的学习路径就是:

在你这学的...

但是在了解到这个知识点之前,我大致也明白参数传递的形式.

关键点:

运算符=就是创建或修改变量在内存中的指向.
初始化变量时为创建,重新赋值即为修改.

首先一个非常简单的例子:

var a = {b: 1};// a = {b: 1}
var c = a;// c = {b: 1}
a = 2;// 重新赋值a
console.log(c);// {b: 1}

接着是上一段代码在内存中的分布:

栈 堆
a, c {b: 1}
然后一步一步执行代码:

  1. 创建变量a指向对象{b: 1};
  2. 创建变量c指向对象{b: 1};
  3. a重新指向常量区的2;

栈 堆 常量区
a 2
c {b: 1}

所以c从始至终都是指向对象{b: 1}.

var value = 1;
function foo(v) {
    v = 2;
    console.log(v); //2
}
foo(value);
console.log(value) // 1

将案例一等价替换:

var value = 1;
function foo() {
    var v = value; // 创建变量v指向value所指向的值
    v = 2;// v重新指向另外的值
    console.log(v); //2
}
foo(value);
console.log(value) // 1,value从始至终都未改变指向.

案例三也可以这样替换.

接着分析案例二:

修改一下我的第一个例子:

var a = {b: 1};// a = {b: 1}
var c = a;// c = {b: 1}
a.b = 2;// 重新赋值对象a中的属性b
console.log(c);// {b: 2},// c也随着修改,从

在内存中的分布:

栈 堆 常量区
a,c [[Object]]
b 1
执行完a.b = 2后:

栈 堆 常量区
a,c [[Object]]
b 2

那么a,c从始至终都未改变指向,只是b改变了而已
第一张内存分布图将{b: 1}放入堆中,是为了大家更方便抓住重点

所以案例二等量替换为

var obj = {
   value: 1
};
function foo() {
   var o = obj;
   o.value = 2;// 变量value改变了指向,而o并未改变
   console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2

image
你好,我觉得b应该一直在堆中吧?为什么会出现在栈里面呢?

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024

@mqyqingfeng
JavaScript中没有指针,所以在JavaScript中不存在变量指向另一个变量的引用,所有变量引用指向的都是值。函数在执行的过程中,形参对实参做一次复制,是使用值复制还是使用引用复制,则由值的类型决定。
直接举第三个例子来说明:

var obj = {
    value: 1
};
function foo(o) {
    o = 2;
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

变量obj引用指向值 { value: 1 },foo 执行时形参对实参做一次复制使得形参o引用也是指向值 { value: 1 },这里注意o并不是指向obj;接着执行o = 2,相当于更新了o的引用,使o引用指向常量2,但是其实我们并没有改变原来的指向值 { value: 1 },所以obj的引用指向值是没有变的。如果执行的是o.value = 2,相当于是改变了原来的指向值 { value: 1 },变成了{ value: 2 },所以obj的引用指向最后的更新值 { value: 2 }。
以上从另外一个角度解释了下参数传递。

多谢,感觉看你解释的更加清晰、简单、易懂

from blog.

wangdabaoqq avatar wangdabaoqq commented on May 2, 2024

对于第三个例子, 我更倾向与于 @imaxing 的解释。
它提到 o = 2 的是就把 o 与 obj 的拷贝引用切断了, 重新为o 在栈内存中开辟了一块内存, 所以不会在影响原始值
这种说法表明o的值储存在栈内存中, 而obj储存在堆内存中, 所以不是同一个引用地址也就不存在当修改o的值时, obj的值不会跟着修改。
这种解释对吗?麻烦您 @mqyqingfeng 能说明下吗。

from blog.

DaVinciSecret avatar DaVinciSecret commented on May 2, 2024

麻烦死了,看完评论都懵逼了

from blog.

ChiuMungZitAlexander avatar ChiuMungZitAlexander commented on May 2, 2024

其实很简单,没那么复杂。你用控制台就能理解:

// step1
var obj1 = { v: 1 };
var obj2 = obj1;

// step2
obj1.v = 999;
console.log(obj1, obj2); //  {v: 999} {v: 999}

// step3
obj1 = 123;
console.log(obj1, obj2); // 123 {v: 999}

传参其实就是这个原理。

Step1和Step2上面的人讲的都很详细,这个其实是“引用的值“的拷贝,或者理解成现在obj1和obj2指向同一个地方就好。

关键在于Step3。

如果是按引用传递,obj1和obj2的指向会被”绑定“,共享箭头,也就是说,随便改哪一个,都会使另一个跟着被改的那个”牵着鼻子“走。

如下图:

// 按引用传递
obj1 ───┐
        │───> {v: 1}
obj2 ───┘

// 修改任意
obj1 ───┐    [1, 2, 3]
        │───↗ {v: 1}
obj2 ───┘
  • 因为共享箭头,obj1指向变了会带着obj2的箭头也变,如果是这种情况,obj1赋值[1, 2, 3]之后,obj2应该也等于[1, 2, 3]才对,但实际并不是。

华丽的分割线

如果是共享传递,obj1和obj2指向同一个位置,但各有各的箭头。

// 按值传递
obj1 ───>  {v: 1}
obj2 ───↗

// 修改任意
          [1, 2, 3]
obj1 ───↗  {v: 1}
obj2 ───↗

*obj1指向另一个地方,而{v: 1}位置没变。obj2的箭头还指向原来的位置。

from blog.

DaVinciSecret avatar DaVinciSecret commented on May 2, 2024

帮忙解释解释:
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);
alert (v3.a);

from blog.

DaVinciSecret avatar DaVinciSecret commented on May 2, 2024

@Latube 我以为传的是引用

from blog.

Latube avatar Latube commented on May 2, 2024

@DaVinciSecret 了解了C中的指针后会很好理解的(^_^)

from blog.

g1f9 avatar g1f9 commented on May 2, 2024

我觉得还是修改下共享传递的概念吧,感觉解释得不怎么清晰。改成传递的是对象的指针更合适,指针也是值,所以按值传递也没毛病。

from blog.

TStoneLee avatar TStoneLee commented on May 2, 2024

案例一:
var obj = {
value: 1
};
function foo(o) {
o.value = 2;
console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
案例二:
var obj = {
value: 1
};
function foo(o) {
o = 2;
console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1

这两个案例中,修改的o不同, 案例一中是对象的属性,但是案例二中修改的o变量,那案例二中怎么会去修改对象的属性呢?(传入的是对象,但是案例二有去改变传入对象的属性值吗?)这个地方没有明白,大神能答疑一下吗?

from blog.

lunhui1994 avatar lunhui1994 commented on May 2, 2024

哈哈,本来没看懂,基于axdhxyzx的观点,觉得反而更理解mqyqingfeng的意思了。我试着说下类比的理解:

A、变量名变量值的关系好比快捷方式真实文件的关系
B、值类型类比为文件 引用类型类比为文件夹

文中的第三种传递方式

//1、2
var obj = {value: 1};
//4
function foo(o) {
//5
o = 2;
console.log(o);
}
//3
foo(obj);
console.log(obj.value)
1.创建文件夹“{value: 1}”
2.创建一个快捷方式obj
3.实参:步骤2创建的快捷方式
4.形参:创建o快捷方式,但o不指向obj指向的文件夹,却指向了快捷方式obj本身(快捷方式的快捷方式叫高阶快捷方式?哈哈,应该就是就是共享传递的意思吧)
5.修改o快捷方式的指向,改为指向文件“2”

不太明白高阶快捷方式,第4步o应该指向的就是文件夹吧,并不是指向obj本身吧。

from blog.

wamich avatar wamich commented on May 2, 2024

哈哈,本来没看懂,基于axdhxyzx的观点,觉得反而更理解mqyqingfeng的意思了。我试着说下类比的理解:
A、变量名变量值的关系好比快捷方式真实文件的关系
B、值类型类比为文件 引用类型类比为文件夹

文中的第三种传递方式

//1、2
var obj = {value: 1};
//4
function foo(o) {
//5
o = 2;
console.log(o);
}
//3
foo(obj);
console.log(obj.value)
1.创建文件夹“{value: 1}”
2.创建一个快捷方式obj
3.实参:步骤2创建的快捷方式
4.形参:创建o快捷方式,但o不指向obj指向的文件夹,却指向了快捷方式obj本身(快捷方式的快捷方式叫高阶快捷方式?哈哈,应该就是就是共享传递的意思吧)
5.修改o快捷方式的指向,改为指向文件“2”

不太明白高阶快捷方式,第4步o应该指向的就是文件夹吧,并不是指向obj本身吧。

现在看来确实歪理邪说,你看下 @sunsl516 @ChiuMungZitAlexander 的解答,他们写的很好

from blog.

rutingjin avatar rutingjin commented on May 2, 2024

感觉将引用类型(A)的变量(*A)作为参数传递给函数,像是创建了一个指向该引用的指针(*A)的指针(**A)。然后将这个指针(**A)传递给了函数,修改(**A)的属性的操作导致A的属性值改变了,而直接赋值的话,只是改变了指针(**A)的指向,A的值并未受到影响。这么理解不知道对不对?

from blog.

wzb96 avatar wzb96 commented on May 2, 2024

我觉得最后一个例子也是按值传递没有错

  1. o一开始的值是obj的值(按值传递),它们都是一个地址,指向obj指向的对象
  2. o赋值为2,所以o的值就是2,此时o与obj没有任何关系,obj依然指向原有的对象

from blog.

a87604476 avatar a87604476 commented on May 2, 2024

感觉将引用类型(A)的变量(*A)作为参数传递给函数,像是创建了一个指向该引用的指针(*A)的指针(**A)。然后将这个指针(**A)传递给了函数,修改(**A)的属性的操作导致A的属性值改变了,而直接赋值的话,只是改变了指针(**A)的指向,A的值并未受到影响。这么理解不知道对不对?

恩,是的,是这么理解,无论基本类型传递还是引用传递其实都是值传递,基本类型就是传递基本类型的值,比如1,2,3这样,引用类型传递的是地址值

from blog.

Chance722 avatar Chance722 commented on May 2, 2024

获益匪浅 感谢

from blog.

fengandzhy avatar fengandzhy commented on May 2, 2024

这个插入代码怎么实现换行啊?你看我下面代码死活换行不了。
var value = 1; function foo() { var v = value; // 创建变量v指向value所指向的值 v = 2;// v重新指向另外的值 console.log(v); //2 } foo(value); console.log(value) // 1,value从始至终都未改变指向.

from blog.

xucongxin avatar xucongxin commented on May 2, 2024

收获颇丰,感谢

from blog.

chaihongjun avatar chaihongjun commented on May 2, 2024

函数传参,如果是基本类型变量,那么就是复制这个基本类型变量的值,如果是对象类型,复制的是这个变量的指针(内存地址)。“ECMAscript中所有函数的参数都是按值传递”。这里的值,一个是真实的变量(基本类型)值,一个是对象的内存地址。不知道这么理解对不对。

from blog.

chaihongjun avatar chaihongjun commented on May 2, 2024

我觉得 就是 一个 复制值 一个复制指针。看的都晕了

跟我的感觉一样,我就是这么理解的。

from blog.

id77 avatar id77 commented on May 2, 2024
var obj = {
    value: 1
};
function foo(o) {
    console.log(o === obj);
    o = 2;
    console.log(o); //2
}
foo(obj);

true
2

from blog.

a87604476 avatar a87604476 commented on May 2, 2024

from blog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.