2.02.25对象及引用类型数据的深拷贝

2.02.25 对象及引用类型数据的深拷贝

1.对象

1.概念

  • JavaScript的基本数据类型包括数字、字符串、布尔值、null、和undefined值。其他所有的值都是对象(Object)。比如数组数据类型他就是一个对象。JavaScript中的对象是一个可变的键控集合。在JavaScript中数组是对象,函数是对象、正则表达式是对象,当然,对象自然也是对象。
  • 对象是无序的数据的集合
  • 对象是属性的容器,其中每个属性都拥有名字和值。对象适合用于汇集和管理数据。对象可以包含其他对象,所有可以容易的表示成树状或图形结构。

2.创建对象

  1. 对象字面量 创建对象
  • 属性与属性值以键值对的形式存在,多个键值对用逗号隔开
  • 属性的名字可以是包括空字符串在内的任意字符串。属性值可以是除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'
    }
}
  • 函数作为对象中的属性存在,这函数就称之为方法
  1. 使用构造函数创建对象(不推荐使用)
  • 语法:
var 变量名 = new Object();
变量名.属性1 = 属性值1;
变量名.属性2 = 属性值2;
变量名.属性3 = 属性值3;

//不赋值的话,创建出来就是空对象:
var 变量名2 new Object();
console.log(变量名2)    // {}

3.增删改查对象中的属性

  1. 查找对象中的属性
  • 语法:
    • 对象名[“属性名”]
    • 对象名.属性名
var car = {
    make:"chery",
    model:"bel air",
    year:1957,
    "color":"red",
    passengers:2,
    concertible:false,
    mileage:1021
}

car.color   //red
car["color"] //red
  • 注意:如果你尝试检索一个并不存在的成员属性的值,将返回undefined
  1. 修改对象中的属性
  • 语法:直接给对应的属性赋值
car.color=black   
car["color"]=black 
  • 如果对象之前没拥有那个属性名,那么该属性就会被扩展到对象中,就相当于增加对象的属性
  1. 增加对象中的属性
  • 语法:给不存在的属性赋值即可
car.N2=true
car["N2]=true
  1. 删除对象中的属性
  • 语法:使用delete运算符
delete stooge.nickName
console.log(stooge.nickName) // undefined
  1. 注意:变量名提升
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.对象的杂项

  1. 获取对象中所有的属性名
  • 语法:object.keys(对象) => 返回一个属性名组成的数组
console.1og(Object.keys(car))
//['brand','model ','year ','color','核载人数','可折叠车棚','N2']

  1. 判断某个属性是否在对象中
  • 语法: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.对象的方法

  1. 方法的概念:对象的属性是一个函数,这个函数就是方法
  2. 语法:
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. 区别数组变量与对象变量

  1. 调用数组API 只有数组才可以调用数组API 对象调用的话会返回undefined,这里的调用API可以不带(),让其打印函数体
var a=[],b={};
console.log("a =>a.push ); // fn
console.log("b =>b.push ); // undefined

  1. Array. isArray()静态方法 判断是否是数组
console.1og("a =>", Array.isArray(a)); //true
console.1og("b =>", Array.isArray(b));  //false
  1. instaceOf 判断一个对象是否是由另一个对象实例化出来的
console.1og( a instanceof Array); // true
console.log( b instanceof Array); // false
  1. 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. 对象类型与数组类型的深拷贝
  1. 没有嵌套的引用类型的拷贝
  • 数组 拷 数组:遍历数组的每一项,存储到一个新数组上
  • 对象 拷 对象:遍历对象的每一项,存储到一个新对象上
  1. 嵌套的引用类型的拷贝
  • 思路:我们知道,当数组嵌套数组的时候,我们要想深拷贝,可以使用嵌套循环去遍历数组,进行嵌套数组的深拷贝,那么我们就可以用这个思路去进行下面四种情况的深拷贝
  1. 数组 嵌套 数组
  2. 对象 嵌套 对象
  3. 对象 嵌套 数组
  4. 数组 嵌套 对象
  5. 上面这几种情况,或者更复杂,在里面再嵌套多层数组或对象,都可以使用下面这个深拷贝的函数:
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这条路,返回函数本身,虽然是函数的浅拷贝,但不影响(可以有上面可知函数不用考虑深拷贝)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值