简单的栈 指针 引用
- 结合下案例的理解 对于
函数传参是传递对象指针的副本
的理解
对象&函数传参
- 函数传参是传递对象指针的副本
案例分析 模拟程序预解析和区分argument(引用实参),形参
案例分析(一)
function test(person) {
person.age = 26
person = {
name: 'yyy',
age: 30
}
return person
}
const p1 = {
name: 'yck',
age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
解释:函数传参是传递对象指针的副本
流程:
-js预解析 : 函数声明提升 声明常量变量提升 函数内部预解析
- p1=指向对象
{name:'yck', age: 25}
的指针 - test(p1)传指针,引用传递,函数内部顺序执行,
person.age = 26
修改该对象 - 代码继续执行 ,其中person为形参,初始为p1(指向第一个对象的指针),
- 函数内部
person = {}
将形参obj指向新的对象,即另一个对象指针 - return person指针与函数头部 ,故而p2为指向后者对象的指针
output console.log(p1) // { name: 'yck', age: 26 }
——— console.log(p2) // { name: 'yyy', age: 30 }
案例分析(二)
- 在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者用ECMAScript 的概念来说,就是arguments对象中的一个元素)
- 在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
流程:
- 预解析 提升声明 函数体
-
setName(person)
传递old
引用指针 - 函数内部顺序执行
old
引用指针指向的对象新添加 person.name = 'Nicholas' - 函数未执行完毕 alert在函数指向后 故而结果未定
- person形参指向新的函数内部的
new
对象,该对象添加person.name = 'Greg' -
函数执行完毕,内部的对象呗销毁
,则person依然指向old对象 - 即输出old对象的name属性
"Nicholas"
Point 实际上,当在函数内部重写obj 时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
js预解析 声明 变量提升 js特性 作用域
- 变量提升 声明提升
- 函数内部
在被调用时
即当js执行进入函数时
,函数内部声明过的所有变量会被提到最前,但同时对变量的赋值等操作不会被提升 - 函数内外有重名的变量时,局部变量会覆盖全局变量,原因是函数域优先于全局域
案例(二)增加
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
console.log(person.name)
console.log(obj.name)
console.log(person)
console.log(obj)
}
var person = new Object();
setName(person);
console.log(person.name); //"Nicholas"
流程:
- js预解析 变量、函数体的声明 提升(undefined) (赋值操作不会)
-
js执行进入函数时
,函数内部声明过的所有变量会被提到最前,但同时对变量的赋值等操作不会被提升 -
setName(person)
调用进入函数执行声明提升(undefined) 、赋值顺序不变
- entry person实参引用 obj形参接收指针副本指向person对于的对象
- entry(入口)
obj.name
引用改变 对象 { name: 'Nicholas' } -
obj = new Object();
赋值语句 不提升 注:形参obj接收引用传递的指针值,地址值(指针)的传递不会改变对象,只会改变与真实对象的连接和断开
连接的时候可以改变对象内部,即对引用类型的对象属性赋值。但对象本身无法改变,只能与之断开(即指针的重写)
- 即上述函数内部执行到
obj = new Object();
指针,地址值传递,重写,与原来的对象连接断开,建立新的连接 - 即此后的obj未断开连接之前均是obj本身的执行对象
- 输出如下
$ node test.js
Nicholas
Greg
{ name: 'Nicholas' }
{ name: 'Greg' }
Nicholas
IMPORTANT LINK
援引评论区 https://www.cnblogs.com/zareb/p/5699571.html
1.的确是指针,或者叫地址值。
2.引用类型作为参数传入函数,传的是个地址值,或者指针值,不是那个引用类型本身,它还好好的呆在堆内存呢。赋值给argument的同样是地址值或者指针。所以他说是value值传递一点没错,传的是个地址值。
3.的确如此。另外还有一步,形参变量obj它也被赋值了person指针。
4.对传入的引用类型进行属性赋值,此时就对内存中的真实对象产生作用了,改变了他的值。但不会改变自己本身。这个地址值的传递,结果是只会产生第一与堆内存真实对象的连接和断开两种情况。连接的时候可以操作对象内部,但对象本身无法被改变,诸如删除。只能与之断开。
5.创建一个新对象,跟上面的形参变量同名,即意味着形参变量被覆盖,在函数内部,有一个变量优先级的问题,this和arguments > 形参变量 > 函数声明变量 > 普通变量。只要不赋值,这个优先顺序就是起用的。一旦赋值,则以赋值为优先。因此这里出现了同名变量obj被断开了与person的联系,即值被改变成了新创建的对象的地址值,以前的对象不再可以被操纵,转而操纵的是新创建的对象。
提炼总结
- 函数传参对象 引用 地址值/指针传递
- 地址值的重写改变与原对象的连接和断开,不改变对象
- 对象的改变 1、建立连接 2、对引用类型的指针进行
属性赋值