JS06 - JavaScript 普通对象及其创建、遍历、堆栈、拷贝

179 阅读5分钟

什么是普通对象

  • JavaScript 普通对象是拥有一系列属性的引用数据类型,这些属性主要以键值对(键-属性名/值-属性值)的形式呈现
  • 类似生活场景,一辆汽车可以看做一个对象,其重量、颜色等是属性,汽车的启动、停止等动作可以叫做方法
  • 核心来说,普通对象就是一系列键值对的集合

创建普通对象

字面量创建

  • 明确给定要创建的键值对,如果是创建空普通对象,则只需要{}
//eg.1 创建一个空普通对象
var emptyObj = {}
emptyObj.name = "James"  // 空对象也可后期添加成员

//eg.2 创建一个非空普通对象
var conObj = {
    name:"James",
    age:20
}

构造函数创建

  • JavaScript 内置了 Object() 的内置函数,可以通过关键字 new 创建一个普通对象,但成员需要在创建之后添加
var personObj = new Object()
personObj.name = "James"
personObj.age = 30

键(key)- 注意事项

  • 任意设置:可以是字符串、数字、Symbol等基本类型,也可以是对象、函数等引用数据类型
  • [Symbol("age")]:如果是Symbol、对象、函数等作为key,需要加上[],否则报错
var carObj = {
    name:"Volvo",
    time:2018,
    "nation":"American",
    [Symbol("serial")]:1204613151,
    [function(){}]:"内置防伪",
    [{engineering:"volvo"}]:"发动机防伪",
}
for (const key in carObj) {
    if (Object.hasOwnProperty.call(carObj, key)) {
        const element = carObj[key];
        console.log(`key:${key} - valua:${carObj[key]}`)
    }
}
//key:name - valua:Volvo
//key:time - valua:2018
//key:nation - valua:American
//key:function(){} - valua:内置防伪
//key:[object Object] - valua:发动机防伪

基本操作 - CRUD

借助点语法 .

var personObj = new Object()
//Create
personObj.name = "James"
personObj.age = 30
//Read
document.write(personObj.name)
document.write(personObj.age)
//Update
personObj.name = "Charles"
personObj.nameNick = "Jane" //如果key不一样,就会变成sCreate
//Delete
personObj.name = null //假删除 --> key还存在,value为null(空)
delete personObj.name //真删除 --> key和value都不存在了

借助中括号 []

var personObj = new Object()
//Create
personObj["name"] = "James"   //使用[],必须要将name用引号,否则会将name当做全局变量查找,而不会作为对象key
personObj["age"] = 30
//Read
document.write(personObj["name"])
document.write(personObj["age"])
//Update
personObj["name"] = "Charles"
personObj["nameNick"] = "Jane" //如果key不一样,就会变成Create
//Delete
personObj["name"] = null //假删除 --> key还存在,value为null(空)
delete personObj["name"] //真删除 --> key和value都不存在了

两种寻址:点语法VS中括号

  • 如果是规范合理命名对象的key,这两种方法效果相同
  • 如果对象key命名剑走偏锋,使用点语法可能会导致代码运行错误,中括号引用能防止怪异属性名带来的错误
var homeObj = {
    $tag:"No.25",
    "#seconds":56,
    "a+b":"equal"
}
console.log(homeObj.$tag)           //No.25
console.log(homeObj["#seconds"])    //56
console.log(homeObj["a+b"])         //equal

console.log(homeObj.#seconds)
//SyntaxError: Private field '#seconds' must be declared in an enclosing class
console.log(homeObj.a+b)
//ReferenceError: b is not defined  这里把+当成为运算符

遍历操作 - for in

var personObj = new Object()
personObj["name"] = "James"
personObj["age"] = 30
personObj["post"] = "adviser"

for (const key in personObj) {
    console.log(`key:${key} - value:${personObj[key]}`)
}
// key:name - value:James
// key:age - value:30
// key:post - value:adviser

栈(Stack)与堆(Heap)存储

问题场景

  • 场景1:将一个对象赋值给另一个变量名,修改其中任意一个变量的属性,两者都被修改了
/* 场景1:将一个对象赋值给另一个变量名,修改其中任意一个变量的属性,两者都被修改了 */
var personObj = new Object()
personObj["name"] = "James"
personObj["age"] = 30
personObj["post"] = "adviser"
var personObjCop = personObj
//未修改
console.log(personObj)  //{name: "James", age: 30, post: "adviser"}
console.log(personObjCop)  //{name: "James", age: 30, post: "adviser"}
//修改personObj
personObj.name = "Charles"   
console.log(personObj)      //{name: "Charles", age: 30, post: "adviser"}
console.log(personObjCop)   //{name: "Charles", age: 30, post: "adviser"}
//修改personObjCop
personObjCop.age = 50
console.log(personObj)      //{name: "Charles", age: 50, post: "adviser"}
console.log(personObjCop)   //{name: "Charles", age: 50, post: "adviser"}
  • 场景2:声明两个字面量完全相同的普通对象,但进行等性比较的时候,依然是false
/* 场景2:声明两个字面量完全相同的普通对象,但进行等性比较的时候,依然是false */
var targetObj1 = {target:"money"}
var targetObj2 = {target:"money"}
//以下两者都是false
console.log(targetObj1 == targetObj2)
console.log(targetObj1 === targetObj2)
  • 场景3:将一个基本数据类型的值传给另一个变量名,修改任意一个,不会影响另外一个
/* 场景3:声明将一个基本数据类型的值传给另一个变量名,修改任意一个,不会影响另外一个 */
var param01 = "parameter"
var param02 = param01
//修改param01,没有影响到param02
param01 = "variable"
console.log(param02)    //parammeter

堆栈图解

栈内存 - 基本数据类型.png

堆内存 - 引用数据类型.png

栈和堆.png

拷贝对象

复制对象不能够存在修改其中一个对象,导致另外一个对象也被修改了,因此,不能单纯地赋值,因为对象是引用类型,栈内存放的只是堆中的地址值,需要将对象的元素逐个赋值到被复制对象的元素中

var objPrime = {
    name:"James",
    age:50
}
var objUltimate = new Object()

// var objUltimate = objPrime  //只是复制了地址值

//复制对象(遍历对象内的元素)
for (const key in objPrime) {
    if (Object.hasOwnProperty.call(objPrime, key)) {
        const element = objPrime[key];
        objUltimate[key] = element;
    }
}
console.log(objPrime,objUltimate)
//检测复制结果(检测是赋值的元素,还是只是复制的地址值)
objPrime["name"] = "Test"
if(objPrime["name"] == objUltimate["name"]){
    console.log("复制失败")
}else{
    console.log("复制成功")
}

浅拷贝

  • 含义:在拷贝对象的时候只能正确拷贝第一维的数据,如果是二维对象,仅能拷贝地址值
let objOri = {name:"James",age:20};
let objCopy = {};
//浅拷贝
function shallowCopy(objCopy, objOri){
    for(let key in objOri){
        objCopy[key] = objOri[key];
    }
    return objCopy;
}
console.log(shallowCopy(objCopy,objOri));   //{name: 'James', age: 20}
console.log(objOri.name = "Joker", objOri);    //Joker {name: 'Joker', age: 20} -> 拷贝成功
let objOri = { name: "James", age: 20, address: { home: "town", job: "county" } };
let objCopy = {};
//浅拷贝 -> 利用展开运算符
objCopy = { ...objOri };

objOri.name = "Joker";
objOri.address.home = "city";
console.log(objOri);
// {name: 'Joker', age: 20, address: {…}}
//     address: {home: 'city', job: 'county'}
//     age: 20
//     name: "James"
//     [[Prototype]]: Object
console.log(objCopy);
// {name: 'James', age: 20, address: {…}}
//     address: {home: 'city', job: 'county'} -> 因为浅拷贝,导致全被修改了
//     age: 20
//     name: "James"
//     [[Prototype]]: Object

深拷贝

  • 含义:在拷贝对象的时候能够通过递归,把多维对象的字段都拷贝到
let objOri = { name: "James", age: 20, hobby: ["篮球", "唱跳", "Rap"], location: { posfirst: "county", posecond: "home" } };
let objCopy = {};
//深拷贝
function deepCopy(objCopy, objOri) {
    for (let key in objOri) {
        // `?` 表示如果前面的为假,就不会走后面的`.`了
        if (objOri[key]?.toString() === "[object Object]") {
            objCopy[key] = {};
            deepCopy(objCopy[key], objOri[key]);
        } else if (Object.prototype.toString.call(objOri[key]) === "[object Array]") {
            objCopy[key] = [];
            deepCopy(objCopy[key], objOri[key]);
        } else {
            objCopy[key] = objOri[key];
        }
    }
    return objCopy;
}
deepCopy(objCopy, objOri);
console.log(objCopy);
// {name: 'James', age: 20, hobby: Array(3), location: {…}}
//     age: 20
//     hobby: (3) ['篮球', '唱跳', 'Rap']
//     location: {posfirst: 'county', posecond: 'home'}
//     name: "James"
//     [[Prototype]]: Object
console.log(objOri);
// {name: 'James', age: 20, hobby: Array(3), location: {…}}
//     age: 20
//     hobby: (3) ['篮球', '唱跳', 'Rap']
//     location: {posfirst: 'county', posecond: 'home'}
//     name: "James"
//     [[Prototype]]: Object
objOri.location.posfirst = "hometown";
(objOri.hobby)[0] = "足球";
console.log(objOri.location.posfirst, (objOri.hobby)[0]);
// hometown 足球
console.log(objCopy.location.posfirst, (objCopy.hobby)[0]);
// county 篮球 -> 赋值不受影响,拷贝成功
//深拷贝 -> JSON方法,但对value为undefined的key不能拷贝
objCopy = JSON.parse(JSON.stringify(objOri));