对象
定义对象
<script>
var obj={a:1}
key:value
键 : 值
键只能是字符串或symbol
如果不加双引号,可以用纯数字或者纯字母,或者首字符是字母后面包含数字或者其他字符,但是不能使用非法字符
如果使用双引号,key可以使用任意字符定义
若果key使用[]定义,可以在[]中使用变量,把变量的值作为属性名
</script>
实例化
<script>
//实例化对象是在面向对象编程中创建一个对象的过程。
//使用已经定义的类来创建该类的具体实例,
//以便在程序中使用这些实例来表示和操作数据或实现特定行为。
//实例化不仅是在计算机内存中为新创建的对象分配空间的过程,而且使得对象能够访问类中定义的成员变量和方法。
var obj=new Object({a:1,b:2})
</script>
对象中分为两个部分,一个是属性,一个是方法
属性对应的是值,方法对应的是函数
var obj={
a:1,//对应值的叫属性
b:function(){}//对应函数的叫方法
}
对象的特征
1.有一个名称对应一个值,这种写法叫做键值对,键key,值value,键是唯一的,如果有相同的就会覆盖值
2.要求必须一个键对应一个值,不能仅有键或仅有值
3.键和值是一一对应关系,所以可以通过键直接拿到值(决定了再对象中查找一个值的速度最快,可以直接通过键获取到值)
4.再对象中的存储是非连续的,这个存储是松散型结构,遍历是无序的,不能做排序
5.因为对象的键是无序的 所以添加和删除都不需要考虑位置,直接插入就可以,对象插入,添加,删除速度最快
6.因为对象的键没有记录数量,因此对象是没有长度
对象的操作
-
对象的操作就是操作里面的数据,分为:增、删、改、查
-
操作对象上的数据有两种语法:点语法和数组关联语法
-
点语法
-
增: 对象名.键名 = 值
-
删: delete 对象名.键名
delete 关键词会同时删除属性的值和属性本身删除完成后,属性在被添加回来之前是无法使用的。
delete
操作符被设计用于对象属性。它对变量或函数没有影响。 -
改: 对象名.键名 = 值
-
查: 对象名.键名
数组关联语法
-
// 准备一个空对象
var obj = {}
console.log(obj)
// 1. 增加
obj['name'] = '安安'
obj['age'] = 18
obj['gender'] = '男'
console.log(obj) // { age: 18, name: '安安', gender: '男' }
// 2. 删除
delete obj['gender']
console.log(obj) // { age: 18, name: '安安' }
// 3. 修改
obj['name'] = '小安'
console.log(obj) // { age: 18, name: '小安' }
// 4. 查询
console.log(obj['name']) // '小安'
console.log(obj['age']) // 18
console.log(obj['gender']) // undefined
对象操作的区别
对于名字的操作
- 如果是符合变量命名规则和规范的键名, 两种语法都可以
- 纯数字只能使用数组关联语法, 不能使用点语法
- 带有特殊符号的, 只能使用数组关联语法, 不能使用点语法
// 准备一个对象
var obj = {
a1: 100,
$5: 200,
_346: 400,
1: true,
2: 'hello world',
'#abc': 33,
'font-size': '50px'
}
// 访问
console.log(obj.a1)
console.log(obj['a1'])
console.log(obj.$5)
console.log(obj['$5'])
console.log(obj._346)
console.log(obj['_346'])
console.log(obj[1])
console.log(obj.1) // 会报错
console.log(obj.#abc);//会报错
console.log(obj['#abc']);
console.log(obj['font-size'])
console.log(obj.font - size)
// 不行, 变成了 obj.font 和 size 进行减法运算
和变量相关的时候
- 点语法, 不管如何, 都不能和变量产生联系。始终都是访问的对象内某一个准确的键名
- 数组关联语法, 当你的 [] 内书写的是一个变量的时候,会把变量解析出来填充在 [] 内
// 和变量相关的访问
var obj = {
name: '唐僧',
age: 100,
1: true
}
var a = 'name'
// 使用点语法的时候
console.log(obj.name) // 访问 obj 内一个叫做 name 的键
console.log(obj.a) // 访问 obj 内一个叫做 a 的键, 和 a 变量没有任何关系
console.log(obj['a']) // 访问 obj 内一个叫做 a 的键, 和 a 变量没有任何关系
console.log(obj[a]) // a是一个变量
注意点
let a={
name:"aaa",
age:222
}
let b=a;
console.log(a[name])//不加引号是指一个变量
console.log(a["name"])//加引号是指键名
对象的key
对象的key只能是字符串类型和Symbol类型,如果不是这两种,则会隐式转换
<script>
var a="abc";
var obj={
a:1,//a是属性 不用于除数值以外其他以数值起头的字符串或者非法字符串包含(除字符,数值,下划线,$以外的字符)
"b1-a":2,//b是属性 用于全部任何字符串 可以包含非法字符 但是不能使用symbol作为key
["c"]:3,//c是属性 []中可以使用任意字符串,任意类型或变量(将把变量的值做为属性名)
[a]:4,//这里的a没有使用""就是变量,所以把变量的值做为key "abc",这个属性名就是abc
e:{
f:1
}
}
// 两种调用方法
// 如果对象已经定义好了,使用时
console.log(obj.a) //表示调用obj的a属性
console.log(obj.e.f) //调用f属性
// 点语法
console.log(obj["b1-a"]) //调用b1-a属性 加双引号就是字符串属性
console.log(obj[a]) //调用变量a属性 不加双引号就是变量
</script>
注意点
var obj={
a:function(){//方法
console.log("aa")
}
};
obj.a();
obj["a"]();//这个也会执行函数 两种写法是一致的
// obj.1=1 //错误的写法
//如果使用点语法,属性名的定义和获取都和变量名定义一致
// obj[1]=1; //正确写法
例子
var o={a:1};
var o1={b:2};
var obj={};
obj[o]=1;
console.log(obj)//{[object Object]: 1}
//这里obj[o1]中o1是key但是 不是string也不是symbol
//所以隐式转换为object object
//而{object object} : 1 所以其等于1
console.log(obj[o1]);//1
var obj={
"":1,
"2":3,
a:10
};
// console.log(obj)//{2: 3, "": 1, a: 10}
//会进行隐式转换
var arr=[2];
// console.log(arr)===2
//下式相当于console.log(obj["2"])===3
console.log(obj[arr]);//3
var arr1=["a"];
//["a"]===a
console.log(obj[arr1])//10
//[]===""
console.log(obj[[]])//1
引用地址
<script>
var str='{"a":1,"b":"abc"}' //JSON字符串
JSON.stringify()
将对象转换为JSON字符串 JSON字符串的要求,属性名必须使用""引起来
JSON.parse()
将JSON字符串转换为对象
通过JSON转换为字符后 symbol属性不能转换,方法不能转换,不可枚举的不能转换
将 JavaScript 对象转换为字符串,以便可以将其存储或传输
var obj={a:1,b:2,c:3};
//当让obj.a=10 时 浏览器会把值缓存 当打印输出时 显示的是缓存中的值
//通过方式一 obj.a 直接获取对象中的值 此时不会出错
console.log(obj.a)
obj.a=10;
console.log(obj)
//方式二 就是使用JSON.stringify()
//先把对象转换成json字符串
console.log(JSON.stringify(obj));
console.log(obj)
obj.a=10;
//再通过此把对象转换为字符串 就可以获取当时的值
console.log(JSON.stringify(obj));
console.log(obj)
</script>
JSON.parse()
通过JSON字符串转换为对象,产生一个新对象,如果字符串不是JSON 格式,就会报错
var str='{"a":1,"b":"abc"}' //JSON字符串
console.log(JSON.parse(str));//{a: 1, b: 'abc'}
var a="a";
console.log(JSON.parse(a))
//不是对象的字符串 不能通过JSON.parse()转换为对象
例子
<script>
var obj={a:1,b:2};
var o1=obj;
o1.a=10;
console.log(obj)//{a:10,b:2}
//o1 与 obj的引用地址是一样的 o1的值改变了 obj中的也会改变
var obj={a:1,b:{c:2}};
var o=obj.b;
o.c=10;
console.log(obj)//{a:1,b:{c:10}}
var obj={a:1}
var o1=obj;
obj={a:2};//此处给了obj新的地址
console.log(o1);//o不会发生改变 ,o与obj分别代表了不同的对象
function fn(o){
o.a=10;
}
var obj={a:1};
fn(obj); //此处把函数中的o换成了obj 也就是 obj.a=10
console.log(obj)//obj变化
function fn(o){
o={a:10};//此时o换了新地址
}
var obj={a:1};
fn(obj);
console.log(obj)//obj不变化
// o={a:10};此时o换了新地址
// o.a=10;没有换新地址 只是更改了引用地址里的值
</script>
对象中的this
<script>
//对象中的this 防止变量的引用地址发送变化
var a=20;
var obj={
a:1,//key对应的是值就是属性
b:this.a,//20
//这里的this指向当前对象外 上下文环境中this(属性中)
//原因:因为执行到这时,obj还没有执行完毕,无法赋值给obj,只能给window
c:function(){// key对应的是一个函数,叫做方法
//谁调用c,this就指向谁,this就是当前对象(方法中)
console.log(this.a);//1
console.log(obj.a);//10
}
}
var o=obj;
obj={a:10};//开辟了新的空间
// o.c();
console.log(obj)//{a: 10}
console.log(o);//{a: 1, b: 20, c: ƒ}
// 方法中的this指向当前对象(方法是执行时才调用)
// 属性中的this指向当前对象外 上下文环境中this(属性是一开始就赋值)
var a=0;
var o={
a:1,
b:function(){
var a=2;
console.log(this.a)
var o1={
b:this.a//this指向当前对象外 上下文环境中this 此时是a:1
}
arr.forEach(function(item){
console.log(this);
//回调函数中this自动指向window
},this)
// forEach的第二个参数就是将回调函数中this重新指向第二个参数
console.log(o1);
}
}
o.b();
// 第二个参数时thisArg,可以改变这些函数中回调函数里的this指向
// [].forEach();
// [].map();
// [].filter();
// [].find();
// [].findIndex();
// [].findLast();
// [].findLastIndex();
// [].some();
// [].every();
</script>
对象的内存泄漏
**原因:**堆里面存储的某些对象在引用关系更新之后,不被任何变量引用,这些内容滞留在内存中,就造成了内存泄漏.
清除方法
1.自动调用 浏览器再内存到达一定峰值的时候就会自动调用清除方法(将没有引用地址的清除掉)
2.手动调用(浏览器一般不让手动调用)
<script>
//{a:1,b:2}的地址分别赋给了 obj和o 但是下面又给obj和o 换了一个新地址 导致{a:1,b:2}不能被回收 也不能被引用 积攒的越来越多就会变成垃圾
var obj={a:1,b:2};
var o=obj;
// obj和o都指向的是同一个地址{a:1,b:2}
//给obj/o更换新地址
obj={a:10};
o={b:10};
// 所以{a:1,b:2}就泄漏了
</script>
null用于对象的清除
<script>
//垃圾回收
var obj={a:1,b:2};
obj=null;//让obj为null 就把{a:1,b:2}的引用列表清除了 {a:1,b:2}就会被清除
obj={a:10}//给obj新地址
var o1={a:1,b:2};
var o2=o1;//强引用关系
//此时若是清除o1 {a:1,b:2}并不会被清除,因为{a:1,b:2}的引用列表中有两个定义o1和o2 虽然o1为null但是o2还存在 只有当o2也为null时{a:1,b:2}才会被清除
o1=null;
o2=null;
o1={a:10}//赋予新地址
`var o={a:1,b:2};
var arr=[];//这里还引用着地址,还需要把数组第0项设置为null
arr.push(o);
o=null;//此时{a:1,b:2}也不会被清除`
</script>
typeof null 为什么是obj类型?
conslole.log(typeof null)//object
(null针对对象的引用类型设置的)
因为:在JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”
强引用
引用地址相同,一个对象身上存在着多个引用关系,只清除一个不会对另外一个造成影响
弱引用
将一个引用地址,存储在另一个地址中,清除时造成连锁反应,使得对象被清除
例子: o1存的是地址,o2存的是o1的地址,o1如果o1清除了引用关系o2也就清除了
对象的遍历
//判断是不是对象中的一个属性
var obj={a:1,b:2,c:3};
console.log("a" in obj);true//判断a是不是obj的一个(键)属性
遍历
注意:
-
对象没有办法使用 for 循环来进行遍历
- 因为 for 循环能提供的是一组有规律的数字
- 但是对象内存放的是键值对,键名是没有规律的
-
使用for in 来循环遍历
-
for in 的主要作用就是用来遍历对象数据类型的
-
语法:
-
for (var 变量名 in 对象名) {
// 重复执行的代码, 对象内有多少个成员, 就执行多少回
// 对象名[键名] 就是对象中的每一个成员的值
}
var obj = {
name: 'jack',
age: 18,
gender: '男',
}
for (var key in obj) {
//拿到所有的键名
console.log(key);
//拿到所有的键值
console.log(obj[key]);
}
补充
for...in循环主要用于遍历对象的属性,它返回属性名(字符串类型),适用于遍历对象的属性,而不适用于遍历数组(返回的是数组的索引值)。
for...of循环适用于遍历数组、字符串、Map、Set等可迭代对象,它返回的是元素的值,而不是索引。(不能直接遍历对象 是迭代器)
foreach 是 JavaScript 中 Array 的一个方法,它可以遍历数组并对数组中的每个元素执行指定操作。
//for in for of 遍历对象
const obj={
a:1,
b:2,
c:3
}
for(let i in obj){
console.log(i)//a b c
}
for(let i of obj){
console.log(i)//报错
console.log(obj[i])//正确
}
//for in for of 遍历数组
const arr=['a','b','c']
for(let i in arr){
console.log(i)//0 1 2
//打印的是索引值
}
for(let i of arr){
console.log(i)//a b c
//打印的是元素值
}
for in 遍历 补充
1、for in 无法遍历Symbol类型的key
2、 首先按照key转换为数值,如果纯数值的字符串,按照数字从小到大的顺序排列,在这里,小数点和-都是属于字符不算数值,然后再按照属性的添加顺序遍历
<script>
var obj={a:1,b:2,c:3};
//打印对象中所有的键
for(var key in obj){
console.log(key);//a b c
}
//打印对象中所有的值
for(var key in obj){
console.log(obj[key])//1 2 3
}
var obj={
a:1,
}
obj.c=3;
obj.b=2;
obj[12]=4;
obj[4]=5;
obj[1.2]=6
for(var key in obj){
console.log(key)
//排列顺序为:4 12 a b c 1.2
}
var obj={
a:1,
b:{
c:2,
d:{
e:3,
f:{
g:4
}
}
}
}
for(var key in obj){
console.log(key) //只能得到a b
}
//要使用递归才能遍历所有
</script>
浅拷贝
只修改第一层的话互不影响
<script>
//一层数据
var obj={a:1,b:2,c:3,d:4};
var o={};//此时obj和o分别对应一个地址 彼此之间没有引用关系
//浅复制 深复制需要用递归
for(var key in obj){
o[key]=obj[key] //通过循环 把obj中的值复制o中
}
obj.a=10;
console.log(o.a)//1
//两层数据
var obj={a:1,b:2,c:3,d:4,e:{f:10}};
var o={};
//浅复制
for(var key in obj){
o[key]=obj[key]
}
obj.a=10;
obj.e.f=100;
console.log(o.a) //只改变了外层的引用地址,所以把obj中a对应的值改成10之后 o中的a并不会发送改变
console.log(o.e.f)//这里o中的f会发送变化,因为是浅复制 只复制了外面的一层,并没有改变内存的引用地址
</script>
深复制
暂时只能用JSON实现(symbol,不可枚举的不能实现)
复制
Object.assign(目标对象,源对象1,源对象2,…)
将所有源对象中的值 赋值给目标对象,后面的会覆盖前面的,并且返回目标对象
<script>
var obj={a:1,b:2,c:3,d:4,e:{f:10}};
var o={a:10};
Object.assign(o,obj);//obj中所有值 赋值给o
obj.e.f=100;//修改obj中e:{f:10}的值
console.log(o);//{a: 1, b: 2, c: 3, d: 4, e: {f:100}
console.log(o.e.f)//100
</script>
删除属性
对象中不能删除不可删除属性 比如:arr.length
<script>
var obj={a:1,b:2,c:3};
//删除属性
delete obj.a;
delete obj["a"]; //两种形式一样
console.log(JSON.stringify(obj));
//{"b":2,"c":3}
//null 与 delete的区别
var obj={a:1,b:2,c:{d:10,e:20}};
obj.c=null; //{a:1,b:2,c:null}
delete obj.c; //{a:1,b:2}
</script>
数组如果使用delete删除元素,不会补位,不会造成长度减少,会打破紧密关系
var arr=[1,2,3,4];
delete arr[0];
console.log(arr)//[empty, 2, 3, 4]