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

时间:2022-12-20 02:54:20

I wonder about what the best way is to create an JavaScript object that has properties and methods.

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

I have seen examples where the person used var self = this and then uses self. in all functions to make sure the scope is always correct.

我曾见过有人使用var self = this然后使用self的例子。在所有的函数中,确保范围总是正确的。

Then I have seen examples of using .prototype to add properties, while others do it inline.

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

Can someone give me a proper example of a JavaScript object with some properties and methods?

有人能给我一个JavaScript对象的适当的例子吗?

15 个解决方案

#1


848  

There are two models for implementing classes and instances in JavaScript: the prototyping way, and the closure way. Both have advantages and drawbacks, and there are plenty of extended variations. Many programmers and libraries have different approaches and class-handling utility functions to paper over some of the uglier parts of the language.

在JavaScript中实现类和实例有两个模型:原型方式和闭包方式。两者都有优缺点,并且有很多扩展的变体。许多程序员和库都有不同的方法和类处理实用函数来覆盖语言中一些较丑的部分。

The result is that in mixed company you will have a mishmash of metaclasses, all behaving slightly differently. What's worse, most JavaScript tutorial material is terrible and serves up some kind of in-between compromise to cover all bases, leaving you very confused. (Probably the author is also confused. JavaScript's object model is very different to most programming languages, and in many places straight-up badly designed.)

结果是,在混合型公司中,你会有一大堆元类,它们的行为都略有不同。更糟糕的是,大多数JavaScript教程的内容都很糟糕,并且在折衷方案中涵盖了所有的基本内容,这让您非常困惑。(可能作者也很困惑。JavaScript的对象模型与大多数编程语言非常不同,在很多地方设计得很糟糕。

Let's start with the prototype way. This is the most JavaScript-native you can get: there is a minimum of overhead code and instanceof will work with instances of this kind of object.

让我们从原型方式开始。这是您可以获得的最javascript本地的:最少的开销代码和instanceof将用于此类对象的实例。

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

We can add methods to the instance created by new Shape by writing them to the prototype lookup of this constructor function:

我们可以将方法添加到由新形状创建的实例中,并将它们写入这个构造函数函数的原型查找中:

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

Now to subclass it, in as much as you can call what JavaScript does subclassing. We do that by completely replacing that weird magic prototype property:

现在来子类化它,就像你可以调用JavaScript的子类一样。我们通过完全替换那个奇怪的魔法原型属性来实现:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

before adding methods to it:

在添加方法之前:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

This example will work and you will see code like it in many tutorials. But man, that new Shape() is ugly: we're instantiating the base class even though no actual Shape is to be created. It happens to work in this simple case because JavaScript is so sloppy: it allows zero arguments to be passed in, in which case x and y become undefined and are assigned to the prototype's this.x and this.y. If the constructor function were doing anything more complicated, it would fall flat on its face.

这个示例将会工作,您将在许多教程中看到类似的代码。但是,这个新的Shape()很难看:我们正在实例化基类,即使没有创建实际的形状。它在这个简单的例子中工作,因为JavaScript是如此的草率:它允许传入零参数,在这种情况下,x和y变得没有定义,并被分配给原型。x和this.y。如果构造函数要做更复杂的事情,那么它就会完全失败。

So what we need to do is find a way to create a prototype object which contains the methods and other members we want at a class level, without calling the base class's constructor function. To do this we are going to have to start writing helper code. This is the simplest approach I know of:

因此,我们需要做的是找到一种方法来创建一个原型对象,该对象包含我们在类级别上想要的方法和其他成员,而不调用基类的构造函数。为此,我们必须开始编写帮助代码。这是我所知道的最简单的方法:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

This transfers the base class's members in its prototype to a new constructor function which does nothing, then uses that constructor. Now we can write simply:

这将把基类的原型中的成员转移到一个什么都不做的新构造函数,然后使用这个构造函数。现在我们可以简单地写:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

instead of the new Shape() wrongness. We now have an acceptable set of primitives to built classes.

而不是新的形状()错误。现在我们有了一组可接受的原语来构建类。

There are a few refinements and extensions we can consider under this model. For example here is a syntactical-sugar version:

在这个模型下,我们可以考虑一些改进和扩展。例如,这是一个合成糖的版本:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

Either version has the drawback that the constructor function cannot be inherited, as it is in many languages. So even if your subclass adds nothing to the construction process, it must remember to call the base constructor with whatever arguments the base wanted. This can be slightly automated using apply, but still you have to write out:

这两种版本都有不能继承构造函数的缺点,因为在许多语言中都是这样。因此,即使您的子类没有向构造过程添加任何内容,它也必须记住使用基类想要的参数调用基构造函数。使用apply可以稍微自动化一些,但是您仍然需要写出:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

So a common extension is to break out the initialisation stuff into its own function rather than the constructor itself. This function can then inherit from the base just fine:

因此,一个常见的扩展是将初始化的内容分解为它自己的函数,而不是构造函数本身。这个函数可以很好地从基数上继承:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

Now we've just got the same constructor function boilerplate for each class. Maybe we can move that out into its own helper function so we don't have to keep typing it, for example instead of Function.prototype.subclass, turning it round and letting the base class's Function spit out subclasses:

现在我们得到了每个类相同的构造函数样板。也许我们可以把它移到它自己的助手函数中,这样我们就不需要继续输入它,例如,而不是function。prototype。子类化,将其旋转,并让基类的函数输出子类:

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

...which is starting to look a bit more like other languages, albeit with slightly clumsier syntax. You can sprinkle in a few extra features if you like. Maybe you want makeSubclass to take and remember a class name and provide a default toString using it. Maybe you want to make the constructor detect when it has accidentally been called without the new operator (which would otherwise often result in very annoying debugging):

…尽管语法有点笨拙,但它开始变得更像其他语言。如果您愿意,您可以在其中添加一些额外的特性。也许您希望makeSubclass获取并记住一个类名,并使用它提供一个默认的toString。也许您想让构造函数检测当它在没有新的操作符的情况下被意外调用时(否则通常会导致非常烦人的调试):

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

Maybe you want to pass in all the new members and have makeSubclass add them to the prototype, to save you having to write Class.prototype... quite so much. A lot of class systems do that, eg:

也许您想要传递所有的新成员,并将其添加到原型中,以节省您必须编写类.prototype……那么多。许多类系统都这样做,例如:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

There are a lot of potential features you might consider desirable in an object system and no-one really agrees on one particular formula.

在对象系统中,有许多您可能认为需要的潜在特性,但是没有人真正同意某个特定公式。


The closure way, then. This avoids the problems of JavaScript's prototype-based inheritance, by not using inheritance at all. Instead:

闭包的方法。这避免了JavaScript基于原型的继承的问题,完全不使用继承。而不是:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

Now every single instance of Shape will have its own copy of the toString method (and any other methods or other class members we add).

现在,每个Shape实例都有自己的toString方法的副本(以及我们添加的任何其他方法或其他类成员)。

The bad thing about every instance having its own copy of each class member is that it's less efficient. If you are dealing with large numbers of subclassed instances, prototypical inheritance may serve you better. Also calling a method of the base class is slightly annoying as you can see: we have to remember what the method was before the subclass constructor overwrote it, or it gets lost.

每个实例都有自己的类成员副本的缺点是它的效率较低。如果您正在处理大量子类化的实例,那么原型继承可能会更好地为您服务。同样,调用基类的方法也有点烦人,因为您可以看到:我们必须记住,在子类构造函数覆盖它之前,方法是什么,或者它会丢失。

[Also because there is no inheritance here, the instanceof operator won't work; you would have to provide your own mechanism for class-sniffing if you need it. Whilst you could fiddle the prototype objects in a similar way as with prototype inheritance, it's a bit tricky and not really worth it just to get instanceof working.]

也因为这里没有继承,所以instanceof运算符不能工作;如果需要的话,您必须为类嗅探提供您自己的机制。虽然你可以用与原型继承相似的方式修改原型对象,但这有点棘手,仅仅为了获得工作实例并不值得。

The good thing about every instance having its own method is that the method may then be bound to the specific instance that owns it. This is useful because of JavaScript's weird way of binding this in method calls, which has the upshot that if you detach a method from its owner:

每个实例都有自己的方法的好处是方法可以被绑定到拥有它的特定实例。这很有用,因为JavaScript在方法调用中绑定这种奇怪的方式,结果是,如果将一个方法与它的所有者分离:

var ts= mycircle.toString;
alert(ts());

then this inside the method won't be the Circle instance as expected (it'll actually be the global window object, causing widespread debugging woe). In reality this typically happens when a method is taken and assigned to a setTimeout, onclick or EventListener in general.

然后这个方法内部的循环实例就不会像预期的那样(它实际上是全局窗口对象,导致广泛的调试困难)。实际上,当一个方法被分配给一个setTimeout、onclick或EventListener时,通常会发生这种情况。

With the prototype way, you have to include a closure for every such assignment:

使用原型方法,您必须为每一个这样的任务包含一个闭包:

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

or, in the future (or now if you hack Function.prototype) you can also do it with function.bind():

或者,在将来(或者现在,如果你破解了函数。prototype),你也可以使用函数。bind():

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

if your instances are done the closure way, the binding is done for free by the closure over the instance variable (usually called that or self, though personally I would advise against the latter as self already has another, different meaning in JavaScript). You don't get the arguments 1, 1 in the above snippet for free though, so you would still need another closure or a bind() if you need to do that.

如果您的实例是通过闭包方式完成的,那么绑定将由实例变量上的闭包(通常称为that或self,不过我个人建议不要使用后者,因为self在JavaScript中已经有了另一个不同的含义)来完成。您不会免费获得上面代码片段中的参数1,1,因此如果需要这样做,您仍然需要另一个闭包或bind()。

There are lots of variants on the closure method too. You may prefer to omit this completely, creating a new that and returning it instead of using the new operator:

闭包方法也有很多变体。您可能更喜欢完全省略这个,创建一个新的that并返回它,而不是使用新的操作符:

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

Which way is “proper”? Both. Which is “best”? That depends on your situation. FWIW I tend towards prototyping for real JavaScript inheritance when I'm doing strongly OO stuff, and closures for simple throwaway page effects.

哪条路是“适当的”?两者都有。“最好”是哪一个?那要看你的情况而定。FWIW:当我在做面向对象的东西时,我倾向于原型化以获得真正的JavaScript继承,而闭包则用于简单的一次性页面效果。

But both ways are quite counter-intuitive to most programmers. Both have many potential messy variations. You will meet both (as well as many in-between and generally broken schemes) if you use other people's code/libraries. There is no one generally-accepted answer. Welcome to the wonderful world of JavaScript objects.

但是对于大多数程序员来说,这两种方法都是非常反直觉的。两者都有许多潜在的混乱变化。如果您使用其他人的代码/库,您将同时遇到这两种情况(以及许多介于两者之间和通常被破坏的方案)。没有一个普遍接受的答案。欢迎来到JavaScript对象的奇妙世界。

[This has been part 94 of Why JavaScript Is Not My Favourite Programming Language.]

这就是为什么JavaScript不是我最喜欢的编程语言的第94部分。

#2


87  

I use this pattern fairly frequently - I've found that it gives me a pretty huge amount of flexibility when I need it. In use it's rather similar to Java-style classes.

我经常使用这种模式——我发现当我需要它时,它会给我很大的灵活性。在使用中,它与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;
}();

This uses an anonymous function that is called upon creation, returning a new constructor function. Because the anonymous function is called only once, you can create private static variables in it (they're inside the closure, visible to the other members of the class). The constructor function is basically a standard Javascript object - you define private attributes inside of it, and public attributes are attached to the this variable.

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

Basically, this approach combines the Crockfordian approach with standard Javascript objects to create a more powerful class.

基本上,这种方法结合了Crockfordian方法和标准Javascript对象来创建更强大的类。

You can use it just like you would any other Javascript object:

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

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

#3


24  

Douglas Crockford discusses that topic extensively in The Good Parts. He recommends to avoid the new operator to create new objects. Instead he proposes to create customized constructors. For instance:

道格拉斯·克罗克福德在《美好的部分》中详细讨论了这个话题。他建议避免使用新的操作符来创建新的对象。相反,他建议创建定制的构造函数。例如:

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'});

In Javascript a function is an object, and can be used to construct objects out of together with the new operator. By convention, functions intended to be used as constructors start with a capital letter. You often see things like:

在Javascript中,函数是一个对象,可以用来与新的操作符一起构造对象。按照惯例,作为构造函数的功能以大写字母开头。你经常看到这样的事情:

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

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

In case you forget to use the new operator while instantiating a new object, what you get is an ordinary function call, and this is bound to the global object instead to the new object.

如果您在实例化一个新对象时忘记使用新操作符,那么您得到的是一个普通的函数调用,它被绑定到全局对象而不是新对象。

#4


11  

To continue off of bobince's answer

继续讨论bobince的答案。

In es6 you can now actually create a class

在es6中,您现在可以实际创建一个类

So now you can do:

现在你可以这样做了:

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

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

So extend to a circle (as in the other answer) you can do:

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

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}`;
    }
}

Ends up a bit cleaner in es6 and a little easier to read.

最后在es6中使用了一些清洁剂,并且更容易阅读。


Here is a good example of it in action:

这是一个很好的例子:

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

#5


6  

You can also do it this way, using structures :

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

function createCounter () {
    var count = 0;

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

Then :

然后:

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

#6


4  

When one uses the trick of closing on "this" during a constructor invocation, it's in order to write a function that can be used as a callback by some other object that doesn't want to invoke a method on an object. It's not related to "making the scope correct".

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

Here's a vanilla JavaScript object:

下面是一个普通的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);
};

You might get a lot out of reading what Douglas Crockford has to say about JavaScript. John Resig is also brilliant. Good luck!

你可能会从阅读道格拉斯·克罗克福德关于JavaScript的文章中得到很多。John Resig也很聪明。好运!

#7


4  

Another way would be http://jsfiddle.net/nnUY4/ (i dont know if this kind of handling object creation and revealing functions follow any specific pattern)

另一种方式是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"

#8


4  

Closure is versatile. bobince has well summarized the prototype vs. closure approaches when creating objects. However you can mimic some aspects of OOP using closure in a functional programming way. Remember functions are objects in JavaScript; so use function as object in a different way.

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

Here is an example of closure:

这里有一个结束的例子:

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

A while ago I came across the Mozilla's article on Closure. Here is what jump at my eyes: "A closure lets you associate some data (the environment) with a function that operates on that data. This has obvious parallels to object oriented programming, where objects allow us to associate some data (the object's properties) with one or more methods". It was the very first time I read a parallelism between closure and classic OOP with no reference to prototype.

不久前,我偶然看到了Mozilla关于关闭的文章。以下是我的看法:“闭包允许您将某些数据(环境)与操作该数据的函数关联起来。这与面向对象编程有明显的相似之处,在面向对象编程中,对象允许我们将一些数据(对象的属性)与一个或多个方法关联起来。这是我第一次读到闭包和经典OOP之间的并行性,并且没有提到prototype。

How?

如何?

Suppose you want to calculate the VAT of some items. The VAT is likely to stay stable during the lifetime of an application. One way to do it in OOP (pseudo code):

假设你想计算一些物品的增值税。在应用程序的生命周期中,VAT可能保持稳定。在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;
    }
}

Basically you pass a VAT value into your constructor and your calculate method can operate upon it via closure. Now instead of using a class/constructor, pass your VAT as an argument into a function. Because the only stuff you are interested in is the calculation itself, returns a new function, which is the calculate method:

基本上,您将增值税值传递给构造函数,您的计算方法可以通过闭包对其进行操作。现在,不要使用类/构造函数,而是将VAT作为参数传递到函数中。因为你唯一感兴趣的是计算本身,返回一个新的函数,即计算方法:

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

In your project identify top-level values that are good candidate of what VAT is for calculation. As a rule of thumb whenever you pass the same arguments on and on, there is a way to improve it using closure. No need to create traditional objects.

在您的项目中,确定最适合计算VAT的*值。作为经验法则,无论何时你通过相同的参数,都有一种方法可以使用闭包来改进它。不需要创建传统对象。

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

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

#9


3  

Creating an object

The easiest way to create an object in JavaScript is to use the following syntax :

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

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

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

This works great for storing data in a structured way.

这对于以结构化的方式存储数据非常有用。

For more complex use cases, however, it's often better to create instances of functions :

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

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

This allows you to create multiple objects that share the same "blueprint", similar to how you use classes in eg. Java.

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

This can still be done more efficiently, however, by using a prototype.

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

Whenever different instances of a function share the same methods or properties, you can move them to that object's prototype. That way, every instance of a function has access to that method or property, but it doesn't need to be duplicated for every instance.

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

In our case, it makes sense to move the method f to the prototype :

在我们的例子中,将方法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));

Inheritance

A simple but effective way to do inheritance in JavaScript, is to use the following two-liner :

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

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

That is similar to doing this :

这与此类似:

B.prototype = new A();

The main difference between both is that the constructor of A is not run when using Object.create, which is more intuitive and more similar to class based inheritance.

两者的主要区别在于,在使用对象时,A的构造函数不是运行的。create更直观,更类似于基于类的继承。

You can always choose to optionally run the constructor of A when creating a new instance of B by adding adding it to the constructor of B :

在创建B的新实例时,您可以选择在B的构造函数中添加A的构造函数:

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

If you want to pass all arguments of B to A, you can also use Function.prototype.apply() :

如果要将B的所有参数传递给A,还可以使用Function.prototype.apply():

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

If you want to mixin another object into the constructor chain of B, you can combine Object.create with Object.assign :

如果要将另一个对象混合到B的构造函数链中,可以组合对象。创建与对象。分配:

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

Demo

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());


Note

Object.create can be safely used in every modern browser, including IE9+. Object.assign does not work in any version of IE nor some mobile browsers. It is recommended to polyfill Object.create and/or Object.assign if you want to use them and support browsers that do not implement them.

对象。create可以安全地在所有现代浏览器中使用,包括IE9+。对象。assign在任何IE版本或某些移动浏览器中都不能工作。建议多填充对象。创建和/或对象。如果您希望使用它们并支持不实现它们的浏览器,请进行分配。

You can find a polyfill for Object.create here and one for Object.assign here.

您可以为对象找到一个polyfill。在这里创建一个对象。分配。

#10


0  

In addition to the accepted answer from 2009. If you can can target modern browsers, one can make use of the Object.defineProperty.

除了2009年接受的答案。如果您能够以现代浏览器为目标,就可以使用Object.defineProperty。

The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object. Source: Mozilla

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();

To see a desktop and mobile compatibility list, see Mozilla's Browser Compatibility list. Yes, IE9+ supports it as well as Safari mobile.

要查看桌面和移动兼容性列表,请参见Mozilla的浏览器兼容性列表。是的,IE9+和Safari手机都支持。

#11


0  

You can also try this

你也可以试试这个

    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  

A Pattern That Serves Me Well
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

First, you may change your preference of adding methods to the instance instead of the constructor's prototype object. I almost always declare methods inside of the constructor because I use Constructor Hijacking very often for purposes regarding Inheritance & Decorators.

首先,您可以更改向实例添加方法的首选项,而不是构造函数的prototype对象。我几乎总是在构造函数内部声明方法,因为我经常使用构造函数劫持用于继承和装饰器的目的。

Here's how I decide where which declarations are writ:

以下是我如何决定哪些声明是令状:

  • Never declare a method directly on the context object (this)
  • 不要直接在上下文对象上声明方法(以下)
  • Let var declarations take precedence over function declarations
  • 让var声明优先于函数声明
  • Let primitives take precedence over objects ({} and [])
  • 让原语优先于对象({}和[])
  • Let public declarations take precedence over private declarations
  • 让公共声明优先于私有声明
  • Prefer Function.prototype.bind over thus, self, vm, etc
  • 喜欢Function.prototype。因此绑定self、vm等
  • Avoid declaring a Class within another Class, unless:
    • It should be obvious that the two are inseparable
    • 这两者显然是密不可分的
    • The Inner class implements The Command Pattern
    • 内部类实现命令模式
    • The Inner class implements The Singleton Pattern
    • 内部类实现了单例模式。
    • The Inner class implements The State Pattern
    • 内部类实现状态模式
    • The Inner Class implements another Design Pattern that warrants this
    • 内部类实现了另一种设计模式来保证这一点
  • 避免在另一个类中声明一个类,除非:应该很明显,这两个类是不可分割的,内部类实现命令模式,内部类实现单例模式,内部类实现状态模式,内部类实现另一个设计模式
  • Always return this from within the Lexical Scope of the Closure Space.
  • 总是从闭包空间的词法范围内返回这个。

Here's why these help:

这就是为什么这些帮助:

Constructor Hijacking
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
Model Design
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
Design Patterns
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

As you can see, I really have no need for thus since I prefer Function.prototype.bind (or .call or .apply) over thus. In our Singleton class, we don't even name it thus because INSTANCE conveys more information. For Model, we return this so that we can invoke the Constructor using .call to return the instance we passed into it. Redundantly, we assigned it to the variable updated, though it is useful in other scenarios.

正如您所看到的,我确实不需要这样,因为我更喜欢Function.prototype。这样绑定(或.call或.apply)。在单例类中,我们甚至不这样命名,因为实例传递了更多的信息。对于Model,我们返回它,以便使用.call调用构造函数来返回传递给它的实例。冗余地,我们将它分配给更新的变量,尽管它在其他场景中很有用。

Alongside, I prefer constructing object-literals using the new keyword over {brackets}:

此外,我更喜欢使用new关键字而不是{括号}构造对象-文字:

Preferred
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
Not Preferred
var klass = Super.apply({
    override: x
});

As you can see, the latter has no ability to override its Superclass's "override" property.

如您所见,后者没有能力覆盖其超类的“override”属性。

If I do add methods to the Class's prototype object, I prefer an object literal -- with or without using the new keyword:

如果我向类的原型对象添加方法,我更喜欢对象文字——无论是否使用new关键字:

Preferred
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
Not Preferred
Klass.prototype.method = function m() {...};

#13


0  

I'd like to mention that we can use either a Title or a String to declare an Object.
There are different ways on calling each type of them. See below:

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

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'));

#14


-1  

Bascially there is no concept of class in JS so we use function as a class constructor which is relevant with the existing design patterns.

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

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

Till now JS has no clue that you want to create an object so here comes the new keyword.

到目前为止,JS还不知道要创建一个对象,因此这里出现了new关键字。

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

Ref : Professional JS for web developers - Nik Z

Ref:网络开发者的专业JS - Nik Z。

#15


-1  

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

#1


848  

There are two models for implementing classes and instances in JavaScript: the prototyping way, and the closure way. Both have advantages and drawbacks, and there are plenty of extended variations. Many programmers and libraries have different approaches and class-handling utility functions to paper over some of the uglier parts of the language.

在JavaScript中实现类和实例有两个模型:原型方式和闭包方式。两者都有优缺点,并且有很多扩展的变体。许多程序员和库都有不同的方法和类处理实用函数来覆盖语言中一些较丑的部分。

The result is that in mixed company you will have a mishmash of metaclasses, all behaving slightly differently. What's worse, most JavaScript tutorial material is terrible and serves up some kind of in-between compromise to cover all bases, leaving you very confused. (Probably the author is also confused. JavaScript's object model is very different to most programming languages, and in many places straight-up badly designed.)

结果是,在混合型公司中,你会有一大堆元类,它们的行为都略有不同。更糟糕的是,大多数JavaScript教程的内容都很糟糕,并且在折衷方案中涵盖了所有的基本内容,这让您非常困惑。(可能作者也很困惑。JavaScript的对象模型与大多数编程语言非常不同,在很多地方设计得很糟糕。

Let's start with the prototype way. This is the most JavaScript-native you can get: there is a minimum of overhead code and instanceof will work with instances of this kind of object.

让我们从原型方式开始。这是您可以获得的最javascript本地的:最少的开销代码和instanceof将用于此类对象的实例。

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

We can add methods to the instance created by new Shape by writing them to the prototype lookup of this constructor function:

我们可以将方法添加到由新形状创建的实例中,并将它们写入这个构造函数函数的原型查找中:

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

Now to subclass it, in as much as you can call what JavaScript does subclassing. We do that by completely replacing that weird magic prototype property:

现在来子类化它,就像你可以调用JavaScript的子类一样。我们通过完全替换那个奇怪的魔法原型属性来实现:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

before adding methods to it:

在添加方法之前:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

This example will work and you will see code like it in many tutorials. But man, that new Shape() is ugly: we're instantiating the base class even though no actual Shape is to be created. It happens to work in this simple case because JavaScript is so sloppy: it allows zero arguments to be passed in, in which case x and y become undefined and are assigned to the prototype's this.x and this.y. If the constructor function were doing anything more complicated, it would fall flat on its face.

这个示例将会工作,您将在许多教程中看到类似的代码。但是,这个新的Shape()很难看:我们正在实例化基类,即使没有创建实际的形状。它在这个简单的例子中工作,因为JavaScript是如此的草率:它允许传入零参数,在这种情况下,x和y变得没有定义,并被分配给原型。x和this.y。如果构造函数要做更复杂的事情,那么它就会完全失败。

So what we need to do is find a way to create a prototype object which contains the methods and other members we want at a class level, without calling the base class's constructor function. To do this we are going to have to start writing helper code. This is the simplest approach I know of:

因此,我们需要做的是找到一种方法来创建一个原型对象,该对象包含我们在类级别上想要的方法和其他成员,而不调用基类的构造函数。为此,我们必须开始编写帮助代码。这是我所知道的最简单的方法:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

This transfers the base class's members in its prototype to a new constructor function which does nothing, then uses that constructor. Now we can write simply:

这将把基类的原型中的成员转移到一个什么都不做的新构造函数,然后使用这个构造函数。现在我们可以简单地写:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

instead of the new Shape() wrongness. We now have an acceptable set of primitives to built classes.

而不是新的形状()错误。现在我们有了一组可接受的原语来构建类。

There are a few refinements and extensions we can consider under this model. For example here is a syntactical-sugar version:

在这个模型下,我们可以考虑一些改进和扩展。例如,这是一个合成糖的版本:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

Either version has the drawback that the constructor function cannot be inherited, as it is in many languages. So even if your subclass adds nothing to the construction process, it must remember to call the base constructor with whatever arguments the base wanted. This can be slightly automated using apply, but still you have to write out:

这两种版本都有不能继承构造函数的缺点,因为在许多语言中都是这样。因此,即使您的子类没有向构造过程添加任何内容,它也必须记住使用基类想要的参数调用基构造函数。使用apply可以稍微自动化一些,但是您仍然需要写出:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

So a common extension is to break out the initialisation stuff into its own function rather than the constructor itself. This function can then inherit from the base just fine:

因此,一个常见的扩展是将初始化的内容分解为它自己的函数,而不是构造函数本身。这个函数可以很好地从基数上继承:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

Now we've just got the same constructor function boilerplate for each class. Maybe we can move that out into its own helper function so we don't have to keep typing it, for example instead of Function.prototype.subclass, turning it round and letting the base class's Function spit out subclasses:

现在我们得到了每个类相同的构造函数样板。也许我们可以把它移到它自己的助手函数中,这样我们就不需要继续输入它,例如,而不是function。prototype。子类化,将其旋转,并让基类的函数输出子类:

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

...which is starting to look a bit more like other languages, albeit with slightly clumsier syntax. You can sprinkle in a few extra features if you like. Maybe you want makeSubclass to take and remember a class name and provide a default toString using it. Maybe you want to make the constructor detect when it has accidentally been called without the new operator (which would otherwise often result in very annoying debugging):

…尽管语法有点笨拙,但它开始变得更像其他语言。如果您愿意,您可以在其中添加一些额外的特性。也许您希望makeSubclass获取并记住一个类名,并使用它提供一个默认的toString。也许您想让构造函数检测当它在没有新的操作符的情况下被意外调用时(否则通常会导致非常烦人的调试):

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

Maybe you want to pass in all the new members and have makeSubclass add them to the prototype, to save you having to write Class.prototype... quite so much. A lot of class systems do that, eg:

也许您想要传递所有的新成员,并将其添加到原型中,以节省您必须编写类.prototype……那么多。许多类系统都这样做,例如:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

There are a lot of potential features you might consider desirable in an object system and no-one really agrees on one particular formula.

在对象系统中,有许多您可能认为需要的潜在特性,但是没有人真正同意某个特定公式。


The closure way, then. This avoids the problems of JavaScript's prototype-based inheritance, by not using inheritance at all. Instead:

闭包的方法。这避免了JavaScript基于原型的继承的问题,完全不使用继承。而不是:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

Now every single instance of Shape will have its own copy of the toString method (and any other methods or other class members we add).

现在,每个Shape实例都有自己的toString方法的副本(以及我们添加的任何其他方法或其他类成员)。

The bad thing about every instance having its own copy of each class member is that it's less efficient. If you are dealing with large numbers of subclassed instances, prototypical inheritance may serve you better. Also calling a method of the base class is slightly annoying as you can see: we have to remember what the method was before the subclass constructor overwrote it, or it gets lost.

每个实例都有自己的类成员副本的缺点是它的效率较低。如果您正在处理大量子类化的实例,那么原型继承可能会更好地为您服务。同样,调用基类的方法也有点烦人,因为您可以看到:我们必须记住,在子类构造函数覆盖它之前,方法是什么,或者它会丢失。

[Also because there is no inheritance here, the instanceof operator won't work; you would have to provide your own mechanism for class-sniffing if you need it. Whilst you could fiddle the prototype objects in a similar way as with prototype inheritance, it's a bit tricky and not really worth it just to get instanceof working.]

也因为这里没有继承,所以instanceof运算符不能工作;如果需要的话,您必须为类嗅探提供您自己的机制。虽然你可以用与原型继承相似的方式修改原型对象,但这有点棘手,仅仅为了获得工作实例并不值得。

The good thing about every instance having its own method is that the method may then be bound to the specific instance that owns it. This is useful because of JavaScript's weird way of binding this in method calls, which has the upshot that if you detach a method from its owner:

每个实例都有自己的方法的好处是方法可以被绑定到拥有它的特定实例。这很有用,因为JavaScript在方法调用中绑定这种奇怪的方式,结果是,如果将一个方法与它的所有者分离:

var ts= mycircle.toString;
alert(ts());

then this inside the method won't be the Circle instance as expected (it'll actually be the global window object, causing widespread debugging woe). In reality this typically happens when a method is taken and assigned to a setTimeout, onclick or EventListener in general.

然后这个方法内部的循环实例就不会像预期的那样(它实际上是全局窗口对象,导致广泛的调试困难)。实际上,当一个方法被分配给一个setTimeout、onclick或EventListener时,通常会发生这种情况。

With the prototype way, you have to include a closure for every such assignment:

使用原型方法,您必须为每一个这样的任务包含一个闭包:

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

or, in the future (or now if you hack Function.prototype) you can also do it with function.bind():

或者,在将来(或者现在,如果你破解了函数。prototype),你也可以使用函数。bind():

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

if your instances are done the closure way, the binding is done for free by the closure over the instance variable (usually called that or self, though personally I would advise against the latter as self already has another, different meaning in JavaScript). You don't get the arguments 1, 1 in the above snippet for free though, so you would still need another closure or a bind() if you need to do that.

如果您的实例是通过闭包方式完成的,那么绑定将由实例变量上的闭包(通常称为that或self,不过我个人建议不要使用后者,因为self在JavaScript中已经有了另一个不同的含义)来完成。您不会免费获得上面代码片段中的参数1,1,因此如果需要这样做,您仍然需要另一个闭包或bind()。

There are lots of variants on the closure method too. You may prefer to omit this completely, creating a new that and returning it instead of using the new operator:

闭包方法也有很多变体。您可能更喜欢完全省略这个,创建一个新的that并返回它,而不是使用新的操作符:

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

Which way is “proper”? Both. Which is “best”? That depends on your situation. FWIW I tend towards prototyping for real JavaScript inheritance when I'm doing strongly OO stuff, and closures for simple throwaway page effects.

哪条路是“适当的”?两者都有。“最好”是哪一个?那要看你的情况而定。FWIW:当我在做面向对象的东西时,我倾向于原型化以获得真正的JavaScript继承,而闭包则用于简单的一次性页面效果。

But both ways are quite counter-intuitive to most programmers. Both have many potential messy variations. You will meet both (as well as many in-between and generally broken schemes) if you use other people's code/libraries. There is no one generally-accepted answer. Welcome to the wonderful world of JavaScript objects.

但是对于大多数程序员来说,这两种方法都是非常反直觉的。两者都有许多潜在的混乱变化。如果您使用其他人的代码/库,您将同时遇到这两种情况(以及许多介于两者之间和通常被破坏的方案)。没有一个普遍接受的答案。欢迎来到JavaScript对象的奇妙世界。

[This has been part 94 of Why JavaScript Is Not My Favourite Programming Language.]

这就是为什么JavaScript不是我最喜欢的编程语言的第94部分。

#2


87  

I use this pattern fairly frequently - I've found that it gives me a pretty huge amount of flexibility when I need it. In use it's rather similar to Java-style classes.

我经常使用这种模式——我发现当我需要它时,它会给我很大的灵活性。在使用中,它与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;
}();

This uses an anonymous function that is called upon creation, returning a new constructor function. Because the anonymous function is called only once, you can create private static variables in it (they're inside the closure, visible to the other members of the class). The constructor function is basically a standard Javascript object - you define private attributes inside of it, and public attributes are attached to the this variable.

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

Basically, this approach combines the Crockfordian approach with standard Javascript objects to create a more powerful class.

基本上,这种方法结合了Crockfordian方法和标准Javascript对象来创建更强大的类。

You can use it just like you would any other Javascript object:

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

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

#3


24  

Douglas Crockford discusses that topic extensively in The Good Parts. He recommends to avoid the new operator to create new objects. Instead he proposes to create customized constructors. For instance:

道格拉斯·克罗克福德在《美好的部分》中详细讨论了这个话题。他建议避免使用新的操作符来创建新的对象。相反,他建议创建定制的构造函数。例如:

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'});

In Javascript a function is an object, and can be used to construct objects out of together with the new operator. By convention, functions intended to be used as constructors start with a capital letter. You often see things like:

在Javascript中,函数是一个对象,可以用来与新的操作符一起构造对象。按照惯例,作为构造函数的功能以大写字母开头。你经常看到这样的事情:

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

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

In case you forget to use the new operator while instantiating a new object, what you get is an ordinary function call, and this is bound to the global object instead to the new object.

如果您在实例化一个新对象时忘记使用新操作符,那么您得到的是一个普通的函数调用,它被绑定到全局对象而不是新对象。

#4


11  

To continue off of bobince's answer

继续讨论bobince的答案。

In es6 you can now actually create a class

在es6中,您现在可以实际创建一个类

So now you can do:

现在你可以这样做了:

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

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

So extend to a circle (as in the other answer) you can do:

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

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}`;
    }
}

Ends up a bit cleaner in es6 and a little easier to read.

最后在es6中使用了一些清洁剂,并且更容易阅读。


Here is a good example of it in action:

这是一个很好的例子:

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

#5


6  

You can also do it this way, using structures :

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

function createCounter () {
    var count = 0;

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

Then :

然后:

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

#6


4  

When one uses the trick of closing on "this" during a constructor invocation, it's in order to write a function that can be used as a callback by some other object that doesn't want to invoke a method on an object. It's not related to "making the scope correct".

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

Here's a vanilla JavaScript object:

下面是一个普通的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);
};

You might get a lot out of reading what Douglas Crockford has to say about JavaScript. John Resig is also brilliant. Good luck!

你可能会从阅读道格拉斯·克罗克福德关于JavaScript的文章中得到很多。John Resig也很聪明。好运!

#7


4  

Another way would be http://jsfiddle.net/nnUY4/ (i dont know if this kind of handling object creation and revealing functions follow any specific pattern)

另一种方式是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"

#8


4  

Closure is versatile. bobince has well summarized the prototype vs. closure approaches when creating objects. However you can mimic some aspects of OOP using closure in a functional programming way. Remember functions are objects in JavaScript; so use function as object in a different way.

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

Here is an example of closure:

这里有一个结束的例子:

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

A while ago I came across the Mozilla's article on Closure. Here is what jump at my eyes: "A closure lets you associate some data (the environment) with a function that operates on that data. This has obvious parallels to object oriented programming, where objects allow us to associate some data (the object's properties) with one or more methods". It was the very first time I read a parallelism between closure and classic OOP with no reference to prototype.

不久前,我偶然看到了Mozilla关于关闭的文章。以下是我的看法:“闭包允许您将某些数据(环境)与操作该数据的函数关联起来。这与面向对象编程有明显的相似之处,在面向对象编程中,对象允许我们将一些数据(对象的属性)与一个或多个方法关联起来。这是我第一次读到闭包和经典OOP之间的并行性,并且没有提到prototype。

How?

如何?

Suppose you want to calculate the VAT of some items. The VAT is likely to stay stable during the lifetime of an application. One way to do it in OOP (pseudo code):

假设你想计算一些物品的增值税。在应用程序的生命周期中,VAT可能保持稳定。在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;
    }
}

Basically you pass a VAT value into your constructor and your calculate method can operate upon it via closure. Now instead of using a class/constructor, pass your VAT as an argument into a function. Because the only stuff you are interested in is the calculation itself, returns a new function, which is the calculate method:

基本上,您将增值税值传递给构造函数,您的计算方法可以通过闭包对其进行操作。现在,不要使用类/构造函数,而是将VAT作为参数传递到函数中。因为你唯一感兴趣的是计算本身,返回一个新的函数,即计算方法:

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

In your project identify top-level values that are good candidate of what VAT is for calculation. As a rule of thumb whenever you pass the same arguments on and on, there is a way to improve it using closure. No need to create traditional objects.

在您的项目中,确定最适合计算VAT的*值。作为经验法则,无论何时你通过相同的参数,都有一种方法可以使用闭包来改进它。不需要创建传统对象。

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

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

#9


3  

Creating an object

The easiest way to create an object in JavaScript is to use the following syntax :

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

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

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

This works great for storing data in a structured way.

这对于以结构化的方式存储数据非常有用。

For more complex use cases, however, it's often better to create instances of functions :

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

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

This allows you to create multiple objects that share the same "blueprint", similar to how you use classes in eg. Java.

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

This can still be done more efficiently, however, by using a prototype.

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

Whenever different instances of a function share the same methods or properties, you can move them to that object's prototype. That way, every instance of a function has access to that method or property, but it doesn't need to be duplicated for every instance.

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

In our case, it makes sense to move the method f to the prototype :

在我们的例子中,将方法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));

Inheritance

A simple but effective way to do inheritance in JavaScript, is to use the following two-liner :

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

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

That is similar to doing this :

这与此类似:

B.prototype = new A();

The main difference between both is that the constructor of A is not run when using Object.create, which is more intuitive and more similar to class based inheritance.

两者的主要区别在于,在使用对象时,A的构造函数不是运行的。create更直观,更类似于基于类的继承。

You can always choose to optionally run the constructor of A when creating a new instance of B by adding adding it to the constructor of B :

在创建B的新实例时,您可以选择在B的构造函数中添加A的构造函数:

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

If you want to pass all arguments of B to A, you can also use Function.prototype.apply() :

如果要将B的所有参数传递给A,还可以使用Function.prototype.apply():

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

If you want to mixin another object into the constructor chain of B, you can combine Object.create with Object.assign :

如果要将另一个对象混合到B的构造函数链中,可以组合对象。创建与对象。分配:

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

Demo

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());


Note

Object.create can be safely used in every modern browser, including IE9+. Object.assign does not work in any version of IE nor some mobile browsers. It is recommended to polyfill Object.create and/or Object.assign if you want to use them and support browsers that do not implement them.

对象。create可以安全地在所有现代浏览器中使用,包括IE9+。对象。assign在任何IE版本或某些移动浏览器中都不能工作。建议多填充对象。创建和/或对象。如果您希望使用它们并支持不实现它们的浏览器,请进行分配。

You can find a polyfill for Object.create here and one for Object.assign here.

您可以为对象找到一个polyfill。在这里创建一个对象。分配。

#10


0  

In addition to the accepted answer from 2009. If you can can target modern browsers, one can make use of the Object.defineProperty.

除了2009年接受的答案。如果您能够以现代浏览器为目标,就可以使用Object.defineProperty。

The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object. Source: Mozilla

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();

To see a desktop and mobile compatibility list, see Mozilla's Browser Compatibility list. Yes, IE9+ supports it as well as Safari mobile.

要查看桌面和移动兼容性列表,请参见Mozilla的浏览器兼容性列表。是的,IE9+和Safari手机都支持。

#11


0  

You can also try this

你也可以试试这个

    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  

A Pattern That Serves Me Well
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

First, you may change your preference of adding methods to the instance instead of the constructor's prototype object. I almost always declare methods inside of the constructor because I use Constructor Hijacking very often for purposes regarding Inheritance & Decorators.

首先,您可以更改向实例添加方法的首选项,而不是构造函数的prototype对象。我几乎总是在构造函数内部声明方法,因为我经常使用构造函数劫持用于继承和装饰器的目的。

Here's how I decide where which declarations are writ:

以下是我如何决定哪些声明是令状:

  • Never declare a method directly on the context object (this)
  • 不要直接在上下文对象上声明方法(以下)
  • Let var declarations take precedence over function declarations
  • 让var声明优先于函数声明
  • Let primitives take precedence over objects ({} and [])
  • 让原语优先于对象({}和[])
  • Let public declarations take precedence over private declarations
  • 让公共声明优先于私有声明
  • Prefer Function.prototype.bind over thus, self, vm, etc
  • 喜欢Function.prototype。因此绑定self、vm等
  • Avoid declaring a Class within another Class, unless:
    • It should be obvious that the two are inseparable
    • 这两者显然是密不可分的
    • The Inner class implements The Command Pattern
    • 内部类实现命令模式
    • The Inner class implements The Singleton Pattern
    • 内部类实现了单例模式。
    • The Inner class implements The State Pattern
    • 内部类实现状态模式
    • The Inner Class implements another Design Pattern that warrants this
    • 内部类实现了另一种设计模式来保证这一点
  • 避免在另一个类中声明一个类,除非:应该很明显,这两个类是不可分割的,内部类实现命令模式,内部类实现单例模式,内部类实现状态模式,内部类实现另一个设计模式
  • Always return this from within the Lexical Scope of the Closure Space.
  • 总是从闭包空间的词法范围内返回这个。

Here's why these help:

这就是为什么这些帮助:

Constructor Hijacking
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
Model Design
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
Design Patterns
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

As you can see, I really have no need for thus since I prefer Function.prototype.bind (or .call or .apply) over thus. In our Singleton class, we don't even name it thus because INSTANCE conveys more information. For Model, we return this so that we can invoke the Constructor using .call to return the instance we passed into it. Redundantly, we assigned it to the variable updated, though it is useful in other scenarios.

正如您所看到的,我确实不需要这样,因为我更喜欢Function.prototype。这样绑定(或.call或.apply)。在单例类中,我们甚至不这样命名,因为实例传递了更多的信息。对于Model,我们返回它,以便使用.call调用构造函数来返回传递给它的实例。冗余地,我们将它分配给更新的变量,尽管它在其他场景中很有用。

Alongside, I prefer constructing object-literals using the new keyword over {brackets}:

此外,我更喜欢使用new关键字而不是{括号}构造对象-文字:

Preferred
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
Not Preferred
var klass = Super.apply({
    override: x
});

As you can see, the latter has no ability to override its Superclass's "override" property.

如您所见,后者没有能力覆盖其超类的“override”属性。

If I do add methods to the Class's prototype object, I prefer an object literal -- with or without using the new keyword:

如果我向类的原型对象添加方法,我更喜欢对象文字——无论是否使用new关键字:

Preferred
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
Not Preferred
Klass.prototype.method = function m() {...};

#13


0  

I'd like to mention that we can use either a Title or a String to declare an Object.
There are different ways on calling each type of them. See below:

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

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'));

#14


-1  

Bascially there is no concept of class in JS so we use function as a class constructor which is relevant with the existing design patterns.

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

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

Till now JS has no clue that you want to create an object so here comes the new keyword.

到目前为止,JS还不知道要创建一个对象,因此这里出现了new关键字。

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

Ref : Professional JS for web developers - Nik Z

Ref:网络开发者的专业JS - Nik Z。

#15


-1  

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