JavaScript对象

176 阅读9分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

JS中的对象

前言:本来是想分享一下,我对原型以及原型链的一些认识或者说看法。但是我个人认为,引出原型的使用场景离不开对象,包括对象的继承模式中也会涉及到原型。所以这篇帖子先分享一些对象相关的非常简单的内容。算是水一水~

1.相关概念

在JS中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象。对象是由属性和方法组成的。 属性:事物的特征,在对象中用属性来表示
方法:事物的行为,在对象中用方法来表示

1.1 变量、属性、方法、函数之间的区别

在知道了对象是什么以及对象是由什么组成的之后,难免会把对象的属性和方法与变量和函数联系到一起。那他们之间的区别是什么?

  • 变量与属性相似,都是用来存数据的
        eg:
        var num = 10;
        var obj = {
            age: 10
        }

变量单独声明 使用的时候直接写变量名
属性是在对象中存在的,不需要声明,使用的时候是 对象.属性或者对象['属性名']

  • 函数和方法的相同点,都是实现某种功能、做某件事
    函数单独声明,使用时直接 函数名(); 是单独存在的
    方法在对象里面,调用的时候 对象.方法()

2.创建对象

一般用4种方式去创建对象,分别是对象字面量、Object构造函数、工厂模式、自定义构造函数

2.1对象字面量

什么是对象字面量?就是花括号{}里面包含了表达这个具体事务(对象)的属性和方法
示例

        // 1.利用字面量创建对象{}
        // var obj = {}; 创建了一个空对象
        var obj = {
            uname: '亚索', // 属性:值
            age: 32,
            sex: '男',
            sayHi: function () { //方法的写法就是 方法名:匿名函数
                console.log('哈撒ki!!');
            }
        }

(1) 对象中的属性和方法,采取键值对的形式。即 属性名:属性值、方法名:匿名函数
(2) 多个属性之间用逗号隔开,如上

调用方式

        // (1)调用对象的属性,采取 对象名.属性名
        console.log(obj.uname);
        // (2)调用属性还有一种方法,对象名['属性名']
        console.log(obj['age']);
        // (3)调用对象的方法  对象名.方法名()
        obj.sayHi();

对象字面量适应场景:起始时对象内部的数据是确定的
存在问题:如果创建多个对象会有代码重复

        var p = {
            name: 'Tom',
            age: 12,
            setName: function (name) {
                this.name = name;
            }
        }

        var p = { //创建多个对象代码重复
            name: 'Bom',
            age: 12,
            setName: function (name) {
                this.name = name;
            }
        }

2.2 Object构造函数模式

就是先创建空Object对象,再动态添加属性和方法
示例

        // 一个人:name:"Tom",age:12
        var p = new Object();
        // 向空对象追加属性和方法
        // 利用等号赋值的方法添加属性和方法,每个属性和方法之间用分号结束
        p.name = "Tom";
        p.age = 12;
        p.setName = function (name) {
            this.name = name;
        }
        
        注意:
        //属性名包含特殊字符,如 - 或者空格的时候,这样就追加不进去
        // p.content-type = 'ni hao';
        //这个时候就可以用['属性名']来追加
        p['content-type'] = 'hello world';
        console.log(p['content-type']); //'hello world'
        //或者属性名不确定的时候,也可以用['属性名']这种方法追加
        var propname = 'myAge';
        var value = 18;
        p.propname = value; //实际上追加进去的属性是 propname,而不是myAge
        p[propname] = value; //这样追加进去的,才是myAge

适用场景:起始时不确定对象内部数据
存在问题:语句太多

2.3 工厂模式

什么是工厂模式?就是通过工厂函数动态创建对象并返回。
什么又是工厂函数? 返回一个对象的函数都可以称为工厂函数

示例

        function createPerson(name, age) { //返回一个对象的函数都可以称为工厂函数
            var obj = {
                name: name,
                age: age,
                setName: function (name) {
                    this.name = name;
                }
            }
            return obj;
        }

        //创建两个人
        var p1 = createPerson('tom', 12);
        var p2 = createPerson('Bob', 12);
        //这种模式就解决上一种方式(字面量模式)代码大量重复的问题

        function createStudent(name, price) {
            var obj = {
                name: name,
                price: price
            }
            return obj;
        }

        var s1 = createStudent('李四', 12000);

        console.log(typeof p1, typeof p2, typeof s1);
        //这种模式的问题就是创建的对象没有具体的类型,都是object

适用场景:需要创建多个对象
存在问题:对象没有一个具体的类型,都是Object类型

2.4 自定义构造函数模式

我们为什么需要使用构造函数
就是因为我们前面两种方法一次只能创建一个对象,
一次创建一个对象,里面很多的属性和方法是大量重复的,只能复制
因此可以利用构造函数的方法 重复这些相同的代码 这个函数称为构造函数

示例

        // 构造函数的语法格式:
        // function 构造函数名() {
        //     this.属性 = 值;
        //     this.方法 = function () {};
        // }
        // 使用时:  new 构造函数名();

        function Star(uname, age, sex) {
            this.name = uname;
            this.age = age;
            this.sex = sex;
            this.sing = function (sang) {
                console.log(sang);
            }
        }
        var zxc = new Star('周星驰', 48, '男');
        console.log(typeof zxc); // object
        console.log(zxc.name);
        console.log(zxc['age']);
        console.log(zxc.sex);
        zxc.sing('唐伯虎'); //以这个例子来说实参传递给构造函数中方法中的形参sang

        var zxy = new Star('张学友', 19, '男');
        console.log(zxy.name);
        console.log(zxy.age);
        console.log(zxy['sex']);
        zxy.sing('李香兰');

特点:

  • 构造函数名字的首字母大写
  • 构造函数不需要return就能返回结果
  • 我们调用构造函数,必须使用new
  • 构造函数中的属性和方法前面必须添加this
    使用场景:需要创建多个类型确定的对象
    存在问题:每个对象都有相同的数据,浪费内存

3. new关键字执行过程

  1. new构造函数时可以在内存中创建了一个空的对象
  2. this 就会指向刚才创建的对象
  3. 然后执行构造函数里面的代码 给这个空对象添加属性和方法
  4. 返回这个对象(这也就是构造函数不需要return的原因,因为编译器读到new时,就会执行上面一系列操作,最后返回这个对象)

4. 遍历对象的方法

示例

        //遍历对象
        var obj = {
            uname: 'Guguuuu',
            age: 19,
            sex: '男',
            fn: function () {}
        }
        // 对象中是没有序号的,不能用普通的for来遍历
        // 利用for in 来遍历对象
        // for(变量 in 对象){}

        for (var key in obj) {
            console.log(key); //直接这样输出的是对象的属性名
            console.log(obj[key]); //这样才能输出属性值
        }

        // 注意:for in 中的变量通常我们喜欢写k或者key

5. 相关问题之什么是实例?

之前看弹幕上有人问到了,什么是实例? 书面语中,实例是实际的例子。
实例,就是一个类的真实对象(或者说一个具体的例子)。
比如我们都是“人”,你和我都是“人”类的实例了。
而实例化就是创建对象的过程,用New关键字创建。(创建对象的过程叫实例化)
在计算机语言中,“类”在实例化之后叫做一个“实例”。 “类”是静态的,不占进程内存,而“实例”拥有动态内存。在数据库中,代表一些程序的集合。如Oracle中,实例就是一些能支撑数据库运行的数据库程序。

        function Cake(color, material) {
                this.color = color;
                this.material = material;
            }

            var obj = {
                a: 1,
                b: 2
            };
            var cake1 = new Cake("red", "egg");
            var cake2 = new Cake("yellow", "apple");

JS 是基于原型的面向对象语言, 所有数据都可以当作对象处理

比如说蛋糕,Cake就是构造函数,可以理解为做蛋糕的模具。cake1 和cake2,这两个都是实例。
实例简单的理解就是成品。(就像java黑皮书里面讲的,苹果派和苹果派配方之间的关系)

实例和对象的区别,从定义上来讲:

  1. 实例是类的具象化产品。
  2. 而对象是一个具有多种属性的内容结构。
  3. 实例都是对象,而对象不全是实例。
  4. Js里面没有类(ES6以前)的语法,所以类的概念就通过创造一个对象来实现。

6. 自定义构造函数存在的问题 (引出原型)

还是刚刚那个对应的例子

        function Star(uname, age, sex) {
            this.name = uname;
            this.age = age;
            this.sex = sex;
            this.sing = function (sang) {
                console.log(sang);
            }
        }

我们创建了一个Star构造函数在这个Star构造函数中,为每一个对象都添加sing方法
这个方法是我们在构造函数内部创建的,也就是说构造函数每执行一次,就会创建一个新的sing方法,所有的实例的sing方法都是唯一的
可以通过console.log(对象1.方法 == 对象2.方法) (方法不加括号) 来证明是否是同一个方法

这就导致了,如果我创建10000个对象,就会创建10000个新的方法,而10000个方法实现的功能是一样的
这是完全没有必要的,完全可以使所有对象共享同一个方法
(就比如一个宿舍10人,给每一个人配一个厕所是非常 ** 的事情,应该是10人共享一个厕所)


解决方法:

        解决方法就是把这个 构造函数中这个方法在全局作用域中定义
         function fun(sang) {
                console.log(sang);
            }
         再把fun赋值给this.sing
         this.sing = fun;

这样即使构造函数执行了1000次,我的sing方法还是只有一个,就是这个fun

但是呢!这么写会有一个问题啊,就是把这个函数定义在全局作用域下,污染了全局作用域的命名空间
我这个函数叫fun,那别人就不能叫fun了,别人要是也叫fun,就把你这个函数覆盖掉了,就是说定义在全局作用域中是很不安全的


接下来就涉及到一个概念,叫原型(prototype)
我打算分开来讲,明天更新了再上链接~
传送入口

其他内容比如对象的继承模式我会在原型那边说明


以上,如果有讲错的地方,请指点指点,谢谢😄~