zoukankan      html  css  js  c++  java
  • javascript中创建对象的方式及优缺点(一)

    1. 简单方式创建对象


      // 字面量方式创建对象
      var person1 = {
        name: "xyc",
        age: 23,
        sayHi: function() {
          console.log(name);
        }
      };
      
      // Object方式创建对象
      var person2 = new Object();
      person2.name = "lxy";
      person2.age = 18;
      person2.sayHi = function() {
        console.log(person2.name);
      }

    虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码,如上面的代码,每创建一个类似的person对象,就会重复上面的写法,代码较为冗余

    为了解决这个问题(代码重复),下面引入工厂模式 ==>

    2. 工厂模式创建对象


      function createPerson(name, age, job){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        o.sayName = function(){
          alert(this.name);
        };
        return o; 
      }
      var person1 = createPerson("Nicholas", 29, "Software Engineer");
      var person2 = createPerson("Greg", 27, "Doctor");

    通俗的解释:工厂模式就是利用了函数的封装调用,类比工厂材料==>成品的过程,完成入口参数==>对象的过程,函数可以无数次的生成,因此能够避免上面产生大量重复代码的情况。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)

    为解决这个问题(对象识别),下面引入构造函数模式

    3. 构造函数创建对象


    3.1 构造函数与普通函数的区别

    1. 命名规则:构造函数一般是首字母大写,普通函数遵照小驼峰式命名法
    2. 函数调用
      构造函数:
      (1)new fn( )
      (2)构造函数内部会创建一个新的对象,即f的实例
      (3)函数内部的this指向 新创建的f的实例
      (4)默认的返回值是f的实例
      普通函数:
      (1)fn( )
      (2)在调用函数的内部不会创建新的对象
      (3)函数内部的this指向调用函数的对象(如果没有对象调用,默认是window)
      (4)返回值由return语句决定
    3. 构造函数的返回值
      有一个默认的返回值,新创建的对象(实例),当手动添加返回值后(return语句):
      (1)返回值是基本数据类型-->真正的返回值还是那个新创建的对象(实例)
      (2)返回值是复杂数据类型(对象)-->真正的返回值是这个对象

      function foo() {
        var f2 = new foo2();
        console.log(f2);    // {a: 3}
        console.log(this);  // window
        return true;
      }
      function foo2() {
        console.log(this);  // foo2类型的对象 不是foo2函数
        return {a: 3};
      }
      var f1 = foo();
      console.log(f1);      // true

    3.2 new 操作符作用

    使用new操作符调用构造函数会经历下面几个步骤:
    (1)创建一个以这个函数为原型的空对象.
    (2)将函数的 prototype 赋值给对象的 proto 属性
    (3)将对象作为函数的 this 传进去。如果有 return 出来东西是对象的话就直接返回 return 的内容,没有的话就返回创建的这个对象

    function NewFunc(func){
      var ret = {};
      if (func.prototype !== null) {
        ret.__proto__ = func.prototype;
      }
      var ret1 = func.apply(ret, Array.prototype.slice.call(arguments, 1));
      if ((typeof ret1 === "object" || typeof ret1 === "function") && ret1 !== null) {
        return ret1;
      }
      return ret;
    }

    普通函数的作用主要是封装作用,能够在作用域内多处调用而已

    3.3 构造函数解决对象识别

    创建自定义的构造函数意味着可以通过 instanceof 将它的实例标识为一种特定的类型

      function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.sayHi = function(){
          console.log(this.name);
        }
      }
      
      var person1 = new Person('xyc', 23);
      var person2 = new Person('lxy', 22);
      
      console.log(person1 instanceof Person); //true
      console.log(person2 instanceof Person); //true
      console.log(person1 instanceof Object); //true 因为Person继承自Object,所以这里一样成立.

    3.4 缺陷

    构造函数创建对象的方式解决了代码重复对象识别的问题,但是创建的对象中含有方法时,每实例化一个Person,就会产生一个方法,也就是一个对象,每个对象分别占据内存。因此,构造函数创建对象的方式存在内存大量占用的风险

    利用原型共享的特性,下面引入原型模式

    4. 原型创建对象


      function Person(){}
      Person.prototype = {
        constructor: Person,
        name : "Nicholas",
        age : 29,
        job : "Software Engineer",
        friends : ["Shelby", "Court"],
        sayName : function () {
          alert(this.name);
        } 
      };
      
      var person1 = new Person();
      var person2 = new Person();
      
      person1.sayName();    //"Nicholas"
      person2.sayName();    //"Nicholas"
      
      person1.friends.push("Van");
      alert(person1.friends);    //"Shelby,Court,Van"
      alert(person2.friends);    //"Shelby,Court,Van"
      alert(person1.friends === person2.friends);  //true

    原型创建对象的方式将属性和方法都存在与原型中,也就是说,只要通过这种形式创建的对象都会共享这些属性和对象,相对于方法共享这是我们乐于看到的,但是属性共享让每个实例缺失了“个性”;另外对于引用类型的属性共享时,如上面的例子,多个实例对引用类型的操作会被篡改

    实例一般都要有属于自己的全部属性,这也决定了原型创建方式的局限性。下面引入非常经典的对象创建方式

    5. 构造函数+原型创建对象(重点)


    组合构造函数模式与原型模式:构造函数模式用于定义实力属性,而原型模式用于定义方法和共享的属性

      function Person(name, age, job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["Shelby", "Court"];
      }
      
      Person.prototype = {
        constructor : Person,
        sayName : function(){
          alert(this.name);
        }
      }
      
      var person1 = new Person("Nicholas", 29, "Software Engineer");
      var person2 = new Person("Greg", 27, "Doctor");
      
      person1.friends.push("Van");
      alert(person1.friends);    //"Shelby,Count,Van"
      alert(person2.friends);    //"Shelby,Count"
      alert(person1.friends === person2.friends); //false
      alert(person1.sayName === person2.sayName); //true

    这种方式创建的实例对象,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存;另外,这种方式还支持相构造函数传递参数,解决了上面的各种问题!

    6. Object.create(ES5) 

    在ES5之前,只能使用new来实现原型链集成。总而言之Object.create()和字面量对象应该替换new object()方法。
    Object.create()可以接收两个参数:提供原型的对象,可选属性对象(这个对象包含对新创建对象的配置)。

     
    var Car = {
        drive: function (miles) {
            return this.odometer += miles;
        }
    };
    var tesla = Object.create(Car, {
        'odometer': {
            value: 0,
            enumerable: true
         }     
    ));
    
    //输出10
    console.log(tesla.drive(10));

     总结

  • 相关阅读:
    聪明人 & 普通人
    13种模型及方法论
    面向大规模商业系统的数据库设计和实践
    分治算法
    软件架构
    隐含前提思维模型
    Git回滚代码到某个commit
    使用arthas排查 test 问题
    Arthas
    docker 操作入门
  • 原文地址:https://www.cnblogs.com/chenyablog/p/11282287.html
Copyright © 2011-2022 走看看