【问题标题】:simple javascript delegation using __proto__使用 __proto__ 的简单 javascript 委托
【发布时间】:2012-06-18 00:26:27
【问题描述】:

我习惯了 Java,所以当我尝试这样的事情时:

function subSection(pattern){
    this.total = 0;
    this.pattern = pattern;
    this.distance=0;
}

function enhancer(pattern){
    __proto__:subSection;
    this.pattern = pattern;
}

function silencer(pattern){
    __proto__:subSection;
    this.pattern = pattern;
}

var a = new enhancer("aaa");
document.write(a.distance)

我得到“未定义”。我以为我继承了总和距离数据成员

【问题讨论】:

  • 增强器和消音器功能完全相同吗?
  • 我很好奇...当您收到“未定义”响应时,您使用的是哪个浏览器?
  • @natlee75 它们代表其他东西,但具有相同的数据成员。所以稍后在我的代码中,每个都会有一个静态成员来计算实例的数量。这只是代码的一部分,浏览器是 chrom 16 & chromium 13

标签: javascript inheritance prototype


【解决方案1】:
function subSection(pattern){
    this.total = 0;
    this.pattern = pattern;
    this.distance=100;
}

function enhancer(pattern){
    this.__proto__=new subSection(pattern);
}

function silencer(pattern){
    this.__proto__=new subSection(pattern);
}

var a = new enhancer("aaa");
document.write(a.distance)​;

但这只是RobG 所说的Mozilla 专有财产。

DEMO.

更新:

function subSection(pattern){
    this.total = 0;
    this.pattern = pattern;
    this.distance=100;
}

function enhancer(pattern){
    function F(){};
    F.prototype = new subSection(pattern); // inherited subSection
    return new F();    
}

function silencer(pattern){
    function F(){};
    F.prototype = new subSection(pattern); // inherited subSection
    return new F();
}

var a = new enhancer("aaa");
document.write(a.distance)​;

DEMO.

有用的链接: herehere

【讨论】:

  • 正是我想要的
  • Webkit 浏览器也支持 proto 属性,Opera 也是如此。我猜这个问题发生在 Internet Explorer 中。 :-)
【解决方案2】:

不要使用__proto__,它是 Mozilla 的专有属性(被其他一些人复制)并且已被弃用。分配给 construtor.prototpye。

行:

> __proto__:subSection;

没有将任何东西分配给任何东西,它是一个标签 (__proto__),后跟一条语句 subSection,它是对未分配给任何东西的 subSection 函数的引用。

你好像不懂原型继承,试试 Douglas Crockford 的Prototypal Inheritance in JavaScript

以下可能与您正在尝试做的事情类似,这里肯定有一千个类似的问题:

function SubSection(pattern) {
    this.pattern = pattern;
    this.total = 0;
    this.distance=0;
}

// Add a method to subSection.prototype
SubSection.prototype.getDistance = function() {
  return this.distance;
}

function Enhancer(pattern) {
    this.pattern = pattern;
}

// To make enhancer inherit from subSection, make its prototype
// an instance of subSection
Enhancer.prototype = new SubSection();

var a = new Enhancer("aaa");

document.write(a.getDistance()); // 0

哦,按照惯例,构造函数名称以大写字母开头。

【讨论】:

  • 你在哪里调用 Enhancer.prototype = new SubSection();从?我应该把它放在我的代码中的什么地方,这样其他程序员才能得到它?
  • @Tom - 假设所有代码都在全局命名空间中,那么在哪里调用该行代码并不重要。在 JavaScript 中,函数声明被提升到其作用域的顶部,因此无论在何处声明 SubSection 和 Enhancer 函数,它们都将被 JavaScript 处理,就好像它们是在脚本的最开始处声明的一样。 Enhancer.prototype = new SubSection() 可以在函数声明之前、之后或之间调用。
  • 虽然函数声明在任何代码执行之前被处理并且你可以将它们放在同一个脚本块中的任何地方(或多或少),但最好将它们按顺序放置以便立即声明函数在原型修改的地方(根据上面的代码)。我发现这更具可读性和更容易理解。
【解决方案3】:

你应该为你的对象使用原型继承。我会使用你的增强器对象并像这样定义它:

    //create Enhancer object
function Enhancer(pattern){
    this.subSection(pattern);    
}

//extend Enhancer with object methods
Enhancer.prototype = {

   subSection: function(pattern){
         this.pattern = pattern;
         this.total = 0;        
         this.distance=0;
  }

};

这就是我在这段代码中要做的事情:

  1. 我正在用一个函数定义 Enhancer 对象。请注意,我将“Enhancer”大写以表示此数据结构的“对象”。
  2. 我将“模式”值传递给对象并使用它来调用 Enhancer.subSection();我可以调用 .subSection() 作为 Enhancer 对象的方法,因为我将它定义为使用 .prototype 的方法。

这是这种方法的有效fiddle example

【讨论】:

  • 谢谢克里斯!但这意味着在创建 10 个继承相同事物的类时会出现大量代码重复
  • 实际上 JavaScript 中的一切都是对象。数组、字符串、日期、数字和函数都是对象。 “Enhancer”的大写是一种约定,通常用于指示特定函数将充当返回该对象实例的构造函数。
  • @Tom - 如果 Enhancer.prototype.subSection 已分配给您的 subSection 函数,则此技术不需要任何代码重复。
【解决方案4】:

一般来说,像 totaldistance 这样的属性应该在 subSection 对象的每个实例中被初始化为完全相同的值,而应该在 prototype 而不是构造函数中定义。

对象的prototype 基本上是一个“模板”,该对象的每个实例都基于该模板。每个实例都有在prototype 上定义的每个属性和方法。

因此三个“类”(JavaScript 没有像 Java 等经典继承语言中定义的类,但有时习惯这些语言的人更容易使用该术语)应声明如下:

function subSection(pattern) {
    this.pattern = pattern;
}

function enhancer(pattern) {
    this.pattern = pattern;
}

function silencer(pattern) {
    this.pattern = pattern;
}

totaldistance 移出subSection 构造函数并移入subSection 原型:

subSection.prototype.total = 0;
subSection.prototype.distance = 0;

下一步是在subSectionenhancersilencer 之间设置原型继承,使用无操作函数作为它们之间的一种代理:

function fn() {}
fn.prototype = subSection.prototype;

enhancer.prototype = new fn();
silencer.prototype = new fn();

最后,将enhancersilencer 原型的constructor 属性设置为正确的对象,以便实例的constructor 属性引用正确的构造函数。如果我们不采取这一步,那么enhancersilencer 的任何实例都将错误地引用subSection 构造函数。

enhancer.prototype.constructor = enhancer;
silencer.prototype.constructor = silencer;

现在我们可以实例化enhancersilencer 对象,生成的实例将具有totaldistance 属性以及引用相应对象的constructor 属性。

var a, b;
a = new enhancer('aaaa');
b = new silencer('bbbb');
console.log(a.total + ', ' + a.distance); // 0, 0
console.log(b.total + ', ' + b.distance); // 0, 0
console.log(a.constructor); // (string representation of enhancer constructor)

我们在继承过程中使用fn 函数作为代理的原因是因为一个对象应该能够从另一个不一定具有相同的构造函数参数集的对象继承。

假设我们有以下两个构造函数:

function Person(name, age, hometown) {
    this.name = name;
    this.age = age;
    this.hometown = hometown;
}

function Employee(name, company, title) {
    this.name = name;
    this.company = company;
    this.title = title;
}

Employee 应该能够从Person 继承,即使它们不共享同一组构造函数参数。如果我们简单地将Employee.prototype 设置为等于Person 的新实例,则Employee 原型将具有三个未定义的属性nameagehometown

Employee.prototype = new Person; // name, age and hometown are undefined
var e = new Employee('John Smith', 'New York Times', 'editor'); // e's prototype has undefined properties name, age and hometown

通过使用无操作函数fn(我们几乎可以将它命名为任何我们想要的名称),我们最终为每个实例都提供了一个干净的原型,因为fn 作为构造函数不需要任何参数。

这不是必需的,但它是设置原型继承的一种更简洁的方法:没有太多理由将带有未定义属性的原型弄乱。

【讨论】:

    猜你喜欢
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多