第 98 题:写出如下代码的打印结果
yygmind opened this issue · 45 comments
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object()
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);
http://www.baidu.com
函数的形参是值传递的
I think it’s baidu
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object() // 形参 o 的指向发生改变,指向堆内存中一个新的对象
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl); //"http://www.baidu.com"
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object() // 形参 o 的指向发生改变,指向堆内存中一个新的对象
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl); //"http://www.baidu.com"
所以下面谷歌的赋值是指向新的对象的赋值吗?
- 答案:”http://www.baidu.com“
- 解析:执行上下文的建立过程
函数的形参是值的传递,传递对象的话,函数接受的是这个对象的指针。
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object()//这是一个新的对象
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);
//www.baidu.com
http://www.baidu.com
函数的形参是值传递的
感觉这么说不准确。对象传值传的是引用,但是引用是copy给函数形参。
// 这里把o改成a
// webSite引用地址的值copy给a了
function changeObjProperty(a) {
// 改变对应地址内的对象属性值
a.siteUrl = "http://www.baidu.com"
// 变量a指向新的地址 以后的变动和旧地址无关
a = new Object()
a.siteUrl = "http://www.google.com"
a.name = 456
}
var webSite = new Object();
webSite.name = '123'
changeObjProperty(webSite);
console.log(webSite); // {name: 123, siteUrl: 'http://www.baidu.com'}
传进函数的是原对象的地址(或者说引用),这个地址赋值给了形参(形参看做局部变量),形参变量此时指向原对象,后面o=new object的时候,形参变量保存的是新对象的地址,指向的是新的对象,所以第二次的o.siteUrl 也是给这个新对象属性的赋值,和旧对象无关。最后打印website.SiteUrl 的时候,访问的是旧对象,因为前面的改动都只涉及到形参变量,和website无关,website依然保存着旧对象的引用。
http://www.baidu.com webSite属于复合数据类型,函数参数中以地址传递,修改值会影响到原始值,但如果将其完全替换成另一个值,则原来的值不会受到影响
输出http://www.baidu.com,入参是对象到话,传递到是引用复制, o.siteUrl = "http://www.baidu.com",是改变了实际到内存值,o = new Object()则改变该引用指向,所以o.siteUrl = "http://www.google.com"不会改变值
function(o) 里 o形参是一个LHS查询 相当于 function() { var o = 实参 }
这个题目可以用这样一个角度看
var o = website;
o.siteurl = 'baidu';
o = new Object()
console.log(website.siteurl) // '百度'
function changeObjProperty(o) { // 指向内存中的对象,在这里叫做引用o1, o是函数内的一个声明的对象,与下面传进来的webSite的引用相同
o.siteUrl = "http://www.baidu.com" // 引用在引用o1上加属性
o = new Object() // 这里是引用o2,与下面webSite的引用不同了
o.siteUrl = "http://www.google.com" // 引用o2上加属性
}
let webSite = new Object(); // webSite的引用一直是o1,没改变过
changeObjProperty(webSite);
console.log(webSite.siteUrl); // 所以是baidu.com
- 答案:”http://www.baidu.com“
- 解析:执行上下文的建立过程
请教一下,是因为o.siteUrl是变量声明,所以建立阶段不会被覆盖吗?
那console.log(website.siteUrl);这步的时候前面的代码已经执行了啊?为什么执行阶段也不覆盖呢?
- 答案:”http://www.baidu.com“
- 解析:执行上下文的建立过程
请教一下,是因为o.siteUrl是变量声明,所以建立阶段不会被覆盖吗?
那console.log(website.siteUrl);这步的时候前面的代码已经执行了啊?为什么执行阶段也不覆盖呢?
我觉得覆盖是对于同一个函数级作用域而言的
- 答案:”http://www.baidu.com“
- 解析:执行上下文的建立过程
请教一下,是因为o.siteUrl是变量声明,所以建立阶段不会被覆盖吗?
那console.log(website.siteUrl);这步的时候前面的代码已经执行了啊?为什么执行阶段也不覆盖呢?
抱歉,这里是我看错了题目,我前面看成题目是 var o = new Object()
,理解方向错了。实际上本题的关键应该是变量o所指向的内存(对象)发生了变化。
www.baidu.com 形参按值传
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com" //给webSite的siteUrl属性赋值
o = new Object() //修改o的引用,此时o与webSite无关了
o.siteUrl = "http://www.google.com" //给新o赋值
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl); //http://www.baidu.com
function changeObjProperty(o, str) {
console.log(copy === o) // true
o.siteUrl = "http://www.baidu.com"
o = new Object()
o.siteUrl = "http://www.google.com"
str = 456;
}
let webSite = new Object();
let copy = webSite
let str = '123'
console.log(copy === webSite) // true
changeObjProperty(webSite, str);
console.log(webSite.siteUrl); // http://www.baidu.com
console.log(str) // '123'
函数传参,引用类型 copy 对象指向的内存地址,基本类型 copy 值
一开始真没转过弯来,实践了一下,才明白过来,顺便在这里记录一下:
先让我改造一下:
function changeObjProperty(o) {
// o这个形参实际可以写成var o = webSite
o.siteUrl = "http://www.baidu.com";
// 偷偷添加一行代码,此时website和o指向同一个堆地址
// website.siteUrl = "http://www.baidu.com";
o = new Object() // 相当于 o = {}, o 移情别恋到新的对象引用上去了,而webSite还死心塌地之前的对象引用,因此分道扬镳了
o.siteUrl = "http://www.google.com"
return o;
}
let webSite = new Object(); // let webSite = {}
changeObjProperty(webSite);
console.log(webSite.siteUrl); // http://www.baidu.com
let newo = changeObjProperty(webSite);
console.log(newo.siteUrl) // "http://www.google.com"
因为website是引用类型,o只是对website的引用,改变引用类型的属性可以,但如果o重新赋值相当于重新开辟一块地址,不影响外面的对象(website还是指向原来的地址)
函数的形参与实参修改并非双向~~
正常编程时,不应该修改命名形参;arguments对象中的实参值,都应该作为只读常量来处理
function doAdd(num1, num2) {
arguments[1] = 10
num1 = 50
console.log(arguments[0] + num2)
}
doAdd(10, 20) //60
var o = {}
var o1 = o
o.name = "张三"
o1 = {}
o1.name = "李四"
console.log(o.name)
//o1就可以理解为在函数中使用的形参
//传参也是一个变量向另一个变量赋值的过程,当类型为引用类型时,传递的是指针
传递参数:
ECMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个
变量复制到另一个变量一样。基本类型的传递如同基本类型变量的复制一样,而引用类型的值传递,如同引用类型变量的复制一样
可能会困惑:因为访问变量有按值和按引用两种方式,而参数只能按值传递。
会错误的认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的
证明对象是按值传递的:
` function setName(obj)
{
obj.name='Jack';
obj=new Object();
obj.name='Bob';
}
var person=new Person();
setName(person);
console.log(person.name); 输出Jack`
此例子在《js高级程序设计》一书里面有解释
// o 是对象webSite的引用
function changeObjProperty(o) {
// 给webSite的属性siteUrl赋值
o.siteUrl = "http://www.baidu.com"
// 创建新对象,新引用并赋值给o,下o非上o
o = new Object()
// 给新引用添加属性siteUrl并赋值,和webSite无关
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
// webSite 是个对象,以形参传递,传递的是webSite的引用
changeObjProperty(webSite);
console.log(webSite.siteUrl)
baidu
有些同学可能把[引用类型]和[按引用传递]混淆了 js里函数都是按值传递的
函数中的变量o是局部变量,一开始只是和website指向同一个内存,接着局部变量o指向了新的内存(o = new Object()),所以当再次给o添加属性时,由于指向的内存已经和website不一样了,所以website的属性并不会被修改。
一开始, webSite 和 o 指向的是同一个地址,所以当 o.siteUrl = "http://www.baidu.com" 时, webSite 也共享到了这个 siteUrl : "http://www.baidu.com" 的值。
执行o = new Object(),此时的 o 指向了一个新的地址,webSite 不再和 o 共享数据了,之后 o 再怎么操作都对 webSite 都没有影响了。
所以 webSite.siteUrl = "http://www.baidu.com"
function changeObjProperty(o) {
//函数参数可以理解为局部变量,传入引用类型执行时,可以理解为:局部变量o与全局变量website同时指向同一个对象
//o现在是引用类型,更改成功,siteUrl = "http://www.baidu.com"
o.siteUrl = "http://www.baidu.com"
//更改局部变量o的指向,指向一个新的对象
o = new Object()
//更改局部变量o指向的新的对象的siteUrl="http://www.google.com"
o.siteUrl = "http://www.google.com"
//函数执行结束,局部变量o以及指向的新对象释放
}
//初始化变量
let webSite = new Object();
//进入change调用
changeObjProperty(webSite);
//打印:"http://www.baidu.com"
console.log(webSite.siteUrl);
刚开始a的地址跟webSite的地址一样,假设都指向AAAAAA,AAAAAA里面存的是{siteUrl:"http://www.baidu.com"},后面a指向了新的地址假设叫BBBBBB。BBBBBB里面存的是{siteUrl : "http://www.google.com"},但是webSite指向的地址一直没变始终是AAAAAA
// 这里把o改成a // webSite引用地址的值copy给a了 function changeObjProperty(a) { // 改变对应地址内的对象属性值 a.siteUrl = "http://www.baidu.com" // 变量a指向新的地址 以后的变动和旧地址无关 a = new Object() a.siteUrl = "http://www.google.com" a.name = 456 } var webSite = new Object(); webSite.name = '123' changeObjProperty(webSite); console.log(webSite); // {name: 123, siteUrl: 'http://www.baidu.com'}
非常nice 这个题目和
var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };
console.log(a.x) // undefined
console.log(b.x) // {n:2}
这种类似
写出如下代码的打印结果
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object()
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);
- webSite该对象的地址传给了变量o
- 改变o的siteUrl时webSite对应的也改了
- o = new Object()相当于改变了o的地址,这以后的赋值操作和webSite 都无关了
- 所以打印webSite.siteUrl就是"http://www.baidu.com"
参考
传递参数:
ECMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个
变量复制到另一个变量一样。基本类型的传递如同基本类型变量的复制一样,而引用类型的值传递,如同引用类型变量的复制一样
http://www.baidu.com
函数的形参是值传递的感觉这么说不准确。对象传值传的是引用,但是引用是copy给函数形参。
// 这里把o改成a // webSite引用地址的值copy给a了 function changeObjProperty(a) { // 改变对应地址内的对象属性值 a.siteUrl = "http://www.baidu.com" // 变量a指向新的地址 以后的变动和旧地址无关 a = new Object() a.siteUrl = "http://www.google.com" a.name = 456 } var webSite = new Object(); webSite.name = '123' changeObjProperty(webSite); console.log(webSite); // {name: 123, siteUrl: 'http://www.baidu.com'}
// 这里把o改成a
function changeObjProperty(a) {
/**
* JS 函数预编译(解析)
*
* 1、执行前的一瞬间,会生成一个AO(action object)对象
* 2、将函数内的形参和变量声明存储到AO对象中,值为undefined
* 3、将实参和形参统一,形参作为AO对象的属性名,实参作为AO对象的属性值
*
* ① AO = {}
*
* ① 形成一个局部变量a(形参)
* AO = { a: undefined }
*
* ② 把形参与实参结合,即局部变量a与外部变量webSite指向的是同一块堆内存
* AO = { a: o的引用地址 }
*/
// 改变a对象里的值,即改变webSite对象中的值
a.siteUrl = "http://www.baidu.com"
// 更改局部变量a的引用,指向新的对象,之后与webSite对象无关
a = new Object()
a.siteUrl = "http://www.google.com"
a.name = 456
}
var webSite = new Object()
webSite.name = '123'
// 执行函数,把函数放入执行环境栈,进行函数预编译
changeObjProperty(webSite)
console.log(webSite) // {name: 123, siteUrl: 'http://www.baidu.com'}
打印的肯定是百度。
至于为什么?
在《JS高程》里,第三版4.1.3里面说到,
ES的所有函数的参数都是按值传递
但是回到这个问题下面,这个参数看起来像是引用传递, 其实传递的不是引用, 而是引用的一个副本。可以称之为共享传递。但是这个引用的副本传递,也可以叫做按值传递, 所以红宝书也没说错。所在在这一句
o.siteUrl = 'http://www.baidu.com'
确实把webSite.siteUrl
改成了baidu。但是后续的参数o又指向了新的对象地址。
let a = {}
let b = a
b.name = 'cc'
b = {}
b.name = 'ww'
console.log(a.name) // cc
console.log(b.name) // ww
参数 o
是函数内部的变量;
第一次修改 o.siteUrl
时,o
与 webSit
为同一个对象的引用,所以会影响到外面的 webSite
对象;
函数第二行给 o
赋值了一个新对象,此时 o
与 webSite
就是不同对象的引用了;
最后修改 o
时,改变的是新对象的 siteUrl
,而不会影响到外面的 webSite
;
// 参数按值传递
// 若传入基本类型值,那么函数参数的值就是传入的值
// 若传入引用类型值,那么函数参数的值是该引用类型值的内存地址
function changeObjProperty(o) {
// 给传入对象的siteUrl属性赋值为"http://www.baidu.com"
// 此时webSite对象新增了一个属性siteUrl,值为百度
o.siteUrl = "http://www.baidu.com"
// 参数o可以视为一个局部变量,其当前值为webSite对象的内存地址
// 给o重新复制为空对象的内存地址,从此webSite对象与o无关
o = new Object()
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
// 将引用类型值webSite传入changeObjProperty函数中
changeObjProperty(webSite);
console.log(webSite.siteUrl); // 百度
ECMAScript 中所有函数的参数都是按值传递的。
function changeObjProperty(o) {
// 这里的o相当于接收了一个内存地址
o.siteUrl = "http://www.baidu.com"
// 修改了对于内存地址的参数
o = new Object()
// 修改了0的内存地址指向
o.siteUrl = "http://www.google.com"
// 修改新地址的siteUrl
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);
https://juejin.cn/post/6844903929470271501
js 函数里是 按值传递 的
只不过 对象 传的是 指针的 拷贝 值
函数预编译
- 创建AO(Activation object)对象
- 找形参和变量声明,将变量和形参名作为AO属性名,值为underfind
- 将实参和形参统一
- 找函数声明,函数体作为值附值
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object()
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);
所以函数内部整个过程的大致过程如下
-
预编译阶段
var o = undefined //声明形参 o o = webSite // 形参和实参相统一, 此时变量o和变量webSite指向同一个地址,值为 new Object() 的返回值,即 {}
也就是说,函数的形参会在函数的内部声明同名变量,所以
o
和webSite
为两个不同的变量
-
执行阶段
o.siteUrl = "http://www.baidu.com" //变量o和变量webSite都指向同一个地址,所以他们的值都为 {siteUrl: "http://www.baidu.com"} o = new Object() // 变量o指向新的地址(o = {}),webSite指向仍然不变(webSite = {siteUrl: "http://www.baidu.com"}),所有后面对o的操作都不会影响webSite