如何在JavaScript中“正确”创建自定义对象?

时间:2009-10-20 15:49:02

标签: javascript

我想知道创建具有属性和方法的JavaScript对象的最佳方法是什么。

我见过这个人使用var self = this然后在所有函数中使用self.以确保范围始终正确的示例。

然后我看到了使用.prototype添加属性的示例,而其他人则是内联的。

有人可以给我一个带有一些属性和方法的JavaScript对象的正确示例吗?

14 个答案:

答案 0 :(得分:871)

答案 1 :(得分:90)

我经常使用这种模式 - 我发现它在我需要它时给了我非常大的灵活性。在使用中,它与Java风格的类非常相似。

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

这使用在创建时调用的匿名函数,返回新的构造函数。因为匿名函数只被调用一次,所以可以在其中创建私有静态变量(它们位于闭包内部,对类的其他成员可见)。构造函数基本上是一个标准的Javascript对象 - 您在其中定义私有属性,公共属性附加到this变量。

基本上,这种方法将Crockfordian方法与标准Javascript对象相结合,以创建更强大的类。

您可以像使用任何其他Javascript对象一样使用它:

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method

答案 2 :(得分:24)

Douglas Crockford The Good Parts 中广泛讨论了该主题。他建议避免使用 new 运算符来创建新对象。相反,他建议创建自定义构造函数。例如:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

在Javascript中,函数是一个对象,可以用于与 new 运算符一起构造对象。按照惯例,旨在用作构造函数的函数以大写字母开头。你经常会看到类似的东西:

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

如果您在实例化新对象时忘记使用 new 运算符,则获得的是普通函数调用,绑定到全局对象到了新的对象。

答案 3 :(得分:13)

继续bobince's answer

在es6中,您现在可以实际创建class

现在你可以这样做:

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

所以你可以做到:(或在另一个答案中):

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

在es6中结束一点清洁,更容易阅读。

这是一个很好的例子:



class Shape {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `Shape at ${this.x}, ${this.y}`;
  }
}

class Circle extends Shape {
  constructor(x, y, r) {
    super(x, y);
    this.r = r;
  }

  toString() {
    let shapeString = super.toString();
    return `Circular ${shapeString} with radius ${this.r}`;
  }
}

let c = new Circle(1, 2, 4);

console.log('' + c, c);




答案 4 :(得分:6)

你也可以这样做,使用结构:

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

然后:

var counter1 = createCounter();
counter1.increaseBy(4);

答案 5 :(得分:5)

另一种方式是http://jsfiddle.net/nnUY4/ (我不知道这种处理对象创建和显示功能是否遵循任何特定模式)

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"

答案 6 :(得分:4)

当在构造函数调用期间使用关闭“this”的技巧时,它是为了编写一个函数,该函数可以被一些不想在对象上调用方法的其他对象用作回调。这与“使范围正确”无关。

这是一个vanilla JavaScript对象:

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

您可能会阅读Douglas Crockford关于JavaScript的内容。 John Resig也很棒。祝你好运!

答案 7 :(得分:4)

Closure是多才多艺的。 bobince 在创建对象时很好地总结了原型与闭包方法。但是,您可以使用函数编程方式中的闭包来模仿OOP的某些方面。记住函数是JavaScript中的对象;所以以不同的方式使用函数作为对象。

以下是关闭的一个例子:

function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}

不久前,我偶然发现了Mozilla关于Closure的文章。以下是我的观点:“闭包允许您将一些数据(环境)与对该数据进行操作的函数相关联。这与面向对象编程有明显的相似之处,其中对象允许我们关联某些数据(对象的属性)使用一个或多个方法“。这是我第一次阅读闭包和经典OOP之间的并行性而没有引用原型。

如何?

假设您要计算某些商品的增值税。增值税可能在申请期限内保持稳定。在OOP(伪代码)中执行此操作的一种方法:

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

基本上,您将VAT值传递给构造函数,并且您的calculate方法可以通过 closure 对其进行操作。 现在,不要使用类/构造函数,将VAT作为参数传递给函数。因为您感兴趣的唯一内容是计算本身,所以返回一个新函数,即计算方法:

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

在您的项目中,确定最适合计算增值税的顶级值。根据经验,无论何时打开和打开相同的参数,都有一种方法可以使用闭包来改进它。无需创建传统对象。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

答案 8 :(得分:3)

创建对象

在JavaScript中创建对象的最简单方法是使用以下语法:

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

这非常适合以结构化方式存储数据。

但是,对于更复杂的用例,创建函数实例通常更好:

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

这允许您创建共享相同“蓝图”的多个对象,类似于您在例如中使用类的方式。 Java的。

然而,通过使用原型,这仍然可以更有效地完成。

每当函数的不同实例共享相同的方法或属性时,您可以将它们移动到该对象的原型。这样,函数的每个实例都可以访问该方法或属性,但不需要为每个实例复制它。

在我们的例子中,将方法f移动到原型是有意义的:

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

继承

在JavaScript中进行继承的一种简单但有效的方法是使用以下两行:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

这类似于这样做:

B.prototype = new A();

两者之间的主要区别在于A的构造函数在使用Object.create时未运行,Function.prototype.apply()更直观,更类似于基于类的继承。

您可以选择在创建A的新实例时选择性地运行B的构造函数,方法是将其添加到B的构造函数中:

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

如果您想将B的所有参数传递给A,您还可以使用Object.assign

function B() {
    A.apply(this, arguments); // This is optional
}

如果要将另一个对象混合到B的构造函数链中,可以将Object.createpolyfill结合使用:

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

演示

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());

注意

Object.create可以在每个现代浏览器中安全使用,包括IE9 +。 Object.assign在任何版本的IE或某些移动浏览器中都不起作用。如果您想使用它们并支持不实现它们的浏览器,建议here Object.create和/或Object.assign

您可以找到Object.create here的填充物  和Object.assign {{3}}一个。

答案 9 :(得分:0)

var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);

答案 10 :(得分:0)

除了2009年接受的答案外。如果您可以定位现代浏览器,可以使用 Object.defineProperty

  

Object.defineProperty()方法直接定义一个新属性   对象,或修改对象上的现有属性,然后返回   物体。   资料来源:Mozilla

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

要查看桌面和移动兼容性列表,请参阅Mozilla's Browser Compatibility list。是的,IE9 +支持它以及Safari移动。

答案 11 :(得分:0)

你也可以尝试这个

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();

答案 12 :(得分:0)

我想提一下,我们可以使用标题字符串来声明对象。
调用每种类型的方法有不同的方法。见下文:



var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));




答案 13 :(得分:-1)

基本上JS中没有类的概念,所以我们使用函数作为与现有设计模式相关的类构造函数。

//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

直到现在JS不知道你想要创建一个对象,所以这里有新的关键字。

var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

参考:适用于网页开发人员的专业JS - Nik Z