JavaScript对象 、堆与栈

  • 堆与栈
  • JavaScript 对象
    • 对象的分类
    • 创建一个对象
    • 对象属性
    • 访问对象属性
    • 修改对象的属性值;
    • 对象方法
    • 访问对象方法
    • 移除对象的属性
    • 检查属性是否存在
    • 计算属性
    • 保留字段可以用作属性名
    • for…in 循环遍历对象
    • 像对象一样排序
  • 引用复制
    • 比较引用
    • 常量对象
  • 复制和合并,Object.assign
  • 总结

堆与栈

栈:原始数据类型(Undefined,Null,Boolean,Number、String)
堆:引用数据类型(对象、数组和函数)两种类型的区别是: 区别:基础数据类型的数据存储在栈中,变量直接指向的是基础数据类型的值。引用数据类型的数据存储在堆中,变量指向的是引用数据类型的地址。比较:基本数据类型比较时,比较值。而引用数据类型比较时,比较内存地址,如果内存地址相同,指向了同一个对象,则相等,否则不相等。
原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其
在栈中的地址,取得地址后从堆中获得实体

JavaScript 对象

对象是指一个具体的事物。万物皆对象。

一个具体的事物一般都会有行为和特征。

对象可以通过花括号 {…} 和其中包含一些可选的属性来创建。属性是一个键值对,键是一个字符串(也叫做属性名),值可以是任何类型。

我们可以把对象想象成存放文件的橱柜。文件按照他们的名字来排列。这样根据文件名我们就很容易找到、添加或删除一个文件了。

对象的分类

  1. 内建对象

    由ES标准定义的对象,在任何ES实现中都可以使用 ,

    js提供的对象 Math String Number Boolean Function Object

  2. 宿主对象

    主要有浏览器提供的对象。

​ BOM DOM

  1. 自定义对象

开发人员自己定义的对象。

创建一个对象

我们可以用下面两种语法的任一种来创建一个空的对象(“空柜子”):

let user = new Object(); // “构造函数” 的语法
let user = {};  // “字面量” 的语法

通常,我们用花括号。这种方式我们叫做字面量

对象属性

可以说 “JavaScript 对象是变量的容器”。

但是,我们通常认为 “JavaScript 对象是键值对的容器”。

键值对通常写法为 name : value (键与值以冒号分割)。

键值对在 JavaScript 对象通常称为 对象属性

我们可以在创建的时候立即给对象一些属性,在 {...} 里面放置一些键值对。

let user = {     // 一个对象name: "John",  // 键 "name",值 "John"age: 30        // 键 "age",值 30
};

属性有键(或者也可以叫做名字,标识符),在冒号的前面 ":",值在冒号的右边。

user 对象中, 有两个属性:

  1. 第一个的键是 "name" 值是 "John"
  2. 第二个的键是 "age" 值是 30

是一个陈列着标记有两个 “name” 和 “age” 签名文件的橱柜。

访问对象属性

  1. 对象名.属性名
// 读取文件的属性:
alert( user.name ); // John
alert( user.age ); // 30
  1. 对象名[‘属性名’]
person["lastName"];

修改对象的属性值;

  1. 对象名.属性=值;
 man.age = 222;
  1. 对象名[‘属性名’]=值

    man['age'] = 222
    

对象方法

对象的方法定义了一个函数,并作为对象的属性存储。

对象方法通过添加 () 调用 (作为一个函数)。

你可以使用以下语法创建对象方法:

skill : function() {// 代码
}

访问对象方法

hero.skill();

移除对象的属性

移除一个属性,我们用 delete 操作:

delete user.age;

检查属性是否存在

console.log( key in obj ) //返回一个布尔值,key是属性名 obj对象名

计算属性

我们可以在对象字面量中使用方括号。这叫做计算属性

例如:

let fruit = prompt("Which fruit to buy?", "apple");let bag = {[fruit]: 5, // 属性名从 fruit 变量中计算
};alert( bag.apple ); // 5 如果 fruit="apple"

计算属性的含义很简单:[fruit] 含义是这个值从 fruit 变量中获取。

所以,如果一个人输入 "apple"bag 将是 {apple: 5}

本质上,这跟下面的语法相同:

let fruit = prompt("Which fruit to buy?", "apple");
let bag = {};// 从 fruit 变量中获取值
bag[fruit] = 5;

…但是看起来好多了。

我们在方括号中可以用更复杂的表达式:

let fruit = 'apple';
let bag = {[fruit + 'Computers']: 5 // bag.appleComputers = 5
};

方括号比点符号更强大。它允许任何属性名和变量,但写起来也更加麻烦。

大部分时间里,当属性名是已知且简单的时候,用点方法。如果有一些复杂的操作,那么就用方括号。

保留字段可以用作属性名

变量名不能用保留字段,像:“for”, “let”, “return” 等。

对于对象的属性,没有这些限制,都可以的:

let obj = {for: 1,let: 2,return: 3
}alert( obj.for + obj.let + obj.return );  // 6

基本上,什么都可以,只有一个特殊的:"__proto__" 因为历史原因要特别对待。比如,我们不能把它设置为非对象的值:

let obj = {};
obj.__proto__ = 5;
alert(obj.__proto__); // [object Object],这样不行

我们从代码中可以看出来,把它赋值成 5 被忽略了。

如果我们蓄意去存储随机的键值对或者允许一个访问者去指定键,那可能就会产生很多 bug 并且使对象变得危险。

比如,访问者可能选择 “proto” 作为键,这个赋值的逻辑就失败了(像上面那样)。

有一种让对象把 __proto__ 作为属性的方法,在后面章节会讲到,现在我们先来学习对象的更多知识。 还有另外一种数据结构 Map,我们会在后面章节学到,它支持任意的键值。

for…in 循环遍历对象

为了使用对象所有的属性,就可以利用 for..in 循环。这跟 for(;;) 是完全不一样的东西。

语法:

for(key in object) {// 各个属性键值的执行区
}

例如,我们列出 user 所有的属性值:

let user = {name: "John",age: 30,isAdmin: true
};for(let key in user) {// keysalert( key );  // name, age, isAdmin// 属性键的值alert( user[key] ); // John, 30, true
}

注意,所有的 “for” 都允许我们在循环中定义变量,像 let key 这样。

同样,我们可以用其他属性名来代替 key。例如 "for(let prop in obj)" 也很常用。

像对象一样排序

对象有顺序吗?换句话说,如果我们遍历一个对象,我们会按照赋值属性的顺序来获得属性吗?这靠谱吗?

简短的回答是:”有特别的顺序“:整数属性有顺序,其他是按照创建的顺序,细节如下:

例如,让我们考虑一个带有电话号码的对象:

let codes = {"49": "Germany","41": "Switzerland","44": "Great Britain",// ..,"1": "USA"
};for(let code in codes) {alert(code); // 1, 41, 44, 49
}

对象可用于向用户建议选项列表。如果我们的网站主要面向德国用户,可能想让 49 来当做第一个。

然而如果我们执行代码,会看到完全不同的景象:

  • USA (1) 在最前面
  • 然后是 Switzerland (41) 以及其它内容

因为这些电话号码是整数,所以它们以升序来排列。所以我们看到的是 1, 41, 44, 49

整数属性?那是什么?

这里的“整数属性”术语指的是一个字符串,可以在不改变的情况下对整数进行转换。

所以,“49” 是一个整数属性名,因为我们把它转换成整数,再转换回来,它还是一样。但是 “+49” 和 “1.2” 就不行了:

// Math.trunc 是内置的去除小数点的方法。
alert( String(Math.trunc(Number("49"))) ); // "49",同样,整数属性
alert( String(Math.trunc(Number("+49"))) ); // "49",不同于 "+49" ⇒ 不是整数属性
alert( String(Math.trunc(Number("1.2"))) ); // "1",不同于 "1.2" ⇒ 不是整数属性

…另外一边,如果属性名不是整数,那它们就按照创建时候的顺序来排序

let user = {name: "John",surname: "Smith"
};
user.age = 25; // 增加一个// 非整数属性是按照创建的顺序来排列的。
for (let prop in user) {alert( prop ); // name, surname, age
}

所以,这就解决了电话号码的问题,我们把整数属性转换成非整数的,在前面增加一个 "+" 就行了。

像这样:

let codes = {"+49": "Germany","+41": "Switzerland","+44": "Great Britain",// ..,"+1": "USA"
};for(let code in codes) {alert( +code ); // 49, 41, 44, 1
}

现在跟预想的一样了。

引用复制

对象和其他原始的类型相比有一个很重要的区别,对象都是按引用存储复制的。

原始类型是:字符串,数字,布尔类型 – 是被整个赋值的。

例如:

let message = "Hello!";
let phrase = message;

结果是我们得到了不同的值,每个存的都是 "Hello!"

对象跟这个不一样。

变量存储的不是对象本身,而是对象的“内存地址”,是对象的引用。

下面是对象的存储结构图:

let user = {name: "John"
};

在这里,对象存在内存里面。user 有一个对它的引用。

当对象被复制的时候 – 引用被复制了一份, 对象并没有被复制。

我们想象对象是一个抽屉,变量是一个钥匙,拷贝对象复制了钥匙,但是并没有复制抽屉本身。

例如:

let user = { name: "John" };let admin = user; // 复制引用

现在我们有了两个变量,但是都指向同一个对象:

我们可以用任何变量去获取抽屉内容,改变它的内容:

let user = { name: 'John' };let admin = user;admin.name = 'Pete'; //  改变 "admin" 的引用alert(user.name); // 'Pete', changes are seen from the "user" reference

上面的例子展示了只存在一个对象,就像我们的抽屉有两把钥匙,如果一个钥匙(admin)去使用了抽屉,稍后使用另外一个钥匙(user)打开的时候,就会看到有变化。

比较引用

等号 == 和严格等 === 对于对象来说没差别。

当两个引用指向同一个对象的时候他们相等。

例如,两个引用指向同一个对象,他们相等:

let a = {};
let b = a; // 复制引用alert( a == b ); // true,两个变量指向同一个对象
alert( a === b ); // true

如果是两个不同的属性,他们就不相等,即使都是空的。

let a = {};
let b = {}; // 两个独立的对象alert( a == b ); // false

如果比较两个对象 obj1 > obj2 或者用一个对象比较原始值 obj == 5,对象被转换成原始值。我们不久就会学习到对象的转化是如何实现的,但是事实上,上面的比较真的极少用到,要不就是你代码写错了。

常量对象

一个被 const 修饰的对象可以被修改。

例如:

const user = {name: "John"
};user.age = 25; // (*)alert(user.age); // 25

看起来好像 (*) 这行会报错,但是不是的,这完全没问题。这是因为 const 仅仅修饰 user。在这里 user 始终存储的都是同一个对象的引用。引用的地址没有变,只是引用的对象被修改了。

如果你想把 user 赋值给其他的什么,那就会报错了,例如:

const user = {name: "John"
};// 错误(不能再给 User 赋值)
user = {name: "Pete"
};

…那么我们应该怎么样创建不可变的对象属性呢?如果想让 user.age = 25 这样的赋值报错呢。这也是可以的,

复制和合并,Object.assign

复制一个对象的变量也等同于创建了此对象的另一个引用。

那么我们该怎么复制一个对象呢?创建一份独立的拷贝,一份复制?

如果我们真的想这么做,就需要创建一个新的对象,遍历现有对象的属性,在原始值的状态下复制给新的对象。

像这样:

let user = {name: "John",age: 30
};let clone = {}; // 新的空对象// 复制所有的属性值
for (let key in user) {clone[key] = user[key];
}// 现在复制是独立的复制了
clone.name = "Pete"; // 改变它的值alert( user.name ); // 原对象属性值不变

我们也可以用[Object.assign](javascript:if(confirm(‘https://developer.mozilla.org/zh/docs/Web/JavaScript/Reference/Global_Objects/Object/assign \n\n���ļ��޷��� Teleport Ultra ����, ��Ϊ ����һ�����·���ⲿ������Ϊ������ʼ��ַ�ĵ�ַ�� \n\n�����ڷ������ϴ���?’))window.location=‘https://developer.mozilla.org/zh/docs/Web/JavaScript/Reference/Global_Objects/Object/assign’) 来实现。

语法是:

Object.assign(dest[, src1, src2, src3...])
  • 参数 destsrc1, ..., srcN(可以有很多个)是对象。
  • 这个方法复制了 src1, ..., srcN 的所有对象到 dest。换句话说,从第二个参数开始,所有对象的属性都复制给了第一个参数对象,然后返回 dest

例如,我们可以用这个方法来把几个对象合并成一个:

let user = { name: "John" };let permissions1 = { canView: true };
let permissions2 = { canEdit: true };// 把 permissions1 和 permissions2 的所有属性都拷贝给 user
Object.assign(user, permissions1, permissions2);// 现在 user = { name: "John", canView: true, canEdit: true }

如果接收的对象(user)已经有了同样属性名的属性,前面的会被覆盖:

let user = { name: "John" };// 覆盖 name,增加 isAdmin
Object.assign(user, { name: "Pete", isAdmin: true });// 现在 user = { name: "Pete", isAdmin: true }

我们可以用 Object.assign 来代理简单的复制方法:

let user = {name: "John",age: 30
};let clone = Object.assign({}, user);

它复制了 user 对象所有的属性给了一个空对象,然后返回拷贝后的对象。事实上,这跟循环赋值一样,但是更短。

直到现在,我们是假设所有的 user 属性都是原始值,但是如果对象属性指向对象呢?

像这样:

let user = {name: "John",sizes: {height: 182,width: 50}
};alert( user.sizes.height ); // 182

现在,并不能拷贝 clone.sizes = user.sizes,因为 user.sizes 是一个对象,它按引用拷贝。所以 cloneuser 共享了一个对象。

像这样:

let user = {name: "John",sizes: {height: 182,width: 50}
};let clone = Object.assign({}, user);alert( user.sizes === clone.sizes ); // true,同一个对象// user 和 clone 共享 sizes 对象
user.sizes.width++;       // 在这里改变一个属性的值
alert(clone.sizes.width); // 51,在这里查看属性的值

为了解决上面的的问题,我们在复制的时候应该检查 user[key] 的每一个值,如果是一个对象,我们再复制一遍这个对象,这叫做深拷贝。

有一个标准的深拷贝算法,解决上面和一些更复杂的情况,叫做 [Structured cloning algorithm](javascript:if(confirm(‘https://w3c.github.io/html/infrastructure.html \n\n���ļ��޷��� Teleport Ultra ����, ��Ϊ ����һ�����·���ⲿ������Ϊ������ʼ��ַ�ĵ�ַ�� \n\n�����ڷ������ϴ���?’))window.location=‘https://w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm’)。为了不重复造轮子,我们使用它的一个 JS 实现的库 [lodash](javascript:if(confirm(‘https://lodash.com/ \n\n���ļ��޷��� Teleport Ultra ����, ��Ϊ ����һ�����·���ⲿ������Ϊ������ʼ��ַ�ĵ�ַ�� \n\n�����ڷ������ϴ���?’))window.location=‘https://lodash.com/’), 方法名叫做 [_.cloneDeep(obj)](javascript:if(confirm(‘https://lodash.com/docs \n\n���ļ��޷��� Teleport Ultra ����, ��Ϊ ����һ�����·���ⲿ������Ϊ������ʼ��ַ�ĵ�ַ�� \n\n�����ڷ������ϴ���?’))window.location=‘https://lodash.com/docs#cloneDeep’)。

总结

对象是具有一些特殊特性的关联数组。

他们存储键值对:

  • 属性的键必须是字符串或者符号(通常是字符串)。
  • 值可以是任何类型。

我们可以用下面的方法获取属性:

  • 点符号: obj.property
  • 方括号 obj["property"],方括号中可以使用变量 obj[varWithKey]

其他操作:

  • 删除属性:delete obj.prop
  • 检查属性是否存在:"key" in obj
  • 遍历对象:for(let key in obj) 循环。

对象根据引用来赋值或者复制。换句话说,变量存的不是对象的"值",而是值的 “引用”(内存地址)。 所以复制变量或者传递变量到方法中只是复制了对象的引用。 所有的引用操作(像增加,删除属性)都作用于同一个对象。

深拷贝的话我们可以使用 Object.assign 或者 [_.cloneDeep(obj)](javascript:if(confirm(‘https://lodash.com/docs \n\n���ļ��޷��� Teleport Ultra ����, ��Ϊ ����һ�����·���ⲿ������Ϊ������ʼ��ַ�ĵ�ַ�� \n\n�����ڷ������ϴ���?’))window.location=‘https://lodash.com/docs#cloneDeep’)。

我们在这一章学习的叫做“基本对象” — 对象。

JavaScript 中还有很多其他类型的对象:

  • Array 存储有序数据集合。
  • Date 存储时间日期。
  • Error 存储错误信息
    bj[“property”],方括号中可以使用变量obj[varWithKey]`。

其他操作:

  • 删除属性:delete obj.prop
  • 检查属性是否存在:"key" in obj
  • 遍历对象:for(let key in obj) 循环。

对象根据引用来赋值或者复制。换句话说,变量存的不是对象的"值",而是值的 “引用”(内存地址)。 所以复制变量或者传递变量到方法中只是复制了对象的引用。 所有的引用操作(像增加,删除属性)都作用于同一个对象。

深拷贝的话我们可以使用 Object.assign 或者 [_.cloneDeep(obj)](javascript:if(confirm(‘https://lodash.com/docs \n\n���ļ��޷��� Teleport Ultra ����, ��Ϊ ����һ�����·���ⲿ������Ϊ������ʼ��ַ�ĵ�ַ�� \n\n�����ڷ������ϴ���?’))window.location=‘https://lodash.com/docs#cloneDeep’)。

我们在这一章学习的叫做“基本对象” — 对象。

JavaScript 中还有很多其他类型的对象:

  • Array 存储有序数据集合。
  • Date 存储时间日期。
  • Error 存储错误信息
  • …等

JavaScript对象 、堆与栈相关推荐

  1. js学习笔记(对象~堆和栈)

    对象 为什么要用对象 让参数更少,代码更简洁 什么是对象 1.现实生活中:万物皆对象,对象是一个具体的事物,一个具体的事物就会有行为和特征.比如:一个人.一部手机 2.类和对象 (1)类:描述一类事物 ...

  2. JavaScript中堆与栈的区别

    在理解堆与栈这两个概念时,需要放到具体的场景下去理解.一般情况下有两层含义: (1)内存操作场景下,堆与栈表示两种内存的管理方式. (2)数据结构场景下,堆与栈表示两种常用的数据结构. 1.内存操作场 ...

  3. JavaScript 中堆和栈的区别

    JS变量都存放在内存中,而内存给变量开辟了两块区域,分别为栈区域和堆区域 栈像个容器,容量小速度快 堆像个房间,容量较大 讲这些之前我们先说说基本数据类型和引用数据类型 我们知道在js中的数据类型可以 ...

  4. JavaScript理解堆和栈

    博主在思否上面找到一篇讲的不错的文章, 记录一下 点这里跳转

  5. 吃人的那些 Java 名词:对象、引用、堆、栈

    作为一个有着 8 年 Java 编程经验的 IT 老兵,说起来很惭愧,我被 Java 当中的四五个名词一直困扰着:对象.引用.堆.栈.堆栈(栈可同堆栈,因此是四个名词,也是五个名词).每次我看到这几个 ...

  6. [转载]如何限制一个类对象只在栈(堆)上分配空间?

    一般情况下,编写一个类,是可以在栈或者堆分配空间.但有些时候,你想编写一个只能在栈或者只能在堆上面分配空间的类.这能不能实现呢?仔细想想,其实也是可以滴. 在C++中,类的对象建立分为两种,一种是静态 ...

  7. 限制对象在堆或栈中声明

    ***********************************************声明*************************************************** ...

  8. [Java_kaikeba]java中堆和栈的区别(对象变量的理解)

    .堆和栈都是java用来在RAM中存放数据的地方.与C++不同,java自动管理堆栈,       程序员不能直接设置堆栈. .区别       1.       .栈中存放基本数据类型变量(int. ...

  9. 8.JVM 关于对象分配在堆、栈、TLAB的理解

    引言 我们知道,一般在java程序中,new的对象是分配在堆空间中的,但是实际的情况是,大部分的new对象会进入堆空间中,而并非是全部的对象,还有另外两个地方可以存储new的对象,我们称之为栈上分配以 ...

  10. 详细讲解JavaScript中的堆和栈

    1.栈(stack)和堆(heap) stack为自动分配的内存空间,它由系统自动释放: heap则是动态分配的内存,大小不定也不会自动释放. 2.基本类型和引用类型 基本类型:存放在栈stack内存 ...

最新文章

  1. C++中的volatile关键字
  2. [转载]hadoop集群默认配置和常用配置
  3. 只能匹配第一列吗_VLOOKUP会用了吗?不会的抓紧看
  4. 04springMVC结构,mvc模式,spring-mvc流程,spring-mvc的第一个例子,三种handlerMapping,几种控制器,springmvc基于注解的开发,文件上传,拦截器,s
  5. 【SpringBoot】SpringBoot之Bean之自动加载
  6. Java基础学习总结(90)——Java单元测试技巧
  7. 多租户系统技术优越性及架构选型---springCloud工作笔记167
  8. sam卡和sim卡区别_SAM卡槽是不是和SIM卡槽是一个东西,有高人知道么?
  9. ubuntu 12.04 配置内核崩溃自动重启及转存
  10. 谷歌seo外链Backlinks研究工具推荐
  11. 低功耗视频解码芯片-TVP5150
  12. Oracle analytics server(OAS) 支持 mysql 社区版配置
  13. 无人驾驶真体验!老百姓都能打得到的“共享无人车”来了
  14. css3 移动端video视频全屏,横屏展示,适配微信/打包成app
  15. 三星Android手机进入工程模式
  16. 手撕自动驾驶算法——IMU测量模型、运动模型、误差模型
  17. PB实现BASE64加解密
  18. 华为内部批判:“过度高薪”养了一群闲人
  19. vlookup中lookup_value是公式可以吗?
  20. shell运行html文件路径,PowerShell文件系统(二)访问文件和目录

热门文章

  1. 全智通A+常见问题汇总解答—A+人脉关系中的新建权限取消掉后,保存权限,权限依然存在
  2. 英国和中国的时差是多
  3. 3dmax 2014加载panda3d插件失败
  4. Windows下获取本地IP地址的两种方法
  5. 未曾读过刘备的人,不足以谈人生
  6. 工信部颁发“免死金牌” 抢票软件继续存活
  7. Python爬虫-QQ音乐下载(详解)
  8. Genlovy_Hoo大神的杰作
  9. 阿里云:疫情期间全力保障教育平台“停课不停学”
  10. 历史类:古希腊与亚历山大帝国