js--如何创建对象?

前言

  作为前端开发者,谈起如何创建一个对象,你是否是只能想到利用常见的字面量的方式创建对象?你是否想过new Object()创建对象的过程是什么?本文整理了一些常见的创建对象的方法。

正文

  创建对象的方法总结大概有以下几种:

  (1)对象字面量的方式;

 

      var Person = {}; //相当于var Person = new Object();
      var Person = {
        name: "Nike",
        age: 29,
      };

 

  对象字面量是对象定义的一种简单书写形式,目的在于简化创建包含大量属性的对象的过程。

  (2)工厂模式;

  工厂模式,常见的设计模式之一,创建对象过程不需要像客户暴露创建对象的具体逻辑,而是通过一个共同的接口来指向一个新常见的对象,换句话说就是用函数封装了创建对象的细节,从而通过调用函数达到了函数复用的目的,这种方式的缺点是无法识别对象的类型,无法建立对象和类型之间的联系。实例如下:

      function person(name, age) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.sayHello = function () {
          console.log("hello");
        };
        return o;
      }
    var per = new Person("xiaoming",20)
      console.log(per.name) // xiaoming
      console.log(per.sayHello()) // hello

  (3)构造函数模式;

  JavaScript 中每一个函数都可以作为构造函数,只要是通过new操作符创建的对象,我们就把这个函数称为构造函数,这种模式不需要显示的创建对象,而是直接将属性和方法赋值给 this ,没有 return 语句,注意常用规范需要将构造函数函数名首字母大写。实例如下:

 

      function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayHello = function () {
          console.log(this.name);
        };
      } 
      var person = new Person("xiaoming", 18);
      person.sayHello(); // xiaoming
      person.constructor == Person; // true
      person instanceof Object; //true
      person instanceof Person; //true 
     Peron("xiaohong", 18);//当作普通函数使用
       window.sayHello(); //xiaohong

 

  当作构造函数使用时候,即使用了 new 操作符的时候,当用new关键字调用时,会创建一块新的内存空间,标记为 person 实例,执行构造函数首先会创建一个对象,然后将对象的原型指向构造函数的  prototype  属性,然后将执行上下文中的  this  指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象。创建的对象有一个构造函数 constructor 属性。实质就是创建了一个 object 引用类型的实例,然后把实例保存在变量 person 中。因此使用instanceof检查类型的时候,即属于 object 类型,又属于 Person 类型。
  当上面的函数当作普通函数使用的时候,即不使用new操作符的时候,属性和方法添加到了 window 对象上,因此 window.sayHello() 输出“xiaohong”
  优缺点:构造函数创建的对象解决了上面工厂模式的缺点,使得对象和构造函数之间建立了联系,因此我们可以通过原型来识别对象的类型,但是这种模式造成了不必要的函数对象的创建,由于构造函数中定义一个函数就是定义一个对象,this.sayHello = new Function( console.log ( this.name ) ),这样就导致每一个构造生成的实例都回包含一个 Function 实例,而函数对象是所有实例都可以通用的,这样就导致了内存空间浪费的问题。

  (4)原型模式;

  因为每一个函数都有一个 prototype 属性,这个属性值为一个对象,这个对象包含了通过构造函数创建的所有实例能够共享的属性和方法,因此我们可以使用原型对象来添加公共的属性和方法,从而实现代码的复用。这种模式相对于构造函数来说,解决了构造的实例中函数对象复用的问题,但是这种模式也存在一定的问题,首先没有办法通过传入参数来初始化实例的属性,另外如果是一个引用数据类型的值,那么所有实例将共享一个对象,一个实例对引用数据类型的值做改变将会影响到别的实例对该类型的引用。实例如下:

        function Person(){}
        Person.prototype.name='xioming'
        Person.prototype.sayHello=function(){
            console.log(this.name)
        }
        var person=new Person()
        person.sayHello()//xiaoming
        console.log(Person.prototype.isPrototypeOf(person))//true 因为person对象内部有一个指向Person.prototype的指针,即person的原型指向Person

  (5)构造函数和原型组合模式;

  组合使用构造函数模式和原型模式是最常见的创建自定义对象的方法,其中构造函数用于创建实例的属性,原型模式用于创建共享的属性和方法。

 

     function Person(name, age) {
          this.name = name;
          this.age = age;
        }
        Person.prototype = {
          constructor: Person,
          sayName: function () {
            console.log(this.name);
          },
        };
        var p = new Person("xiaozhang", 123);
        console.log(p.sayName()); // xiaozhang

 

  组合使用两种方式,虽然结合了两种模式的优点,但是出现的问题是代码的封装性较差。

 

  (6)动态原型模式;

  动态原型模式是将原型方法赋值的创建过程移动到了构造函数内部,通过对属性是否存在的判断,可以实现仅在一次调用函数时对原型对象赋值一次的效果,这种方式很好地对上面的组合模式进行了封装。

 

        function Person(name, age) {
          this.name = name;
          this.age = age;
          if (typeof this.sayHello != "function") {
            Person.prototype.sayHello = function () {
              console.log("hello");
            };
          }
        }

  通过判断实现,在调用函数时对原型对象进行赋值操作,只有sayHello函数不存在时才会把方法放回原型中,初次调用构造函数的试试才会执行。

 

  (7)寄生构造函数模式;

  实例方法如下:假设我们呢需要创建一个具有额外方法的特殊数组:

        function SpecialArray() {
          var arr = new Array();
          //添加值
          Array.push.apply(arr, arguments);
          //添加方法
          arr.toPipedString = function () {
            return this.join("|");
          };
          return arr;
        }
        var color = new SpecialArray(
          "red",
          "blue"
        );
        console.log(color.toPipedString()); //red|blue

  在这个函数内部,首先创建了一个数组,然后push()方法(用构造函数接收到的所有参数)初始化了数组的值。随后,又给数组实例添加了一个toPipedString()方法,该方法返回以竖线分割的数组值。最后,将数组以函数值的形式返回。接着,我们调用了SpecialArray构造函数,向其中传入了用于初始化数组的值,此后调用了toPipedString()方法。但似乎返回的对象与构造函数或构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建 对象没有什么不同。为此,不能依赖instranceof操作符来确定对象类型。

总结

  以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。

  

 

 

posted @ 2021-03-26 14:21  zaisy'Blog  阅读(303)  评论(0编辑  收藏  举报