2.02.25 对象及引用类型数据的深拷贝
1.对象
1.概念
JavaScript的基本数据类型包括数字、字符串、布尔值、null、和undefined值。其他所有的值都是对象(Object)。比如数组数据类型他就是一个对象。JavaScript中的对象是一个可变的键控集合。在JavaScript中数组是对象,函数是对象、正则表达式是对象,当然,对象自然也是对象。 对象是无序的数据的集合 对象是属性的容器,其中每个属性都拥有名字和值。对象适合用于汇集和管理数据。对象可以包含其他对象,所有可以容易的表示成树状或图形结构。
2.创建对象
对象字面量 创建对象
属性与属性值以键值对的形式存在,多个键值对用逗号隔开 属性的名字可以是包括空字符串在内的任意字符串。属性值可以是除undefined值以外任何值。 在对象字面量中,如果属性名是一个合法的JavaScript变量名(非保留字),则并不强制要求用引号括住属性名。属性的值可以是包含另一个对象字面量在内的任意表达式 对象是可以嵌套的 对象有两个相同的属性,后面的那个属性会覆盖前面那个属性 JSON对象很严格,其属性名的引号不能省略,最后的键值对不能带逗号 语法:
var empty_object = {};
var stooge = {
"first-name": 'Jackly', // 必须使用引号括住属性名因为first-name不是一个合法变量名
"last-name": 'Howard',
nickName: 'mike' // nickName是合法变量名,是否使用引号括住则是可选的
}
var flight = {
airline: 'Oceanic',
number: 815,
departure: {
time: '2022-09-22 14:55',
city: 'Sydney'
},
arrival: {
time: '2022-09-23 10:42',
city: 'Los Angeles'
}
}
使用构造函数创建对象(不推荐使用)
var 变量名 = new Object();
变量名.属性1 = 属性值1;
变量名.属性2 = 属性值2;
变量名.属性3 = 属性值3;
//不赋值的话,创建出来就是空对象:
var 变量名2 new Object();
console.log(变量名2) // {}
3.增删改查对象中的属性
查找对象中的属性
var car = {
make:"chery",
model:"bel air",
year:1957,
"color":"red",
passengers:2,
concertible:false,
mileage:1021
}
car.color //red
car["color"] //red
注意:如果你尝试检索一个并不存在的成员属性的值,将返回undefined
修改对象中的属性
car.color=black
car["color"]=black
如果对象之前没拥有那个属性名,那么该属性就会被扩展到对象中,就相当于增加对象的属性
增加对象中的属性
car.N2=true
car["N2]=true
删除对象中的属性
delete stooge.nickName
console.log(stooge.nickName) // undefined
注意:变量名提升
var phone = {};
var a;
console.log(phone); //{brand: '华为', model: 'p50', weight: '150g', color: 'blue mix green', year: 2021, …}
console.log(a); //undefind
phone.brand="华为";
phone.model="p50";
phone.weight="150g";
phone.color="blue mix green";
phone.year=2021;
phone.system="鸿蒙";
a=1;
phone.wifi="true";
phone.nfc="true";
phone.photo="true";
phone.music="true";
console.log(phone); //{brand: '华为', model: 'p50', weight: '150g', color: 'blue mix green', year: 2021, …}
console.log(a); // 1
4.遍历对象
使用for in 遍历 语法: for(var 属性 in 对象)
for(var i in obj){
console.log( i, obj[i] ); // obj["brand"]
}
注意 i 是带引号的属性名 所以只能使用obj[i]的方式获取属性 不能使用obj.i,而且obj.i表示的不是obj."属性名"这层意思,而是表示,obj里面的 i 的这个属性的意思,可知obj里面根本没有 i 这个属性 注意:for in 遍历时属性名的出现顺序时不确定的,因此要对任何可能出现的顺序有所准备。如果你想要确保属性以特定顺序出现,最好的办法就是完全避免使用for in语句,而是创建一个数组在其中以正确的顺序包含对象的属性名。使用for得到我们想要的属性。
var stooge = {
"first-name": 'Jackly',
"middle-name": 'Jafferson'
"last-name": 'Howard',
nickName: 'mike'
}
var properties = [
"first-name",
"middle-name",
"last-name",
"nickName"
]
for(var i = 0; i < properties.length; i++) {
var key = properties[i]
var value = stooge[key]
console.log(key + ' : ' + value)
}
5.对象的杂项
获取对象中所有的属性名
语法:object.keys(对象) => 返回一个属性名组成的数组
console.1og(Object.keys(car))
//['brand','model ','year ','color','核载人数','可折叠车棚','N2']
判断某个属性是否在对象中
语法:in运算 => “属性名” in 对象名 返回布尔值
var result = "color" in car;
console.log("result =>",result); // true color在对象中
var result = "N2" in car;
console. log("result =>",result); // false N2不在对象中
6.对象的方法
方法的概念:对象的属性是一个函数,这个函数就是方法 语法:
var fiat = {
make: "Fiat",
model: "500",
year: 1957,
color: "Medium Blue",
passengers: 2,
convertible: false,
mileage: 88000,
drive: function() {
alert.log("Zoom zoom!");
}
};
注意:方法名要先写,然后是冒号(😃,在写关键字function,再是参数列表,再是花括号
7.this的简单理解
var fiat = {
make: "Fiat",
model: "500",
year: 1957,
color: "Medium Blue",
passengers: 2,
convertible: false,
mileage: 88000,
started: false, // 存储发动机当前状态的属性(true表示发动机已启动,false表示发动机已熄灭)。
start: function() { // 启动发动机的方法,当前它只是将属性started设置为true。
started = true;
},
stop: function() { // 熄灭发动机的方法,它只是将属性started设置为false。
started = false;
},
// 在这里实现了有趣的行为:当你试图开动汽车时,如果发动机已启动,将显示Zoom zoom!,否则你将被告知要先启动发动机
drive: function() {
if (started) {
alert("Zoom zoom!");
} else {
alert("你需要先启动汽车发动机.");
}
}
};
运行:接下来我们调用上面 fiat 对象的方法试着驾驶这台汽车。
fiat.drive(); // 首先,我们尝试开动汽车,这将显示一条消息,让我们先启动发动机。
fiat.start(); // 然后,我们启动发动机,
fiat.drive(); // 并开动汽车
fiat.stop(); // 最后,我们熄灭发动机
然后我们发现:启动代码后我们会发现,在JavaScript控制台中会输出如下错误ReferenceError: started is not defined。其错误的原因就是。在fiat对象中的 drive、 start 、stop 方法中所引用的变量started会被解析成局部变量、形参、或全局变量,而started不属于上述任何一种变量,而是对象fiat的一个属性。这就导致我们创建的对象方法无法正确的访问到该属性。为了解决这一问题,有一个JavaScript关键字 this,可使用它来告诉JavaScript,访问当前所处的对象
var fiat = {
make: "Fiat",
model: "500",
year: 1957,
color: "Medium Blue",
passengers: 2,
convertible: false,
mileage: 88000,
started: false,
// 每次引用属性started时,都在它前面加上this. ,告诉Js你所指的是当前对象的属性started,以免Js以为你引用的是一个变量。
start: function() {
this.started = true;
},
stop: function() {
this.started = false;
},
drive: function() {
if (this.started) {
alert("Zoom zoom!");
} else {
alert("你需要先启动汽车发动机.");
}
}
};
//这样的代码就能正确运行了
你若不写成this,你也可以写成 把this.started写成fiat.started,代码一样可以正确跑起来
2.深拷贝
1. 区别数组变量与对象变量
调用数组API 只有数组才可以调用数组API 对象调用的话会返回undefined,这里的调用API可以不带(),让其打印函数体
var a=[],b={};
console.log("a =>a.push ); // fn
console.log("b =>b.push ); // undefined
Array. isArray()静态方法 判断是否是数组
console.1og("a =>", Array.isArray(a)); //true
console.1og("b =>", Array.isArray(b)); //false
instaceOf 判断一个对象是否是由另一个对象实例化出来的
console.1og( a instanceof Array); // true
console.log( b instanceof Array); // false
4.constructor 判断一个对象是谁实例化出来的
console.log(a.constructor); // Array()
console.log(b.constructor); // object()
2. 基本类型的拷贝
基本类型的拷贝就是深拷贝,修改其中一项不会改变另一项
3. 引用类型的深拷贝
1. 不用考虑函数的深拷贝
因为修改其中一项不会改变另一项 你修改其中一项时,其实是给修改的那一项重写,重新给修改项新地址
var fn1 = function(){};0x666
var fn2 = fn1; // 0x666
fn2 = function(){} // 0x888
console.log( function(){} == function(){} ); //false
2. 对象类型与数组类型的深拷贝
没有嵌套的引用类型的拷贝
数组 拷 数组:遍历数组的每一项,存储到一个新数组上 对象 拷 对象:遍历对象的每一项,存储到一个新对象上
嵌套的引用类型的拷贝
思路:我们知道,当数组嵌套数组的时候,我们要想深拷贝,可以使用嵌套循环去遍历数组,进行嵌套数组的深拷贝,那么我们就可以用这个思路去进行下面四种情况的深拷贝
数组 嵌套 数组 对象 嵌套 对象 对象 嵌套 数组 数组 嵌套 对象 上面这几种情况,或者更复杂,在里面再嵌套多层数组或对象,都可以使用下面这个深拷贝的函数:
function deepCopy(obj) {
// 如果是基本类型 或函数, 不用拷贝 直接返回
if(typeof obj != "object"){
return obj;
}
// 如果是引用类型
else{
// 根据参数决定要拷贝的数据类型
var newObj = Array.isArray(obj) ? [] : {};
// 遍历引用类型 函数自调用 直至都是基本类型 没有嵌套
for(var i in obj){
newObj[i] = deepCopy( obj[i] );
}
}
return newObj;
}
注意:就算对象里面有函数,上面这个函数也能给与拷贝,因为:
typeof 数组 // 控制台打印 object 这是一个字符串 typeof 对象 // 控制台打印 object 这是一个字符串 typeof 函数 // 控制台打印 function 这是一个字符串 当对象里面有函数时,他直接走if这条路,返回函数本身,虽然是函数的浅拷贝,但不影响(可以有上面可知函数不用考虑深拷贝)