// 创建一个对象,并将其赋值给名为 man 的变量 let man = { name: "xiaan" }; // 调用 human 的 set 方法,并传递两个参数(键和值)给它 human.set...当我们将 man 变量重新赋值为 null 时,内存中对原始对象的唯一引用是弱引用,它来自我们前面创建的 WeakMap。...通过引用或引用链从根中访问的值(例如,全局变量中的对象引用另一个对象,该对象也引用另一个对象——这些都被认为是可访问的值)。...如果我们通过赋值 null 来覆盖 person 的值: person = null; 那么对象将被垃圾回收,"xiaan" 值将无法再次访问。...然而,如果我们将person 设置为 null: person = null; 那么对象仍然在内存中,因为它可以通过 programmer 变量访问。简单地说,这就是垃圾收集的工作方式。
那么我们先来聊一下JavaScript的原始值(值类型)以及复杂值(引用类型),以及他们在内存空间中的存储,关于他们你可能不清楚的一些事: 我们先通过一个经典的面试题类型(并不是原题,我即兴发挥...事实上,原始值存储在栈内存中,按值来访问。复杂值(引用类型)在堆内存里面,按引用地址访问;然后我们会想到局部变量和全局变量在内存中的存储:下面是我在一个群中给一个同行的回答(前辈们莫见笑) ?...b、原始值的比较采用值比较 我们通过比较原始值来确定其值在字面上是否相同, 通过下面的代码来理解“值比较“的概念,并将它与复杂数字进行比较: ?...我相信我们已经理解:指向内存中复杂对象的变量,只有在引用相同对的‘地址’的情况下才是相等的,相反,两个单独创建的对象、即使具有相同的类型并拥有完全相同的属性,他们也是不相等的。...7、复杂对象具有动态属性 通过这一点,我们可以根据需求为复杂对象有任意多个引用。 ? 上述代码,objA、pointer1、pointer2都引用了内存中的同一对象, ?
引用类型理解:变量之间的互相赋值,只是指针的交换,而并非将对象(普通对象,函数对象,数组对象)复制一份给新的变量,对象依然还是只有一个,只是多了一个指引~~;例如: var a={x:1,y:2} //...需要开辟内存空间保存对象,变量 a 的值是一个地址,这个地址指向保存对象的空间; var b=a; // 将a 的指引地址赋值给 b,而并非复制一给对象且新开一块内存空间来保存; // 这个时候通过 a...来修改对象的属性,则通过 b 来查看属性时对象属性已经发生改变; 值类型(神秘的 NaN 值除外)将始终与具有相同值的另一个值类型的完全相等,如下: const first = "abc" + "def...不幸的是,与该对象交互的代码仍然可以访问其键为 symbol 的属性。 在调用代码尚不能访问 symbol 本身的情况下,这甚至是可能的。...假设我们为属性名的字符串版本使用某种名称空间/随机值,那么我们就消除了多个库意外发生名称冲突的风险。 但是,仍然有一个微小的区别。
如何处理 JavaScript 中的克隆对象JavaScript 处理对对象的赋值的方式与处理基本值的方式不同。它不是保存值,而是使用指向内存中值的指针。...这个概念被称为引用赋值,其中变量不存储实际值,而是存储指向对象内存位置的引用。这意味着如果两个变量指向同一个对象,对其中一个的任何修改都会影响另一个。...preserve the value', () => { expect(weather.today).toBe('')})❌ 失败,因为对象不是原始值,所以在这种情况下 JavaScript 使用引用赋值...复制策略根据原始对象和具体需求,可以在两种复制策略之间进行选择:浅拷贝浅拷贝创建一个新对象,只复制对象的顶层结构,而原始对象中的嵌套对象或元素仍然保持它们的引用。...,应保留值✅ 通过,应保留嵌套值⚠️ 注意:JSON.parse/JSON.stringify 方法有重要的限制:日期被转换为字符串无穷大和 NaN 被转换为 null对象属性中的 undefined、
(a(4) // 报错 以上代码先把 f() 函数保存在变量 a 中,然后将f变量设置为 null ,结果指向原始函数的引用只剩下一个。...当某个函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性([Scope])。然后,使用 this、arguments和其他命名参数的值来初始化函数的活动对象。...但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部活动对象处于第三位。直到作为作用域链重点的全局执行环境。 在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量。...无论什么时候函数在访问一个变量时,就会从作用域链中搜索具有相同名字的变量,函数执行完成后,局部活动对象将被销毁,内存中仅保存全局作用域。...使用闭包可以在JS中模仿块级作用域 创建并立即调用一个函数,这样即可以执行其中的代码,又不会在内存中留下对该函数的引用 结果就是函数内部的所有变量都会被立即销毁--除非将某些变量赋值给了包含作用域中的变量
基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象。 当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值。...obj1赋值给onj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2, 但是实际上他们共同指向了同一个堆内存对象。实际上改变的是堆内存对象。...c 复制变量时的不同 1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量, 此后这两个变量是完全独立的,他们只是拥有相同的value而已。...2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量, 也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。...2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!
即便把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。 ---- 闭包 不少开发人员总是搞不清匿名函数和闭包这两个概念。...当调用compare()函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。...显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。 无论什么时候在函数中访问一个变量时,都会从作用域链中搜索具有相应名字的变量。...; } 上面的代码中,通过把element.id的一个副本保存在一个变量中,并且在闭包中引用该变量消除了循环引用。...因此,有必要把element变量设置为null。这样就能够解除对DOM对象的引用,顺利地减少其引用数,确保正常回收其占用的内存。 ---- 模仿块级作用域 JavaScript中没有块级作用域的概念。
(这种说法不严密,当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象) 在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。...这5种基本类型是按值访问的,因此可以操作保存在变量中的实际的值。 动态的属性 复制变量的值:在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。...如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。...当从一个变量向另一个变量赋值引用类型值值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中,不同的是,这个值的副本实际上是个指针(可以理解为复制了地址值),而这个指针指向存储在堆中一个对象...复制操作结束后两个变量实际上将引用同一个对象。 传递参数:ECMAScript中所有函数的参数都是按值传递的,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
因此如果想要给一个变量赋值一个引用类型的值,那么则必须在堆内存中为这个值分配空间,由于这种值的大小不固定,因此不能把她保存到栈内存中,但是内存地址的大小是固定的,因此可以将内存地址保存在栈内存中。...1.3 动态属性 定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值。但是,当这个值保存到变量中以后,对不同类型值可以执行的操作则大相径庭。...改变n 不会 改变 n2 当一个变量向另一个变量复制引用类型的值时,同样也会将存储在栈中的值复制一份放到为新变量分配的空间中。...基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中。 从一个变量向另一个变量赋值基本类型的值,会创建这个值的一个副本。 包含引用类型值的变量实际上包含的并不是对象本身,而是指向该对象的指针。...JavaScript引擎目前都不再使用这种算法,但再IE中访问非原生JavaScript对象时,这种算法仍然可能会导致问题。 当代码中存在循环引用现象时,“引用计数”算法就会导致问题。
2、复制变量值 (1)基本类型 如果一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。此后,这2个变量可以参与任何操作而不会相互影响。...var num1 = 5; var num2 = num1; //5 (2)引用类型 当一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。...基本类型值和引用类型值具有以下特点: 1、基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中。 2、从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本。...5、从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同个对象。...Javascript引擎目前都不再使用这种算法;但在IE中访问非原生 Javascript对象(如DOM元素)时,这种算法仍然可能会导致问题。
但在下一行访问这个属性时,发现该属性不见了。这说明只能给引用类型值动态地添加属性,以便将来使用。 复制变量值 在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。...如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。此后,这两个变量可以参与任何操作而不会相互影响。...当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。...也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。...而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为 null 来释放其引用——这个做法叫做解除引用(dereferencing)。
如果一个操作数是数字,另一个操作数是对象,则先尝试把对象转换为数字。 如果两个操作数都是对象,则比较引用地址。如果引用地址相同,则相等;否则不等。 示例1 下面是特殊操作数的相等比较。...在全等运算中,应注意以下几个问题: 如果两个操作数都是简单的值,则只要值相等,类型相同,就全等。 如果一个操作数是简单的值,另一个操作数是复合型对象,则不全等。... undefined等于 true,所以表达式(a > b || a b)的返回值为 true,但是表达式 null >= undefined 的返回值为 false JavaScript赋值运算符详解...1 = 100; //返回错误 赋值运算有以下两种形式: 简单的赋值运算= :把等号右侧操作数的值直接复制给左侧的操作数,因此左侧操作数的值会发生变化。...示例2 在下面表达式中,逻辑与左侧的操作数是一个赋值表达式,右侧的操作数也是一个赋值表达式。但是左侧赋的值是一个简单值,右侧是把一个函数赋值给变量b。
(execution context)及相应的作用域链,并把作用域链赋值给一个特殊的内部属性(即[[Scope]])。...无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。...但是,闭包的情况又有所不同。 在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。...更为重要的是,createComparisonFunction() 函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。...但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期: function createFunctions() { var result = new Array(); for
当我们将存储原始类型数据的变量赋值给另一个变量时,其实是将变量存储的值复制了一份保存到了另一个变量中。...例如,下面的代码通过new操作符和创建一个Object对象的实例,并将实例保存到obj变量中: var obj = new Object(); obj.name = 'zhangsan'; 引用类型的数据并没有直接存储在变量的内存空间中...当我们将一个引用类型的变量赋值给另一个变量时,实际上将变量的中保存的地址拷贝了一份给了另一个变量,这时这两个变量都指向了同一个对象。...但是当我们不再使用某个引用类型的变量时,最好还是解除变量对实例的引用,这样有利于垃圾回收机制及时的进行回收,从而释放内存。解除引用最简单的方式就是,将变量赋值为null。...总结 JavaScript没有类,但是它有类型,分为原始类型和引用类型。 原始类型的值直接被保存在变量中,引用类型的值并不是直接保存变量中,变量中保存的仅仅是引用类型的值所在的内存地址。
原始类型(或叫值类型):数值、字符串、布尔、Null、Undefined 引用类型:对象 原始类型赋值给变量,遍历存储的是这个值本身,而你用类型赋值给变量,变量存储的是一个引用,这个引用会指向内存中的这个对象...二、原始类型与引用类型的差异 接下来,我们在实际案例中展示一下原始类型与引用类型的区别: 原始类型与引用类型赋值的区别 实例代码 1 var str1 = "hello world"; 2 var str2...,但是我们只是将obj1的name属性赋值为xiaohong,并没有将obj2的name属性赋值为xiaohong,为什么他们都变成了同一个值?...,比较的也是值,如果值相等,则返回true,如果值不等,则返回false,引用类型存的是应用,比较的也是引用,如果两个引用指向同一个对象,返回true,指向不同对象,则返回false,上面的例子中,两个对象虽然属性相同...student对象的克隆对象,这两个对象所有属性都相同,我们修改其中一个对象的时候不会影响另一个对象。
,只有引用值可以动态添加后面可以使用的属性 4.1.2 复制值 额… let num1 = 5; let num2 = num1; 两个变量独立使用,互不干扰 对于引用值而言,复制值得操作可以理解为复制了内存的引用...在按引用传递参数时,值在内存中的位置会被保存在局部变量中,对本地变量的修改会反映到函数外部 4.1.4 确定类型 采用typeof操作符用于判断一个变量是否为原始值。...,不能重新赋予引用值,但是可以改变引用值的属性 const o1 = {}; o1 = {}; // TypeError: 给常量赋值 const o2 = {}; o2.name = 'Jake'...4.3.2 引用计数 原理:跟踪每个值被引用的次数 流程: 声明一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1 同一个值又被赋值给另一个变量,这个引用类型值的引用次数加...1 当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1 当引用次数变成0时,说明没办法访问这个值了 当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存
,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。...在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(arguments对象中的一个元素),在向参数传递引用数据类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部...setName(person); console.log(person.name);//“Nicholas” 以上代码创建了一个对象保存在了变量person中,然后将对象传入setName()函数中之后就被复制给了...然后又将一个新对象赋值给了obj,同时将其name改为了“Greg”,如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为“Greg”的新对象,但是接下来访问的时候person.name...这里我的理解是函数的参数都是按值传递的,传Object类型也是一样,只不过这个值是地址值,也就是说函数的参数传递的都是栈空间中的值(值类型的值就是数据,引用类型的值就是地址)上面的代码我是这么理解的:
但是这不能表示 null 是一个对象。 因为第一代 JavaScript 引擎中的 JavaScript 值表示为32位的字符。最低3位作为一种标识,表示值是对象,整数,浮点数或者布尔值。...== null ) }复制代码 在原始值里面有一个特例,NaN 虽然是原始值,但是它和它本身是不相等的。...因此,如果链式使用这些运算符,会多次确认相同值的真假。这样的检查对于原始值类型成本不大,但是对于对象,如果能通过配置来转换布尔值,成本很大。...JS 中的 % 求余操作符并不是我们平时认为的取模。 -9%7 复制代码 求余操作符会返回一个和第一个操作数相同符号的结果。取模运算是和第二个操作数符号相同。...因此通过 shift 方法移除 arguments 对象中的元素之后,obj 仍然是 arguments[0] 的别名,method 仍然是 arguments[1] 的别名。
换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。因此,如果 JavaScript 具有动态作用域,理论上,下面代码中的 foo() 在执行时将会输出3。...但是 foo 此时并没有赋值(如果它是一个函数声明而不是函数表达式就会赋值)。foo()由于对 undefined 值进行函数调用而导致非法操作,因此抛出 TypeError 异常。...c: anotherArray, // 另一个引用!...复制出的新对象中 a 的值会复制旧对象中 a 的值,也就是 2,但是新对象中 b、c、d 三个属性其实只是三个引用。 深复制。除了复制 myObject 以外还会复制 anotherArray。...类的继承(委托)其实就是复制,但和其他语言中类的表现不同(其他语言类表现出来的都是复制行为),JavaScript 中的多态(在继承链中不同层次名称相同,但是功能不同的函数)并不表示子类和父类有关联,子类得到的只是父类的一份复本
领取专属 10元无门槛券
手把手带您无忧上云