JS 基础知识点及常考面试题(一)2

简单的栈 指针 引用

  • 结合下案例的理解 对于 函数传参是传递对象指针的副本的理解

对象&函数传参

  • 函数传参是传递对象指针的副本
  • 案例分析 模拟程序预解析和区分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、对引用类型的指针进行属性赋值

你可能感兴趣的:(JS 基础知识点及常考面试题(一)2)