原型或内联,有什么区别?

Jon*_*den 9 javascript

我只是在学习Javascript,我很想知道,正在使用原型声明,如下所示:

function TSomeObj()
{
  this.name="my object";
}

TSomeObj.prototype.showname = function() {
  alert(this.name);
}
Run Code Online (Sandbox Code Playgroud)

基本上像这样做:

function TSomeObj()
{
  this.name="my object";
  this.showname = function() {
    alert(this.name);
  }
}
Run Code Online (Sandbox Code Playgroud)

当我转储对象的属性时,我得到相同的结果:

TSomeObj (inline version) =
{ 
    'name': 'my object',
    'test': function
}

TSomeObj (prototype declaration) =
{ 
    'name': 'my object',
    'test': function
}
Run Code Online (Sandbox Code Playgroud)

使用原型声明的好处是什么?除了更少的混乱和更有序的源代码或许.

更新:我或许应该使人们更清楚,这是最终的结果,我很好奇.最终结果是相同的(即在对象原型中都注册了一个新函数) - 但是他们这样做的方式却截然不同.感谢您的所有回复和信息!

Eri*_*ikE 22

我最初回答了错误的问题.以下是您实际问的问题的答案.我会留下我的其他笔记,以防它们对某人有帮助.

在构造函数中通过向对象添加属性与在this.prop外部执行此操作不同Object.prototype.prop.

  1. 最重要的区别是,当您向函数原型添加属性并从中实例化新对象时,通过加强继承链而不是直接在对象上来访问新对象中的属性.

    var baseobj = {};
    function ObjType1() {
       this.prop = 2;
    }
    function ObjType2() {}
    ObjType1.prototype = baseobj;
    ObjType2.prototype = baseobj; // these now have the *same* prototype object.
    ObjType1.prototype.prop = 1;
    // identical to `baseobj.prop = 1` -- we're modifying the prototype
    
    var a = new ObjType1(),
      b = new ObjType2();
    //a.hasOwnProperty('prop') : true
    //b.hasOwnProperty('prop') : false -- it has no local property "prop"
    //a: { prop = 2 }, b : { prop = 1 } -- b's "prop" comes from the inheritance chain
    
    baseobj.prop = 3;
    //b's value changed because we changed the prototype
    //a: { prop = 2 }, b : { prop = 3 }
    
    delete a.prop;
    //a is now reflecting the prototype's "prop" instead of its own:
    //a: { prop = 3 }, b : { prop = 3 }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 第二个区别是,当代码执行时,向原型添加属性会发生一次,但每次创建新对象时都会向构造函数内的对象添加属性.这意味着使用原型可以更好地执行并使用更少的内存,因为在叶子/邻近对象上设置相同的属性之前不需要新的存储.

  3. 另一个区别是内部添加的函数可以访问私有变量和函数(在构造函数中声明的函数var),而基于原型或外部添加的函数则不会,因为它们具有错误的范围:

    function Obj(initialx, initialy) {
       var x = initialx,
          y = initialy;
       this.getX = function() {
          return x;
       }
       var twoX = function() { // identical to `function twoX() { ... }`
          return x * 2;
       }
       this.getTwoX = function() {
          return twoX();
       }
    }
    
    Obj.prototype.getY = function() {
       return y; // fails, even if you try `this.y`
    }
    Obj.prototype.twoY = function() {
       return y * 2; // fails
    }
    Obj.prototype.getTwoY = function() {
       return twoY(); // fails
    }
    
    var obj = new Obj();
    // obj.y : fails, you can't access "y", it is internal
    // obj.twoX() : fails, you can't access "twoX", it is internal
    // obj.getTwoX() : works, it is "public" but has access to the twoX function
    
    Run Code Online (Sandbox Code Playgroud)

关于javascript对象,函数和继承的一般说明

  1. javascript中的所有非标量变量都是对象.(并且一些明显的非对象类型在使用方法时进行装箱,例如布尔(Booleans)).它们都有点像哈希/字典,因为它们具有可以分配给它们的无限(?)个键/值对.

  2. 每个对象都有一个"原型"的继承链,它一直到基础对象.当您访问对象的属性时,如果该对象本身不存在该属性,则检查该对象的秘密原型,如果不存在,则检查该对象的原型,依此类推等等.有些浏览器通过属性公开这个原型__proto__.常规对象没有prototype属性,因为此属性用于函数,用于存储使用该函数作为其构造函数创建的任何新对象的原型的对象.

  3. javascript函数是对象的特殊情况,除了具有对象的键/值对之外,还具有参数和按顺序执行的一系列语句.

  4. 每次调用一个函数对象时,它都会与另一个通过关键字从函数内访问的对象配对this.通常,this对象是函数属性的对象.例如,''.replace()将字符串文字装入a String,然后在replace函数内部,这将引用该对象.另一个例子是当一个函数附加到DOM元素(可能是按钮上的onclick函数)时,则this引用DOM元素.您可以this使用apply或手动选择配对对象call.

  5. 当使用new关键字调用javascript函数时var obj = new Obj(),这会导致特殊事情发生.如果你不明确返回任何东西,然后,而不是obj将现包含返回值的的Obj功能,它包含了这个对象,将其用在调用时函数配对,这将是在其继承的第一个父一个新的空对象链设置为Obj.prototype(记住,在某些浏览器中可访问obj.__proto__).调用的Obj()函数在运行时可以修改新对象的属性.然后返回该对象.

  6. 你不必太担心关键字constructor,只需要说明obj.constructor指向Obj函数(所以你可以找到创建它的东西),但你可能不需要在大多数情况下使用它.

回到你的问题.要理解在构造函数中修改对象属性和修改其原型之间的区别,请尝试以下方法:

var baseobj = {prop1: 'x'};
function TSomeObj() {
   this.prop2 = 'y';
};
TSomeObj.prototype = baseobj;
var a = new TSomeObj();
//now dump the properties of `a`
a.prop1 = 'z';
baseobj.prop1 = 'w';
baseobj.prop2 = 'q';
//dump properties of `a` again
delete a.prop1;
//dump properties of `a` again
Run Code Online (Sandbox Code Playgroud)

您将看到该设置a.prop1实际上是创建邻近对象的新属性,但它不会覆盖基础对象的prop1.prop1从中删除a后,您将获得prop1我们更改的继承.此外,即使我们添加了prop2after之后a,a仍然具有该属性.这是因为javascript使用原型继承而不是经典继承.修改原型时,TSomeObj还要修改所有以前实例化的对象,因为它们正在从中继承.

当您以任何编程语言实例化一个类时,新对象将采用其"构造函数"类的属性(我们通常将其视为对象的同义词).在大多数编程语言中,除了停止程序和更改类声明之外,您无法更改类或实例化对象的属性或方法.

但是,Javascript允许您在运行时修改对象 "类" 的属性,并且还修改该类型类的所有实例化对象,除非它们具有覆盖修改的属性.对象可以生成可以生成对象的对象,因此这可以在一直到Object类的链中工作.我把"类"放在引号中,因为实际上,Javascript中的所有内容都是一个对象,并且实际上不存在类这样的东西,除了new关键字允许你创建带有继承链的新对象,所以我们调用它们是类,即使它们只是使用new关键字调用构造函数的结果.

其他一些注意事项:函数有一个Function构造函数,对象有一个Object构造函数.Function构造函数的原型是(惊讶,惊讶)Object.

从没有运行构造函数的对象继承

在某些情况下,能够在没有运行构造函数的情况下创建新的"对象实例"是有用的.您可以从类继承而不像这样继承类的构造函数(几乎像手动操作child.__proto__ = parent):

function inheritFrom(Class) {
   function F() {};
   F.prototype = Class.prototype;
   return new F();
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ahn 7

接受的答案错过了原型和绑定到特定对象的方法之间最重要的区别,所以我要澄清一下

  • Prototype'd函数只被声明一次.附加功能使用

    this.method = function(){}
    
    Run Code Online (Sandbox Code Playgroud)

    每当您创建类的实例时,都会一次又一次地重新声明.因此,原型通常是将函数附加到类的首选方法,因为它们使用较少的内存,因为该类的每个实例都使用相同的函数.然而,正如Erik指出的那样,使用原型附加到附加到特定对象的函数具有不同的范围,因此原型不能访问函数构造函数中定义的"私有"变量.

  • 至于原型究竟是什么,因为它是来自传统OO语言的奇怪概念:

    • 每当您创建函数的新实例时:

      var obj = new Foo();
      
      Run Code Online (Sandbox Code Playgroud)

      运行以下逻辑(不是字面上的代码,但类似的东西):

      var inheritsFrom = Foo,
        objectInstance = {};
      
      objectInstance.__proto__ = inheritsFrom.prototype;
      
      inheritsFrom.apply( objectInstance, arguments );
      
      return objectInstance;
      
      Run Code Online (Sandbox Code Playgroud)

      所以:

      • 创建一个新对象{},以表示该函数的新实例
      • 该函数的原型被复制到__proto__新对象.请注意,这是一个复制的引用,所以Foo.prototypeobjectInstance.__proto__现在指的是在一个人造物体和变化可以在其它立即可见.
      • 调用该函数时,将this在函数中设置此新对象
    • 每当您尝试访问函数或属性时,例如:obj.bar(),运行以下逻辑:

      if( obj.hasOwnProperty('bar') ) {
          // use obj.bar
      } else if( obj.__proto__ ){
          var proto = obj.__proto__;
          while(proto){
              if( proto.hasOwnProperty('bar') ){
                  // use proto.bar;
              }
      
              proto = proto.__proto__;
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      换句话说,检查以下内容:

      obj.bar
      obj.__proto__.bar
      obj.__proto__.__proto__.bar
      obj.__proto__.__proto__.__proto__.bar
      ... etc
      
      Run Code Online (Sandbox Code Playgroud)

      直到__proto__最终等于null因为你已经到达原型链的末尾.

      __proto__现在有很多浏览器实际暴露,因此您可以在Firebug或Chrome/Safari中的控制台中进行检查.IE没有暴露它(并且在内部可能有很多不同的名称).