Use of 'prototype' vs. 'this' in J

2018-12-30 22:31发布

What's the difference between

var A = function () {
    this.x = function () {
        //do something
    };
};

and

var A = function () { };
A.prototype.x = function () {
    //do something
};

14条回答
冷夜・残月
2楼-- · 2018-12-30 23:06

What's the difference? => A lot.

I think, the this version is used to enable encapsulation, i.e. data hiding. It helps to manipulate private variables.

Let us look at the following example:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

Now, the prototype structure can be applied as following:

Different adults have different ages, but all of the adults get the same rights.
So, we add it using prototype, rather than this.

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

Lets look at the implementation now.

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

Hope this helps.

查看更多
高级女魔头
3楼-- · 2018-12-30 23:08

Think about statically typed language, things on prototype are static and things on this are instance related.

查看更多
其实,你不懂
4楼-- · 2018-12-30 23:09

I believe that @Matthew Crumley is right. They are functionally, if not structurally, equivalent. If you use Firebug to look at the objects that are created using new, you can see that they are the same. However, my preference would be the following. I'm guessing that it just seems more like what I'm used to in C#/Java. That is, define the class, define the fields, constructor, and methods.

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

EDIT Didn't mean to imply that the scope of the variable was private, I was just trying to illustrate how I define my classes in javascript. Variable name has been changed to reflect this.

查看更多
查无此人
5楼-- · 2018-12-30 23:10

Let me give you a more comprehensive answer that I learned during a JavaScript training course.

Most answers mentioned the difference already, i.e. when prototyping the function is shared with all (future) instances. Whereas declaring the function in the class will create a copy for each instance.

In general there is no right or wrong, it's more a matter of taste or a design decision depending on your requirements. The prototype however is the technique that is used to develop in an object oriented manner, as I hope you'll see at the end of this answer.

You showed two patterns in your question. I will try to explain two more and try to explain the differences if relevant. Feel free to edit/extend. In all examples it is about a car object that has a location and can move.

Object Decorator pattern

Not sure if this pattern is still relevant nowadays, but it exists. And it is good to know about it. You simply pass an object and a property to the decorator function. The decorator returns the object with property and method.

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

Functional Classes

A function in JavaScript is a specialised object. In addition to being invoked, a function can store properties like any other object.

In this case Car is a function (also think object) that can be invoked as you are used to do. It has a property methods (which is an object with a move function). When Car is invoked the extend function is called, which does some magic, and extends the Car function (think object) with the methods defined within methods.

This example, though different, comes closest to the first example in the question.

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

Prototypal Classes

The first two patterns allow a discussion of using techniques to define shared methods or using methods that are defined inline in the body of the constructor. In both cases every instance has its own move function.

The prototypal pattern does not lend itself well to the same examination, because function sharing via a prototype delegation is the very goal for the prototypal pattern. As others pointed out, it is expected to have a better memory footprint.

However there is one point interesting to know: Every prototype object has has a convenience property constructor, which points back to the function (think object) it came attached to.

Concerning the last three lines:

In this example Car links to the prototype object, which links via constructor to Car itself, i.e. Car.prototype.constructor is Car itself. This allows you to figure out which constructor function built a certain object.

amy.constructor's lookup fails and thus is delegated to Car.prototype, which does have the constructor property. And so amy.constructor is Car.

Furthermore, amy is an instanceof Car. The instanceof operator works by seeing if the right operand's prototype object (Car) can be found anywhere in the left operand's prototype (amy) chain.

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

Some developers can be confused in the beginning. See below example:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

The instanceof operator returns false, because Dog's prototype cannot be found anywhere in fido's prototype chain. fido is a simple object that is created with an object literal, i.e. it just delegates to Object.prototype.

Pseudoclassical patterns

This is really just another form of the prototypal pattern in simplified form and more familiar to do those who program in Java for example, since it uses the new constructor.

It does the same as in the prototypal pattern really, it is just syntactic sugar overtop of the prototypal pattern.

However, the primary difference is that there are optimizations implemented in JavaScript engines that only apply when using the pseudoclassical pattern. Think of the pseudoclassical pattern a probably faster version of the prototypal pattern; the object relations in both examples are the same.

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

Finally, it should not be too difficult to realize how object oriented programming can be done. There are two sections.

One section that defines common properties/methods in the prototype (chain).

And another section where you put the definitions that distinguish the objects from each other (loc variable in the examples).

This is what allows us to apply concepts like superclass or subclass in JavaScript.

Feel free to add or edit. Once more complete I could make this a community wiki maybe.

查看更多
其实,你不懂
6楼-- · 2018-12-30 23:14

As others have said the first version, using "this" results in every instance of the class A having its own independent copy of function method "x". Whereas using "prototype" will mean that each instance of class A will use the same copy of method "x".

Here is some code to show this subtle difference:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

As others have mentioned, there are various reasons to choose one method or the other. My sample is just meant to clearly demonstrate the difference.

查看更多
忆尘夕之涩
7楼-- · 2018-12-30 23:15

I know this has been answered to death but I'd like to show an actual example of speed differences.

Function directly on object

Function on prototype

Here we're creating 2,000,000 new objects with a print method in Chrome. We're storing every object in an array. Putting print on the prototype takes about 1/2 as long.

查看更多
登录 后发表回答