JavaScript 定义类

本文最后更新于:2019年8月11日 凌晨

在 JavaScript 定义类

场景

在一个新的项目时,需要在 JavaScript 中编写与后端对应的实体类时,因为不想使用下面的方法定义类了,感觉实在不够灵活…

var User = {
  //more field and function
}

或者

function User() {
  //more field and function
}

解决

目前 ES6 已经完全普及开来,所以如果需要定义类的话,请使用 ES6 class 关键字 定义类而非下面这几种方式(需要支持 IE 浏览器的读者可以继续往下看)。

所以去 Google 了一下,大致发现了三种方法:

  1. 构造函数法
    经典方法,基本上是 JavaScript 书籍上都会说的方法。使用构造函数模拟 class,在其内部使用 this 关键字指代实例对象。

    function User() {
      //注:这里所有的 property/function 都是对象级别(没错连方法都是。。。)。所以,toJSON() 方法在每个实例中都有一份,比较浪费内存,可以新建两 个 User 对象 user1,user2,然后使用 user1.toJSON === user2.toJSON 验证一下,你会发现为 false...(2333)
      this.name = 'rxliuli'
      this.toJSON = function() {
        return JSON.stringify(this)
      }
    }
    
    //想要定义所有对象都公用的 property/function,需要使用 Object.prototype 属性(原型),例如下面定义一个公用的 toJSON() 方法
    User.prototype.toJSON = function() {
      return JSON.stringify(this)
    }

    这种方法的缺陷很明显了,定义起来实在太过麻烦,使用 Object.prototype 让对象的定义实在很不直观。

  2. Object.create() 法
    为了解决 “构造函数法” 的缺点,更方便地生成对象,Javascript 的国际标准 ECMAScript 第五版(目前通行的是第五版),提出了一个新的方法 Object.create()。

    var User = {
      //注:这里所有的 property/function 都是继承得到的(类似于上面的 Object.prototype),所以没有改变的 property/function 只会有一份
      name: 'rxliuli',
      toJSON: function() {
        return JSON.stringify(this)
      },
    }

    调用的话直接使用 Object.create() 生成实例,并不需要使用 new

    var user = Object.create(User)
    user.name = '琉璃'
    console.log(user.toJSON())

    这种方法看起来简洁很多,但不能实现私有属性和私有方法,实例之间没有共享数据,对于模拟 class 而言并不是很好。

  3. 极简主义法
    据说是比 Object.create() 更好的方法,此方法不使用 thisproperty,代码部署起来也比较简单。

    • 封装
      js var User = { createNew: function () { var user = {}; user.name = 'rxliuli'; user.toJSON = function () { return JSON.stringify(this); }; return user; } }
      使用时调用 User.createNew() 即可。
      js var user = User.createNew(); user.name = '琉璃'; console.log(name);
      不知道你有没有发现,这里使用 createNew() 创建的对象的方法又是对象级别的了。但这种方法定义好处是,可以实现私有属性/私有方法,能够实现继承,能够在 class 之间共享数据,同时允许在构造对象时传入参数。

    • 继承
      通过第三种方法可以很容易实现,只要子类在创建对象时调用父类的 createNew() 方法即可!
      例如下面的 PersonStudent 类。

      var User = {
        createNew: function() {
          var user = {}
          user.name = 'rxliuli'
          user.toJSON = function() {
            return JSON.stringify(this)
          }
          return user
        },
      }
      
      var Student = {
        createNew: function() {
          var student = User.createNew()
          /*这里只定义 Student 特有的方法属性和方法即可*/
          student.grade = 1
          return student
        },
      }
    • 私有属性和私有方法

      createNew() 方法中,只要不是定义在 user 对象上的 property/function,就都是私有的。

      var User = {
          createNew: function () {
              var user = {};
              user.name = 'rxliuli';
              /*私有属性(最好不要问女人的年龄哦)*/
              var age = 17;
              /*这里的变量 age 对于外部而言就是无法读取的,只有通过 user 的公有方*/法 showAge() 读取
              user.showAge = function () {
                  alert(age);
              }
              user.toJSON = function () {
                  return JSON.stringify(this);
              };
              return user;
          }
      }
    • 数据共享
      有时候我们需要所有的实例对象都共享一项数据(其实就类似于静态变量/方法啦)。这种情况数据封装到类对象的里面,createNew() 方法的外面就好了。

      var User = {
        createNew: function() {
          var user = {}
          user.name = 'rxliuli'
          user.showAge = function() {
            alert(age)
          }
      
          return user
        },
        /**
         * 这个方法便是所有 User 对象公用的方法啦
         * @returns {string}
         */
        toJSON: function() {
          return JSON.stringify(this)
        },
      }

      然后生成两个实例对象,这两个实例对象的 toJSON() 方法是同一个,如果有个实例对象修改了,那其他所有的对象都会受到影响。

      var user = User.createNew()
      var user2 = User.createNew()
      console.log(user.toJSON === user2.toJSON)

      还记得上面提出的发现方法 3 的一个问题么?

      不知道你有没有发现,这里使用 createNew() 创建的对象的方法又是对象级别的了

    这里其实可以解决一部分的,当然,前提是你的数据不需要修改,然而需要修改的数据也不该使用共享数据了。


其实也可以直接不使用类来构建对象的,使用 {} 也能直接进行构建对象

var user = {
  name: 'rxliuli',
  //注:这里所有的 property/function 都是对象级别(没错连方法都是。。。)。所以,toJSON() 方法在每个实例中都有一份,比较浪费内存
  toJSON: function() {
    return JSON.stringify(this)
  },
}

好了,一个 user 对象就构建好了,可以立即使用了。

user.name = '琉璃'
console.log(name)

这种方法快速构建对象对于 function 参数而言很有帮助,然而同样缺陷极大,不适用于大量构造对象(因为每一次构建都要写这么多。。。),而且对象之间没有明显的关联。

那么,关于 JavaScript 定义类就到这里啦 (〜 ̄ ▽  ̄)〜