Use of 'prototype' vs. 'this' in JavaScript?

What's the difference between

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

and

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

The examples have very different outcomes.

Before looking at the differences, the following should be noted:

  • A constructor's prototype provides a way to share methods and values among instances via the instance's private [[Prototype]] property.
  • A function's this is set by how the function is called or by the use of bind (not discussed here). Where a function is called on an object (eg myObj.method() ) then this within the method references the object. Where this is not set by the call or by the use of bind, it defaults to the global object (window in a browser) or in strict mode, remains undefined.
  • JavaScript is a object oriented language, ie everything is an Object, including functions.
  • So here are the snippets in question:

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

    In this case, variable A is assigned a value that is a reference to a function. When that function is called using A() , the function's this isn't set by the call so it defaults to the global object and the expression this.x is effectively window.x . The result is that a reference to the function expression on the right hand side is assigned to window.x .

    In the case of:

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

    something very different occurs. In the first line, variable A is assigned a reference to a function. In JavaScript, all functions objects have a prototype property by default so there is no separate code to create an A.prototype object.

    In the second line, A.prototype.x is assigned a reference to a function. This will create an x property if it doesn't exist, or assign a new value if it does. So the difference with the first example is which object's x property is involved in the expression.

    Another example is below. It's similar to the first one (and may be what you meant to ask about):

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

    In this example, the new operator has been added before the function expression so that the function is called as a constructor. When called with new , the function's this is set to reference a new Object whose private [[Prototype]] property is set to reference the constructor's public prototype. So in the assignment statement, the x property will be created on this new object. When called as a constructor, a function returns its this object by default, so there is no need for a separate return this; statement.

    To check that A has an x property:

    console.log(A.x) // function () {
                     //   //do something
                     // };
    

    This is an uncommon use of new, since the only way to reference the constructor is via A.constructor. It would be much more common to do:

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

    Another way of achieving a similar result is to use an immediately invoked function expression:

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

    In this case, A assigned the return value of calling the function on the right hand side. Here again, since this is not set in the call, it will reference the global object and this.x is effectively window.x . Since the function doesn't return anything, A will have a value of undefined .

    These differences between the two approaches also manifest if you're serializing and de-serializing your Javascript objects to/from JSON. Methods defined on an object's prototype are not serialized when you serialize the object, which can be convenient when for example you want to serialize just the data portions of an object, but not it's methods:

    var A = function () { 
        this.objectsOwnProperties = "are serialized";
    };
    A.prototype.prototypeProperties = "are NOT serialized";
    var instance = new A();
    console.log(instance.prototypeProperties); // "are NOT serialized"
    console.log(JSON.stringify(instance)); 
    // {"objectsOwnProperties":"are serialized"} 
    

    Related questions :

  • What does it mean that JavaScript is a prototypal language?
  • What is the scope of a function in JavaScript?
  • How does the "this" keyword work?
  • Sidenote: There may not be any significant memory savings between the two approaches, however using the prototype to share methods and properties will likely use less memory than each instance having its own copy.

    JavaScript isn't a low-level language. It may not be very valuable to think of prototyping or other inheritance patterns as a way to explicitly change the way memory is allocated.


    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.


    Take these 2 examples:

    var A = function() { this.hey = function() { alert('from A') } };
    

    vs.

    var A = function() {}
    A.prototype.hey = function() { alert('from prototype') };
    

    Most people here (especially the top-rated answers) tried to explain how they are different without explaining WHY. I think this is wrong and if you understand the fundamentals first, the difference will become obvious. Let's try to explain the fundamentals first...

    a) A function is an object in JavaScript. EVERY object in JavaScript gets an internal property (meaning, you can't access it like other properties, except maybe in browsers like Chrome), often referred to as __proto__ (you can actually type anyObject.__proto__ in Chrome to see what it references. This is just that, a property, nothing more. A property in JavaScript = a variable inside an object, nothing more. What do variables do? They point to things.

    So what does this __proto__ property points to? Well, usually another object (we'll explain why later). The only way to force JavaScript for the __proto__ property to NOT point to another object is to use var newObj = Object.create(null) . Even if you do this, the __proto__ property STILL exists as a property of the object, just it doesn't point to another object, it points to null .

    Here's where most people get confused:

    When you create a new function in JavaScript (which is an object as well, remember?), the moment it is defined, JavaScript automatically creates a new property on that function called prototype . Try it:

    var A = [];
    A.prototype // undefined
    A = function() {}
    A.prototype // {} // got created when function() {} was defined
    

    A.prototype is TOTALLY DIFFERENT from the __proto__ property. In our example, 'A' now has TWO properties called 'prototype' and __proto__ . This is a big confusion for people. prototype and __proto__ properties are in no way related, they're separate things pointing to separate values.

    You may wonder: Why does JavaScript has __proto__ property created on every single object? Well, one word: delegation . When you call a property on an object and the object doesn't have it, then JavaScript looks for the object referenced by __proto__ to see if it maybe has it. If it doesn't have it, then it looks at that object's __proto__ property and so on...until the chain ends. Thus the name prototype chain . Of course, if __proto__ doesn't point to an object and instead points to null , well tough luck, JavaScript realizes that and will return you undefined for the property.

    You may also wonder, why does JavaScript creates a property called prototype for a function when you define the function? Because it tries to fool you, yes fool you that it works like class-based languages.

    Let's go on with our example and create an "object" out of A :

    var a1 = new A();
    

    There's something happening in the background when this thing happened. a1 is an ordinary variable which was assigned a new, empty object.

    The fact that you used the operator new before a function invocation A() did something ADDITIONAL in the background. The new keyword created a new object which now references a1 and that object is empty. Here's what happening additionally:

    We said that on each function definition there's a new property created called prototype (which you can access it, unlike with the __proto__ property) created? Well, that property is being used now.

    So we're now at the point where we have a freshly baked empty a1 object. We said that all objects in JavaScript have an internal __proto__ property which points to something ( a1 also has it), whether it's null or another object. What the new operator does is that it sets that __proto__ property to point to the function's prototype property. Read that again. It's basically this:

    a1.__proto__ = A.prototype;
    

    We said that A.prototype is nothing more than an empty object (unless we change it to something else before defining a1 ). So now basically a1.__proto__ points to the same thing A.prototype points to, which is that empty object. They both point to the same object which was created when this line happened:

    A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}
    

    Now, there's another thing happening when var a1 = new A() statement is processed. Basically A() is executed and if A is something like this:

    var A = function() { this.hey = function() { alert('from A') } };
    

    All that stuff inside function() { } is going to execute. When you reach the this.hey.. line, this is changed to a1 and you get this:

    a1.hey = function() { alert('from A') }
    

    I won't cover why this changes to a1 but this is a great answer to learn more.

    So to summarize, when you do ``var a1 = new A()` there are 3 things happening in the background:

  • A totally new empty object is created and assigned to a1 . a1 = {}
  • a1.__proto__ property is assigned to point at the same thing as A.prototype points to (another empty object {} )

  • The function A() is being executed with this set to the new, empty object created in step 1 (read the answer I referenced above as to why this changes to a1 )

  • Now, let's try to create another object:

    var a2 = new A();
    

    Steps 1,2,3 will repeat. Do you notice something? The key word is repeat. Step 1: a2 will be a new empty object, step 2: its __proto__ property will point to the same thing A.prototype points to and most importantly, step 3: function A() is AGAIN executed, which means that a2 will get hey property containing a function. a1 and a2 have two SEPARATE properties named hey which point to 2 SEPARATE functions! We now have duplicate functions in same two different objects doing the same thing, oops...You can imagine the memory implications of this if we have 1000 objects created with new A , after all functions declarations take more memory than something like the number 2. So how do we prevent this?

    Remember why the __proto__ property exists on every object? So that if you retrieve the yoMan property on a1 (which doesn't exist), its __proto__ property will be consulted, which if it's an object (and is most cases it is), it will check if it contains yoMan , and if it doesn't, it will consult that object's __proto__ etc. If it does, it will take that property value and display it to you.

    So someone decided to use this fact + the fact that when you create a1 , its __proto__ property points to the same (empty) object A.prototype points to and do this:

    var A = function() {}
    A.prototype.hey = function() { alert('from prototype') };
    

    Cool! Now, when you create a1 , it again goes through all of the 3 steps above, and in step 3, it doesn't do anything, since function A() has nothing to execute. And if we do:

    a1.hey
    

    It will see that a1 does not contain hey and it will check its __proto__ property object to see if it has it, which is the case.

    With this approach we eliminate the part from step 3 where functions are duplicated on each new object creation. Instead of a1 and a2 having a separate hey property, now NONE of them has it. Which, I guess, you figured out yourself by now. That's the nice thing...if you understand __proto__ and Function.prototype , questions like these will be pretty obvious.

    NOTE: Some people tend to not call the internal Prototype property as __proto__ , I've used this name through the post to distinguish it clearly to the Functional.prototype property as two different things.

    链接地址: http://www.djcxy.com/p/30050.html

    上一篇: 了解JavaScript中的原型继承

    下一篇: 在JavaScript中使用'原型'与'这个'?