Why in JavaScript is a function considered both a constructor and an object?

前端 未结 8 1261
半阙折子戏
半阙折子戏 2021-01-03 02:23

I have been doing a lot of research on this lately, but have yet to get a really good solid answer. I read somewhere that a new Function() object is created when the JavaScr

8条回答
  •  粉色の甜心
    2021-01-03 02:34

    There is nothing magical about functions and constructors. All objects in JavaScript are … well, objects. But some objects are more special than the others: namely built-in objects. The difference lies mostly in following aspects:

    1. General treatment of objects. Examples:
      • Numbers and Strings are immutable (⇒ constants). No methods are defined to change them internally — new objects are always produced as the result. While they have some innate methods, you cannot change them, or add new methods. Any attempts to do so will be ignored.
      • null and undefined are special objects. Any attempt to use a method on these objects or define new methods causes an exception.
    2. Applicable operators. JavaScript doesn't allow to (re)define operators, so we stuck with what's available.
      • Numbers have a special way with arithmetic operators: +, -, *, /.
      • Strings have a special way to handle the concatenation operator: +.
      • Functions have a special way to handle the "call" operator: (), and the new operator. The latter has the innate knowledge on how to use the prototype property of the constructor, construct an object with proper internal links to the prototype, and call the constructor function on it setting up this correctly.

    If you look into the ECMAScript standard (PDF) you will see that all these "extra" functionality is defined as methods and properties, but many of them are not available to programmers directly. Some of them will be exposed in the new revision of the standard ES3.1 (draft as of 15 Dec 2008: PDF). One property (__proto__) is already exposed in Firefox.

    Now we can answer your question directly. Yes, a function object has properties, and we can add/remove them at will:

    var fun = function(){/* ... */};
    fun.foo = 2;
    console.log(fun.foo);  // 2
    fun.bar = "Ha!";
    console.log(fun.bar);  // Ha!
    

    It really doesn't matter what the function actually does — it never comes to play because we don't call it! Now let's define it:

    fun = function(){ this.life = 42; };
    

    By itself it is not a constructor, it is a function that operates on its context. And we can easily provide it:

    var context = {ford: "perfect"};
    
    // now let's call our function on our context
    fun.call(context);
    
    // it didn't create new object, it modified the context:
    console.log(context.ford);           // perfect
    console.log(context.life);           // 42
    console.log(context instanceof fun); // false
    

    As you can see it added one more property to the already existing object.

    In order to use our function as a constructor we have to use the new operator:

    var baz = new fun();
    
    // new empty object was created, and fun() was executed on it:
    console.log(baz.life);           // 42
    console.log(baz instanceof fun); // true
    

    As you can see new made our function a constructor. Following actions were done by new:

    1. New empty object ({}) was created.
    2. Its internal prototype property was set to fun.prototype. In our case it will be an empty object ({}) because we didn't modify it in any way.
    3. fun() was called with this new object as a context.

    It is up to our function to modify the new object. Commonly it sets up properties of the object, but it can do whatever it likes.

    Fun trivia:

    • Because the constructor is just an object we can calculate it:

      var A = function(val){ this.a = val; };
      var B = function(val){ this.b = val; };
      var C = function(flag){ return flag ? A : B; };
      
      // now let's create an object:
      var x = new (C(true))(42);
      
      // what kind of object is that?
      console.log(x instanceof C); // false
      console.log(x instanceof B); // false
      console.log(x instanceof A); // true
      // it is of A
      
      // let's inspect it
      console.log(x.a); // 42
      console.log(x.b); // undefined
      
      // now let's create another object:
      var y = new (C(false))(33);
      
      // what kind of object is that?
      console.log(y instanceof C); // false
      console.log(y instanceof B); // true
      console.log(y instanceof A); // false
      // it is of B
      
      // let's inspect it
      console.log(y.a); // undefined
      console.log(y.b); // 33
      
      // cool, heh?
      
    • Constructor can return a value overriding the newly created object:

      var A = function(flag){
        if(flag){
          // let's return something completely different
          return {ford: "perfect"};
        }
        // let's modify the object
        this.life = 42;
      };
      
      // now let's create two objects:
      var x = new A(false);
      var y = new A(true);
      
      // let's inspect x
      console.log(x instanceof A); // true
      console.log(x.ford);         // undefined
      console.log(x.life);         // 42
      
      // let's inspect y
      console.log(y instanceof A); // false
      console.log(y.ford);         // perfect
      console.log(y.life);         // undefined
      

      As you can see x is of A with the prototype and all, while y is our "naked" object we returned from the constructor.

提交回复
热议问题