哪种方式最适合在JavaScript中创建对象?在对象属性之前是否需要`var`?

时间:2011-07-27 12:03:19

标签: javascript object

到目前为止,我看到了在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 */ }

8 个答案:

答案 0 :(得分:172)

没有最佳方式,这取决于您的使用案例。

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

答案 1 :(得分:109)

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

  1. 对象构造函数
  2. 文字构造函数
  3. 基于功能
  4. 基于原型
  5. 基于功能和原型
  6. 基于Singleton
  7. 示例:

    1. 对象构造函数
    2. var person = new Object();
      
      person.name = "Anand",
      person.getName = function(){
        return this.name ; 
      };
      
      1. 文字构造函数
      2. var person = { 
          name : "Anand",
          getName : function (){
           return this.name
          } 
        } 
        
        1. function Constructor
        2. function Person(name){
            this.name = name
            this.getName = function(){
              return this.name
            } 
          } 
          
          1. 原型
          2. function Person(){};
            
            Person.prototype.name = "Anand";
            
            1. 功能/原型组合
            2. function Person(name){
                this.name = name;
              } 
              Person.prototype.getName = function(){
                return this.name
              } 
              
              1. 的Singleton
              2. var person = new function(){
                  this.name = "Anand"
                } 
                

                如果您有任何疑惑,可以在控制台上试用。

答案 2 :(得分:10)

创建对象没有“最好的方法”。根据您的使用情况,每种方式都有好处。

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

但是,如果你想要原型继承,你也可以使用Object.create,这使得继承更加明显。

创建一个对象文字(例如:var obj = {foo: "bar"};)如果你碰巧拥有你希望在创建时手头设置的所有属性,那么效果很好。

如果您知道属性名称,稍后要设置属性,NewObject.property1语法通常比NewObject['property1']更可取。但是当你事先没有提到属性的名字时,后者很有用(例如:NewObject[someStringVar])。

希望这有帮助!

答案 3 :(得分:6)

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

示例:

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];

答案 4 :(得分:3)

有许多方法可以在JavaScript中创建对象。 使用构造函数创建对象或对象文字表示法在JavaScript中使用了很多。还要创建一个Object实例,然后向其中添加属性和方法,有三种常见的方法可以在JavaScript中创建对象。

构造函数

有内置的构造函数我们都可以不时使用它们,比如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';

答案 5 :(得分:0)

  

当然有一种最好的方法.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循环来查看该对象是否具有属性将导致您错误的结果。

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

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

答案 6 :(得分:0)

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

最简单的一种是使用对象文字

const myObject = {}

虽然此方法最简单,但有一个缺点,即如果您的对象具有行为(其中具有功能),那么将来如果您要对其进行任何更改,则必须在所有对象中对其进行更改

因此,在这种情况下,最好使用Factory或Constructor函数。(您喜欢的任何人)

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

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

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

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

答案 7 :(得分:0)

尽管这里很多人说没有最佳的对象创建方法,但是有一个合理的理由说明,为什么到2019年为止,有这么多的方法可以用JavaScript创建对象,这与JavaScript的进步有关。 EcmaScript版本的不同迭代可以追溯到1997年。

在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标准的发展相吻合。