简体   繁体   English

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

[英]How to “properly” create a custom object in JavaScript?

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. 我看过一些示例,该人员先使用var self = this ,然后再使用self. in all functions to make sure the scope is always correct. 在所有功能中确保范围始终正确。

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对象的正确示例吗?

There are two models for implementing classes and instances in JavaScript: the prototyping way, and the closure way. 有两种用于在JavaScript中实现类和实例的模型:原型方式和闭包方式。 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. 许多程序员和库使用不同的方法和类处理实用程序功能来覆盖该语言的某些较丑陋的部分。

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. 更糟糕的是,大多数JavaScript教程材料都很糟糕,并且在某种程度上折衷以覆盖所有基础,使您非常困惑。 (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的对象模型与大多数编程语言有很大不同,并且在许多地方都设计得不好。)

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: 我们可以向new Shape创建的实例中添加方法,方法是将其写入此构造函数的prototype查找中:

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. 现在可以对其进行子类化,您最多可以调用JavaScript进行子类化的内容。 We do that by completely replacing that weird magic prototype property: 为此,我们完全替换了怪异的魔术prototype属性:

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. 但是,伙计, new Shape()很丑陋:即使没有创建实际的Shape,我们也正在实例化基类。 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 . 由于JavaScript非常草率,因此它可以工作在这种简单情况下:它允许传入零个参数,在这种情况下, xy变为undefined ,并分配给原型的this.xthis.y If the constructor function were doing anything more complicated, it would fall flat on its face. 如果构造函数执行任何更复杂的操作,则它的外观会平坦。

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. 而不是new Shape()错误。 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.subclass ,将其舍入并让基类的Function吐出子类:

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. 也许您想让makeSubclass记住并记住一个类名,并使用它提供默认的toString 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): 也许您想让构造函数检测在没有new运算符的情况下意外调用了它(否则通常会导致非常烦人的调试):

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. 也许您想传递所有新成员并将makeSubclass添加到原型中,以免您不得不编写Class.prototype... A lot of class systems do that, eg: 许多类系统都这样做,例如:

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. 通过完全不使用继承,避免了JavaScript基于原型的继承问题。 Instead: 代替:

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; [而且因为这里没有继承,所以instanceof运算符将无法工作; 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的结合不可思议的方式有用的this在方法调用,其中有结果,如果你从脱离其所有者的方法:

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). 那么this方法内部的对象将不是预期的Circle实例(它实际上是全局window对象,从而导致广泛的调试麻烦)。 In reality this typically happens when a method is taken and assigned to a setTimeout , onclick or EventListener in general. 实际上,通常在采用方法并将其分配给setTimeoutonclickEventListener时通常会发生这种情况。

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() : 或者,将来(或现在,如果您破解Function.prototype),也可以使用function.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). 如果您的实例是通过闭包方式完成的,则绑定是通过实例变量的闭包免费完成的(通常称为thatself ,但我个人不建议后者,因为self在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. 但是,您没有在上面的代码段中免费获取参数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: 您可能更愿意忽略this完全,创建一个新的that和返回它,而不是使用new操作:

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部分。]

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对象-您在其中定义私有属性,并将公共属性附加this变量。

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

Douglas Crockford discusses that topic extensively in The Good Parts . 道格拉斯·克罗克福德(Douglas Crockford)《好零件》中广泛讨论了该主题。 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. 在Javascript中,函数是对象,可用于与new运算符一起构造对象。 By convention, functions intended to be used as constructors start with a capital letter. 按照约定,打算用作构造函数的函数以大写字母开头。 You often see things like: 您经常会看到类似的东西:

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. 如果您在实例化一个新对象时忘记使用new运算符,则得到的是一个普通的函数调用, 调用绑定到全局对象而不是新对象。

To continue off of bobince's answer 继续bobince的答案

In es6 you can now actually create a class 在es6中,您现在可以实际创建一个class

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

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

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"

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. 当在构造函数调用期间使用关闭“ this”的技巧时,是为了编写一个函数,该函数可以被其他不想调用对象方法的对象用作回调。 It's not related to "making the scope correct". 它与“使范围正确”无关。

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. 阅读Douglas Crockford对JavaScript的评价,可能会使您受益匪浅 John Resig is also brilliant. 约翰·雷西格John Resig )也很聪明。 Good luck! 祝好运!

Closure is versatile. Closure功能多样。 bobince has well summarized the prototype vs. closure approaches when creating objects. 在创建对象时, bobince很好地总结了原型方法与封闭方法。 However you can mimic some aspects of OOP using closure in a functional programming way. 但是,您可以通过函数式编程方式使用闭包来模仿OOP某些方面。 Remember functions are objects in JavaScript ; 记住函数是JavaScript中的对象 ; so use function as object in a different way. 因此以不同的方式将函数用作对象。

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. 前一段时间,我碰到了Mozilla关于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. 这是我第一次阅读闭包与经典OOP之间的并行性,而没有引用原型。

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): 在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 . 基本上,您将VAT值传递给构造函数,然后您的calculate方法可以通过闭包对其进行操作。 Now instead of using a class/constructor, pass your VAT as an argument into a function. 现在,不使用类/构造函数,而是将VAT作为参数传递给函数。 Because the only stuff you are interested in is the calculation itself, returns a new function, which is the calculate method: 因为您唯一感兴趣的是计算本身,所以返回一个新函数,即calculate方法:

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. 无需创建传统对象。

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

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. 这使您可以创建共享同一“蓝图”的多个对象,类似于您在eg中使用类的方式。 Java. 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. 两者之间的主要区别在于,使用Object.create ,不会运行A的构造函数,这更直观,更类似于基于类的继承。

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 : 您可以随时选择可以选择运行的构造A创造的新实例时, B通过增加其添加到的构造B

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的构造函数链中,可以将Object.createObject.assign结合使用:

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.create可以在包括IE9 +在内的所有现代浏览器中安全使用。 Object.assign does not work in any version of IE nor some mobile browsers. Object.assign在任何版本的IE或某些移动浏览器中均不起作用。 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. 如果您要使用Object.create和/或Object.assign并支持未实现它们的浏览器,建议使用Polyfill

You can find a polyfill for Object.create here and one for Object.assign here . 你可以找到一个填充工具Object.create 在这里的,一个Object.assign 这里

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

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. Object.defineProperty()方法直接在对象上定义新属性,或修改对象上的现有属性,然后返回该对象。 Source: Mozilla 资料来源: 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 . 要查看桌面和移动设备兼容性列表,请参见Mozilla的浏览器兼容性列表 Yes, IE9+ supports it as well as Safari mobile. 是的,IE9 +和Safari移动版都支持它。

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();
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 . 首先,您可以更改向实例添加方法而不是向构造函数的prototype对象添加方法的首选项。 I almost always declare methods inside of the constructor because I use Constructor Hijacking very often for purposes regarding Inheritance & Decorators. 我几乎总是在构造函数内部声明方法,因为出于与继承和装饰器有关的目的,我经常使用构造函数劫持

Here's how I decide where which declarations are writ: 这是我决定将哪些声明写在何处的方法:

  • Never declare a method directly on the context object ( this ) 切勿直接在上下文对象上声明方法( this
  • Let var declarations take precedence over function declarations var声明优先于function声明
  • Let primitives take precedence over objects ( {} and [] ) 让基元优先于对象( {}[]
  • Let public declarations take precedence over private declarations public声明优先于private声明
  • Prefer Function.prototype.bind over thus , self , vm , etc thus优先选择Function.prototype.bindselfvm etc
  • 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. 始终从封闭空间的词法范围内返回this

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 设计模式
Klass.prototype.method = function m() {...};

As you can see, I really have no need for thus since I prefer Function.prototype.bind (or .call or .apply ) over thus . 正如你所看到的,我实在是没有必要thus ,因为我喜欢Function.prototype.bind (或.call.apply )以上thus In our Singleton class, we don't even name it thus because INSTANCE conveys more information. 在我们Singleton类,我们甚至不命名它thus ,因为INSTANCE传达更多的信息。 For Model , we return this so that we can invoke the Constructor using .call to return the instance we passed into it. 对于Model ,我们返回this以便我们可以使用.call调用构造方法以返回传递给它的实例。 Redundantly, we assigned it to the variable updated , though it is useful in other scenarios. 冗余,我们赋予它的变量updated ,但它是在其他情况下非常有用。

Alongside, I prefer constructing object-literals using the new keyword over {brackets}: 另外,相对于{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: 如果确实将方法添加到Class的prototype对象,则我更喜欢使用对象文字-使用或不使用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() {...}; 

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

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 参考:面向Web开发人员的专业JS-Nik Z

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM