# 认识对象

作者: 多叉树;转载须注明出处

# 概念

对象(Object) 是 “键值对” 的集合,表示属性和值的映射关系。

对象是一种复杂的数据类型,有了对象为了编写复杂的大型引用奠定了基础。

# 对象的定义

# 方式一

var xiaoLuo = { 
      name: '小小罗', 
      age: 32,
      sex: '男',
      hobbies:['足球'],
      'read-books': ['一个足球明星的自我修养']
}

上面的例子中我们就定义了一个对象(xiaoMing), 并且为该对象定义了四个属性, 性名(name)、 年龄(age)、 性别(sex)、爱好(hobbies)

属性是一种键值对的形式 key (属性名): value(属性值)

如果对象的属性键名不符合JS标识符命名规范,则这个键名必须用引号包裹, 例如上面的 read-books属性。 现实开发中,我们应该尽量避免不符合js命名规范的写法。

# 方式二

方式而分为两步

  1. 使用构造函数创建一个类
  2. 实例化类形成一个对象

// 构造函数创建一个类
function Player(name, age, sex, hobbies) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.hobbies = hobbies;
}

// 创建一个 对象 ,使用new关键字
var xiaoLuo = new Player('小罗', 32, '男', ['足球']);
console.log(xiaoLuo);
 

什么是类?

类表示一类事物, 通常比较抽象和宽泛。比如运动员是一类, 歌唱家是一类

什么是对象?

对象是具体, 比如: 足球运动员小罗

什么是构造函数?

构造函数也是函数,指的是专门用来声明类的函数,为了区分和普通函数的区别,增加可读性,通常首字母大写

类和对象的关系

Untitled

对比两种方式, 我们可以发现, 方式一是单例的, 我们直接初始化一个对象出来,我们希望单例的对象在内存中就只有一份

对于方式二, 是为了封装一个类事物, 根据特定的场景, 实例化特定的对象来表述不同对象之间的差异

# 对象属性访问

如果想访问一个对象,采用如下的语法

// 访问普通的属性
xiaoLuo.hobbies;
// 不符合命名规范的, 使用中括号包裹的形式
xiaoLuo['read-books'];

如果一个变量携带了某个对象的属性名, 也可以使用中括号的形式访问, 以中括号方位的形式很像操作属性, 属性名是下标索引, 通过它就可以得到属性值

如果属性名以变量形式存储,则必须使用方括号形式

var nameKey = 'name';
var name = xiaoLuo[nameKey];

// 下面这种方位方式不生效 , undei
xiaoLuo.nameKey;

# 添加/更新属性


// 对象已存在, 添加一个属性
xiaoLuo.shcool = '小小足球培训学校';
// 更改属性值
xiaoLuo.shcool = '小小足球培训学校分部';

# 删除某个对象属性


 // 删除某个school属性
delete xiaoLuo.shcool;

# 对象的方法

如果某个属性值是函数,则它也被称为对象的 “方法”

<script>
	var xiaoLuo = { 
	    name: '小小罗', 
	    age: 32,
	    sex: '男',
	    hobbies:['足球'],
	    'read-books': ['一个足球明星的自我修养'],
	    sayHello: function() {
	        console.log('大家好,我是小罗');
	    }
	}

	xiaoLuo.sayHello();
</script>

# 对象遍历

和数组变量相似, 对象也可以被遍历, 使用 for…in…语法循环

// key : 属性名
// obj 要遍历的对象
for(key in obj) {
	console.log('属性:' + k + '的值是' + obj[k]);
}

例如

<script>
	  var xiaoLuo = { 
	      name: '小小罗', 
	      age: 32,
	      sex: '男',
	      hobbies:['足球'],
	      'read-books': ['一个足球明星的自我修养'],
	      sayHello: function() {
	          console.log('大家好,我是小罗');
	      }
	  }
	
	  for(var key in xiaoLuo) {
	      console.log('key:', key, 'value:',  xiaoLuo[key])
	  }
</script>

# 克隆

我们都知道,对象属于引用类型值, 如果一个对象作为另外一个对象属性值的时候, 该属性名只能是持有该对象的地址。

Untitled

如上图, 小罗的妻子是小红, 在JS世界中, 我们给小罗定义了一个属性 wife, 将 其妻子小红的引用赋值给了小罗

也就说小红这个“对象”在计算机内存中只存在一份, 小罗对象持有的只是小红的引用

克隆: 就是拷贝,在内存中生成一个独立的副本

**引用类型值克隆:**对于引用类型值, 用等号并不能实现内存的拷贝,这里的等号只是将引用传递给了另外一个变量

var a = new Object();
a.name = '小红';
var b = a;
b.name = '小兰';

// 输出true
console.log(a === b);
// 输出 小兰, 改了 b的名字后 a的也随之改了
console.log(a.name);

上面的例子中, b 和 a 持有的是同一个对象的引用, a 和 b 是一个对象, 并没有实现克隆

针对引用类型值,有两种克隆方式

  1. 浅克隆
  2. 深克隆

# 浅克隆

值克隆该对象的属性, 如果该对象的属性依然是个对象或者数组, 不进行进一步处理

使用 for…in… 循环即可实现对象的浅克隆

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>浅克隆</title>
</head>
<body>
    
    <script>
        var obj1 = {
            a: 1,
            b: 2,
            c: [4,5,6]
        }

        var obj2 = {}

        for(var key in obj1) {
            obj2[key] = obj1[key];
        }

        obj1.a ++;
        obj1.c.push(7);
        
        // 从打印结果也能看出  浅克隆 并没有真正的将 数组c 分开,只是把引用传递给了 obj2.c
        // 值引用的的a 被分开了
        console.log(obj1 == obj2);
        console.log(obj2);
        console.log(obj1);

    </script>
</body>
</html>

# 深克隆

拷贝到底, 如果属性值是对象或者数组,则一直拷贝下去, 知道所有的属性值都在内存中有一个完整副本为止。

我们这里 使用递归来实现。

ps: 递归的概念: 定义一个函数,设定一个退出条件, 可以自己调用自己循环调用下去,知道触发了推出条件为止。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>深克隆</title>
</head>
<body>
    <script>

        var obj1 = {
            a: 1,
            b: 2,
            c: [4, 5, {
                d: 6,
                e: 7,
                f: [9, 10]
            }]
        };

        function deepClone(o) {
            if(Array.isArray(o)) {
                // 数组
                var result = [];
                for(var i = 0; i< o.length; i++) {
                    result.push(deepClone(o[i]));
                }
                return result;
            } 
            
            if(typeof o == 'object') {
                // 对象
                var result = {};
                for(var key in o) {
                    result[key] = deepClone(o[key]);
                }
                return result;
            }

            // 基本类型值
            var result = o;
            return result;
        }

        var obj2 = deepClone(obj1);

        obj1.c[2].d ++;

        console.log(obj1);
        console.log(obj2);

    </script>
</body>
</html>