【问题标题】:Which way is best for creating an object in JavaScript? Is `var` necessary before an object property?哪种方式最适合在 JavaScript 中创建对象?在对象属性之前是否需要`var`?
【发布时间】:2011-10-14 05:02:38
【问题描述】:

到目前为止,我看到了三种在 JavaScript 中创建对象的方法。哪种方式最适合创建对象?为什么?

我还看到,在所有这些示例中,关键字var 没有在属性之前使用 — 为什么? var是不是必须在属性名前声明属性是变量?

在第二种和第三种方式中,对象的名称是大写的,而在第一种方式中,对象的名称是小写的。我们应该为对象名称使用什么大小写?

第一种方式:

function person(fname, lname, age, eyecolor){
  this.firstname = fname;
  this.lastname = lname;
  this.age = age;
  this.eyecolor = eyecolor;
}

myFather = new person("John", "Doe", 50, "blue");
document.write(myFather.firstname + " is " + myFather.age + " years old.");

第二种方式:

var Robot = {
  metal: "Titanium",
  killAllHumans: function(){
    alert("Exterminate!");
  }
};

Robot.killAllHumans();

第三种方式 — JavaScript 对象使用数组语法:

var NewObject = {};

NewObject['property1'] = value;
NewObject['property2'] = value;
NewObject['method'] = function(){ /* function code here */ }

【问题讨论】:

  • “var”的使用取决于变量的范围,它定义了全局与否,搜索它,你会看到区别。
  • 如果您创建杀人机器人,请始终使用var。省略它会使它们成为全球性的
  • "var 的使用取决于变量的范围" -- 这是不好的做法 -- 无论你在什么范围内都应该使用它
  • 方法呢:Object.create()?
  • 如果“因为它提到属性是变量” 得到澄清会很好。这是谁”?这是在哪里提到的?你能引用一个具体的报价吗?

标签: javascript object


【解决方案1】:

没有最好的方法,这取决于你的用例。

  • 如果您想创建多个相似的对象,请使用 方式 1。在您的示例中,Person(名称应以大写字母开头)称为 构造函数。这类似于其他 OO 语言中的
  • 如果您只需要一个对象(如单例),请使用方式 2。如果您希望此对象继承自另一个对象,则必须使用构造函数。
  • 如果您想根据对象的其他属性初始化对象的属性,或者如果您有动态属性名称,请使用 方式 3

更新:作为第三种方式的示例。

相关属性:

以下内容不像this 那样引用book。无法使用对象字面量中其他属性的值来初始化属性:

var book = {
    price: somePrice * discount,
    pages: 500,
    pricePerPage: this.price / this.pages
};

相反,您可以这样做:

var book = {
    price: somePrice * discount,
    pages: 500
};
book.pricePerPage = book.price / book.pages;
// or book['pricePerPage'] = book.price / book.pages;

动态属性名称:

如果属性名称存储在某个变量中或通过某个表达式创建,则必须使用括号表示法:

var name = 'propertyName';

// the property will be `name`, not `propertyName`
var obj = {
    name: 42
}; 

// same here
obj.name = 42;

// this works, it will set `propertyName`
obj[name] = 42;

【讨论】:

  • 感谢您的回答......现在我明白了您的第一点,如果我们想要这样的东西,我们可以使用 way1 myFather=new person("John","Doe",50,"blue") ; myMother=new person("gazy","Doe",45,"brown"); myBrother=new person("poll","Doe",15,"blue");
  • 我想你的意思是 obj[name] = 42。对吧?
  • 我想指出选项 2 和 3 几乎相同,只是您在创建对象后分配属性。这就是所谓的 literal notation,因为您使用 object literal 来创建对象。在引擎盖下,这实际上调用了“new Object()”。你可以在这里阅读更多信息:developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…
  • 对于第二种情况,如果我们使用扩展运算符...从另一个对象继承是否有意义?
【解决方案2】:

定义函数有多种方式。它完全基于您的要求。以下是几种样式:-

  1. 对象构造器
  2. 文字构造函数
  3. 基于函数
  4. 基于原型
  5. 基于函数和原型
  6. 基于单例

示例:

  1. 对象构造函数
var person = new Object();

person.name = "Anand",
person.getName = function(){
  return this.name ; 
};
  1. 文字构造函数
var person = { 
  name : "Anand",
  getName : function (){
   return this.name
  } 
} 
  1. 函数构造函数
function Person(name){
  this.name = name
  this.getName = function(){
    return this.name
  } 
} 
  1. 原型
function Person(){};

Person.prototype.name = "Anand";
  1. 功能/原型组合
function Person(name){
  this.name = name;
} 
Person.prototype.getName = function(){
  return this.name
} 
  1. 单例
var person = new function(){
  this.name = "Anand"
} 

如果你有任何困惑,你可以在控制台上试试。

【讨论】:

  • 嘿@Alex_Nabu - 我已经在我的帖子中分享了这些示例。如果您仍然面临任何挑战,请更新我,我会帮助您。
  • 构建每个示例最终产生完全相同的var person 实例不是更有意义吗?例如在函数构造函数中,您可以简单地添加var person = new Person("Anand")。看似随机的分号用法是怎么回事? :P
  • 它会增加解释每种方式的优缺点的价值。
【解决方案3】:

没有创建对象的“最佳方式”。每种方式都有好处,具体取决于您的用例。

构造函数模式(与new 运算符配对以调用它的函数)提供了使用原型继承的可能性,而其他方式则没有。所以如果你想要原型继承,那么构造函数是个不错的选择。

但是,如果你想要原型继承,你不妨使用Object.create,这样继承更明显。

如果您碰巧在创建时手头有您希望设置的所有属性,那么创建一个对象字面量(例如:var obj = {foo: "bar"};)非常有用。

对于稍后设置属性,如果您知道属性名称,NewObject.property1 语法通常比NewObject['property1'] 更可取。但后者在您实际上没有提前知道属性名称时很有用(例如:NewObject[someStringVar])。

希望这会有所帮助!

【讨论】:

    【解决方案4】:

    我想这取决于你想要什么。对于简单的对象,我想您可以使用第二种方法。当您的对象变大并且您计划使用类似的对象时,我想第一种方法会更好。这样您也可以使用原型对其进行扩展。

    例子:

    function Circle(radius) {
        this.radius = radius;
    }
    Circle.prototype.getCircumference = function() {
        return Math.PI * 2 * this.radius;
    };
    Circle.prototype.getArea = function() {
        return Math.PI * this.radius * this.radius;
    }
    

    我不是第三种方法的忠实拥护者,但它对于动态编辑属性确实很有用,例如var foo='bar'; var bar = someObject[foo];

    【讨论】:

      【解决方案5】:

      有很多方法可以在 JavaScript 中创建对象。 使用构造函数创建对象或对象字面量表示法在 JavaScript 中使用 alot。同样创建 Object 的实例,然后为其添加属性和方法,在 JavaScript 中创建对象的常用方法有 3 种。

      构造函数

      有内置的构造函数,我们都可以不时使用它们,例如 Date()、Number()、Boolean() 等,所有构造函数都以大写字母开头,同时我们可以创建自定义构造函数JavaScript 中的函数如下:

      function Box (Width, Height, fill) {  
        this.width = Width;  // The width of the box 
        this.height = Height;  // The height of the box 
        this.fill = true;  // Is it filled or not?
      }  
      

      您可以调用它,只需使用 new() 来创建构造函数的新实例,创建类似下面的内容并使用填充参数调用构造函数:

      var newBox = new Box(8, 12, true);  
      

      对象字面量

      在 JavaScript 中使用对象字面量创建对象非常常用,这是一个创建简单对象的示例,您可以将任何内容分配给对象属性,只要它们被定义:

      var person = { 
          name: "Alireza",
          surname: "Dezfoolian"
          nose: 1,  
          feet: 2,  
          hands: 2,
          cash: null
      };  
      

      原型设计

      创建 Object 后,您可以为它制作更多成员的原型,例如为我们的 Box 添加颜色,我们可以这样做:

      Box.prototype.colour = 'red';
      

      【讨论】:

        【解决方案6】:

        虽然这里的许多人说创建对象没有最好的方法,但截至 2019 年,为什么在 JavaScript 中有这么多创建对象的方法是有道理的,这与 JavaScript 的进步有关可追溯到 1997 年的 EcmaScript 版本的不同迭代。

        在 ECMAScript 5 之前,只有两种创建对象的方法:构造函数或文字符号(new Object() 的更好替代方法)。使用构造函数表示法,您可以创建一个可以实例化为多个实例的对象(使用 new 关键字),而文字表示法提供单个对象,例如单例。

        // constructor function
        function Person() {};
        
        // literal notation
        var Person = {};
        

        不管你使用什么方法,JavaScript 对象只是键值对的属性:

        // Method 1: dot notation
        obj.firstName = 'Bob';
        
        // Method 2: bracket notation. With bracket notation, you can use invalid characters for a javascript identifier.
        obj['lastName'] = 'Smith';
        
        // Method 3: Object.defineProperty
        Object.defineProperty(obj, 'firstName', {
            value: 'Bob',
            writable: true,
            configurable: true,
            enumerable: false
        })
        
        // Method 4: Object.defineProperties
        Object.defineProperties(obj, {
          firstName: {
            value: 'Bob',
            writable: true
          },
          lastName: {
            value: 'Smith',
            writable: false
          }
        });
        

        在 JavaScript 的早期版本中,模仿基于类的继承的唯一真正方法是使用构造函数。构造函数是一个特殊的函数,使用“new”关键字调用。按照惯例,函数标识符是大写的,尽管它不是必需的。在构造函数内部,我们引用 'this' 关键字来向构造函数隐式创建的对象添加属性。构造函数隐式地将具有填充属性的新对象返回给调用函数,除非您显式使用 return 关键字并返回其他内容。

        function Person(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        
            this.sayName = function(){
                return "My name is " + this.firstName + " " + this.lastName;
            }
        } 
        
        var bob = new Person("Bob", "Smith");
        bob instanceOf Person // true
        

        sayName 方法有问题。通常,在面向对象的基于类的编程语言中,您使用类作为工厂来创建对象。每个对象都有自己的实例变量,但它会有一个指向类蓝图中定义的方法的指针。不幸的是,在使用 JavaScript 的构造函数时,每次调用它都会在新创建的对象上定义一个新的 sayName 属性。所以每个对象都有自己独特的 sayName 属性。这会消耗更多的内存资源。

        除了增加内存资源外,在构造函数内部定义方法消除了继承的可能性。同样,该方法将被定义为新创建对象的属性,而不是其他对象,因此继承不能像这样工作。因此,JavaScript 提供了原型链作为一种继承形式,使 JavaScript 成为一种原型语言。

        如果您有一个父母并且父母共享孩子的许多属性,那么孩子应该继承这些属性。在 ES5 之前,它是这样完成的:

        function Parent(eyeColor, hairColor) {
            this.eyeColor = eyeColor;
            this.hairColor = hairColor;
        }
        
        Parent.prototype.getEyeColor = function() {
          console.log('has ' + this.eyeColor);
        }
        
        Parent.prototype.getHairColor = function() {
          console.log('has ' + this.hairColor);
        }
        
        function Child(firstName, lastName) {
          Parent.call(this, arguments[2], arguments[3]);
          this.firstName = firstName;
          this.lastName = lastName;
        }
        
        Child.prototype = Parent.prototype;
        
        var child = new Child('Bob', 'Smith', 'blue', 'blonde');
        child.getEyeColor(); // has blue eyes
        child.getHairColor(); // has blonde hair
        

        我们使用上述原型链的方式有一个怪癖。由于原型是一个实时链接,通过更改原型链中一个对象的属性,您也将更改另一个对象的相同属性。显然,改变子继承的方法不应该改变父方法。 Object.create 通过使用 polyfill 解决了这个问题。因此,使用 Object.create,您可以安全地修改原型链中子级的属性,而不会影响原型链中父级的相同属性。

        ECMAScript 5 引入了 Object.create 来解决前面提到的用于创建对象的构造函数中的错误。 Object.create() 方法创建一个新对象,使用现有对象作为新创建对象的原型。由于创建了一个新对象,您不再遇到修改原型链中的子属性将修改父对链中该属性的引用的问题。

        var bobSmith = {
            firstName: "Bob",
            lastName: "Smith",
            sayName: function(){
              return "My name is " + this.firstName + " " + this.lastName;
            }
        }
        
        var janeSmith = Object.create(bobSmith, {
            firstName : {  value: "Jane" }
        })
        
        console.log(bobSmith.sayName()); // My name is Bob Smith
        console.log(janeSmith.sayName()); // My name is Jane Smith
        janeSmith.__proto__ == bobSmith; // true
        janeSmith instanceof bobSmith; // Uncaught TypeError: Right-hand side of 'instanceof' is not callable. Error occurs because bobSmith is not a constructor function.
        

        在 ES6 之前,这是一种利用函数构造函数和 Object.create 的常见创建模式:

        const View = function(element){
          this.element = element;
        }
        
        View.prototype = {
          getElement: function(){
            this.element
          }
        }
        
        const SubView = function(element){
          View.call(this, element);
        }
        
        SubView.prototype = Object.create(View.prototype);
        

        现在 Object.create 加上构造函数已被广泛用于 JavaScript 中的对象创建和继承。然而,ES6 引入了类的概念,它主要是 JavaScript 现有的基于原型的继承的语法糖。类语法不会向 JavaScript 引入新的面向对象的继承模型。因此,JavaScript 仍然是一种原型语言。

        ES6 类使继承更容易。我们不再需要手动复制父类的原型函数和重置子类的构造函数。

        // create parent class
        class Person {
          constructor (name) {
            this.name = name;
          }
        }
        
        // create child class and extend our parent class
        class Boy extends Person {
          constructor (name, color) {
            // invoke our parent constructor function passing in any required parameters
            super(name);
        
            this.favoriteColor = color;
          }
        }
        
        const boy = new Boy('bob', 'blue')
        boy.favoriteColor; // blue
        

        总而言之,这 5 种不同的 JavaScript 对象创建策略与 EcmaScript 标准的演变相吻合。

        【讨论】:

          【解决方案7】:

          当然有最好的办法。javascript中的对象具有可枚举和不可枚举的属性。

          var empty = {};
          console.log(empty.toString);
          // . function toString(){...}
          console.log(empty.toString());
          // . [object Object]
          

          在上面的示例中,您可以看到一个空对象实际上具有属性。

          好的,首先让我们看看哪种方法最好:

          var new_object = Object.create(null)
          
          new_object.name = 'Roland'
          new_object.last_name = 'Doda'
          //etc
          
          console.log("toString" in new_object) //=> false
          

          在上面的示例中,日志将输出 false。

          现在让我们看看为什么其他的对象创建方式不正确。

          //Object constructor
          var object = new Object();
          
          console.log("toString" in object); //=> true
          
          //Literal constructor
          var person = { 
            name : "Anand",
            getName : function (){
             return this.name
            } 
          } 
          
          console.log("toString" in person); //=> true
          
          //function Constructor
          function Person(name){
            this.name = name
            this.getName = function(){
              return this.name
            } 
          }
          
          var person = new Person ('landi')
          
          console.log("toString" in person); //=> true
          
          //Prototype
          function Person(){};
          
          Person.prototype.name = "Anand";
          
          console.log("toString" in person); //=> true
          
          //Function/Prototype combination
          function Person2(name){
            this.name = name;
          } 
          
          Person2.prototype.getName = function(){
            return this.name
          }
          
          var person2 = new Person2('Roland')
          
          console.log("toString" in person2) //=> true
          

          正如您在上面看到的,所有示例都记录为 true。这意味着如果您有一个案例,即您有一个 for in 循环来查看对象是否具有属性,可能会导致您得到错误的结果。 em>

          请注意,最好的方法并不容易。您必须逐行定义对象的所有属性。其他方法更容易,创建对象的代码更少,但您必须注意一些案例。 顺便说一句,我总是使用“其他方式”,如果您不使用最佳方式,上述警告的一种解决方案是:

           for (var property in new_object) {
            if (new_object.hasOwnProperty(property)) {
              // ... this is an own property
            }
           }
          

          【讨论】:

            【解决方案8】:

            主要有3种创建对象的方式-

            最简单的方法是使用对象字面量

            const myObject = {}
            

            虽然这种方法是最简单的,但有一个缺点,即如果你的对象有行为(其中的函数),那么将来如果你想对它进行任何更改,你必须在所有对象中更改它

            所以在这种情况下最好使用工厂或构造函数。(任何你喜欢的)

            工厂函数是那些返回对象的函数。例如-

            function factoryFunc(exampleValue){
               return{
                  exampleProperty: exampleValue 
               }
            }
            

            构造函数是那些使用“this”关键字为对象分配属性的函数。例如-

            function constructorFunc(exampleValue){
               this.exampleProperty= exampleValue;
            }
            const myObj= new constructorFunc(1);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-01-13
              • 1970-01-01
              • 2013-05-04
              • 2011-03-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多