摘要:接着下一个例子赋予副本新的地址可见,函数参数传递的并不是变量的引用,而是变量拷贝的副本,当变量是原始类型时,这个副本就是值本身,当变量是引用类型是,这个副本是指向堆内存的地址。
转载自ConardLi: 《【JS进阶】 你真的掌握变量和类型了吗》 公众号: code秘密花园
1. JavaScript数据类型ECMAScript标准规定了7种数据类型,这些数据类型分为原始类型和对象类型
1.1 原始类型局有不可变性:
如下:
var str = "abc";
str.slice(1);
str.substr(1);
str.trim(1);
str.toLowerCase(1);
str[0] = 1;
console.log(str); //abc
以上这些方法都在原字符串的基础上产生一个新字符串,而非直接去改变str,这印证了字符串的不可变形。
当执行以下代码时:
str += "6";
console.log(str); //abc6
可见str的值被改变了,这貌似与字符串的不可变性不符合呀,这个要从内存上来理解。
在JavaScript中,每一个变量在内存中都需要一个空间来存储。
内存空间有分为栈内存和堆内存。
栈内存:
存储的值大小固定
空间较小
可以直接操作其保存的变量,运行效率高
由系统自动分配存储空间
JavaScript中的原始类型的值被直接存储在占中,在变量定义时,栈就为其分配好了内存空间。
由于栈中的内存空间的大小是固定的,所以存储在栈中的变量就是不可变的。
而当执行了 str += "6" 的操作时,实际上是在栈中又开辟了一块内存空间用于存储‘abc6’,然后将变量str指向这块空间。
1.2 引用类型
堆内存:
存储的值大小不定,可动态调整
空间较大,运行效率低
无法直接操作其内部存储,使用引用地址读取
通过代码进行分配空间
引用类型的值实际存储在堆内存中,它在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。
如下:
var obj1 = {name: "abc"};
var obj2 = {age: 18};
var obj3 = function() {...};
var obj4 = [1, 2, 3, 4, 5];
引用类型不具有不可变性,可以改变它们:
obj1.name = "abc6";
obj2.age = 19;
obj4.length = 0;
console.log(obj1); //{name: "abc6"}
console.log(obj2); //{age: 19}
console.log(obj4); //[]
以数组为例,它的很多方法都可以改变它自身:
pop()
push()
shift()
unshift()
reverse()
sort()
splice()
1.3 复制
原始类型的复制:
var name = "abc";
var name2 = name;
name2 = "code";
console.log(name); //abc
内存中有一个变量name,值为abc。从变量name复制出一个变量name2,此时在内存中创建了一个块新的空间用于存储abc,虽然两者的值是相同的,但是两者指向的内存空间完全不同,这两个变量参与任何操作都互不影响。
引用类型的复制:
var obj = {name: "abc"};
var obj2 = obj;
obj2.name = "code";
console.log(obj.name); //code
当复制引用类型的变量时,实际上复制的是堆中存储的地址,所以复制出来的obj2实际上和obj指向的堆中同一个对象。因此,改变其中任何一个变量的值,两一个变量都会受到影响。
1.4 比较
当对两个变量进行比较时,不同类型的变量的表现是不同的:
var name = "abc";
var name2 = "abc";
console.log(name === name2); //true
var obj = {name: "abc"};
var obj2 = {name: "abc"};
console.log(obj === obj2); //false
对于原始类型,比较时会直接比较它们的值,如果值相等,即返回true。
对于引用类型,比较时会比较它们的引用地址,虽然两个变量在堆中存储的对象具有属性值都是相等的,但是它们被存储在了不同的存储空间,因此比较值为false。
1.5 值传递和引用传递
如下:
let name = "abc";
function changeValue(name) {
name = "code";
}
changeValue(name);
console.log(name);
执行上面的代码,如果最终打印出来的name是‘abc’,没有改变,说明函数参数传递的是变量的值,即值传递。如果最终打印出来的是‘code’,函数内部的操作可以改变传入的变量,那么说明函数参数传递的是引用,即引用传递。
上面的例子执行结果是‘abc’,即函数参数仅仅是被传入变量复制给了的一个局部变量。改变这个局部变量不会对外部变量产生影响。
let obj = {name: "abc"};
function changeValue(obj) {
obj.name = "code";
}
changeValue(obj);
console.log(obj.name); //code
ECMAScript中的所有的函数的参数都是按值传递的。
当函数参数是引用类型时,同样将参数复制了一个副本到局部变量,只不过复制的这个副本是指向堆内存中的地址而已,在函数内部对对象的属性进行操作,实际上和外部变量指向堆内存中的值相同,但是这并不代表这引用传递。
接着下一个例子:
let obj = {};
function changeValue(obj) {
obj.name = "abc";
obj = {name: "code"}; //赋予副本新的地址
}
changeValue(obj);
console.log(obj.name); //abc
可见,函数参数传递的并不是变量的引用,而是变量拷贝的副本,当变量是原始类型时,这个副本就是值本身,当变量是引用类型是,这个副本是指向堆内存的地址。
重要的事情说三遍:
ECMAScript中的所有的函数的参数都是按值传递的。
ECMAScript中的所有的函数的参数都是按值传递的。
ECMAScript中的所有的函数的参数都是按值传递的。
转载自ConardLi: 《【JS进阶】 你真的掌握变量和类型了吗》 公众号: code秘密花园
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/104924.html
摘要:所以说在中,也并不是一切都是对象原始类型值不可变原始类型的变量的值是不可变的,只能给变量赋予新的值。可以理解基本类型的变量的值,就是字面上写的数值。有四个变量图调用是传参,内层的会屏蔽外层的。图内层的的值被改变成的值被改变为。 showImg(https://segmentfault.com/img/bVbldfK); 1. demo 如果你对下面的代码没有任何疑问就能自信的回答出输出...
摘要:引子前不久我建立的技术群里一位问了一个这样的问题,她贴出的代码如下所示执行结果如下所示第一个第二个这是一个令人诧异的结果,为什么第一个弹出框显示的是,而不是呢这种疑惑的原理我描述如下一个页面里直接定义在标签下的变量是全局变量即属于对象的变量 1) 引子 前不久我建立的技术群里一位MM问了一个这样的问题,她贴出的代码如下所示: var a = 1; function hehe...
摘要:一栈数据结构与不同,中并没有严格意义上区分栈内存与堆内存。引用数据类型的值是保存在堆内存中的对象。不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。为了更好的搞懂变量对象与堆内存,我们可以结合以下例子与图解进行理解。 showImg(https://segmentfault.com/img/remote/1460000009784102?w=1240&h=683); ...
摘要:要为变量或者常量指定类型也很简单,就是在变量常量名后面加个冒号,再指定类型即可,比如声明函数是类型,即返回值是类型声明参数是类型声明是无返回值的声明是这段代码演示了对函数类型参数类型和变量类型地声明。变量函数参数和返回值需要申明类型。 从 JavaScript 语法改写为 TypeScript 语法,有两个关键点,一点是类成员变量(Field)需要声明,另一点是要为各种东西(变量、参数...
摘要:对象数组初始化表达式,闯关记之上文档对象模型是针对和文档的一个。闯关记之数组数组是值的有序集合。数组是动态的,根闯关记之语法的语法大量借鉴了及其他类语言如和的语法。 《JavaScript 闯关记》之 DOM(下) Element 类型 除了 Document 类型之外,Element 类型就要算是 Web 编程中最常用的类型了。Element 类型用于表现 XML 或 HTML 元素...
阅读 3094·2021-08-03 14:05
阅读 2139·2019-08-29 15:35
阅读 677·2019-08-29 13:30
阅读 3168·2019-08-29 13:20
阅读 2529·2019-08-23 18:15
阅读 1795·2019-08-23 14:57
阅读 2212·2019-08-23 13:57
阅读 1308·2019-08-23 12:10