使用$ .extend和模块模式的简单javascript继承 [英] Simple javascript inheritance using $.extend and module pattern

查看:98
本文介绍了使用$ .extend和模块模式的简单javascript继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道几年后人们如何看待使用模块模式 - esque构造函数模式继承而没有正常的原型继承。为什么程序员不为非单例js类使用模块模式?对我来说,优点是:

I have wondered for a couple years now what people think of doing inheritance with a module-pattern-esque constructor pattern and WITHOUT normal prototypal inheritance. Why do programmers not use a module pattern for non-singleton js classes? For me the advantages are:


  • 非常清晰的公共和私人范围(易于理解代码和api)

  • 无需在回调中通过$ .proxy(fn,this)跟踪'this'指针

  • 不再使用事件处理程序等等var = this等每当我看到'this'时,我知道它是传递给回调的上下文,它不是我跟踪的东西,我知道我的对象实例。

缺点:


  • 小的性能退化

  • 风险可能摇摆不定手指来自Doug Crockford?

考虑一下(只需在任何js控制台中运行)

Consider this (just run in any js console)

var Animal = function () {
    var publicApi = {
        Name: 'Generic',
        IsAnimal: true,
        AnimalHello: animalHello,
        GetHelloCount:getHelloCount
    };

    var helloCount = 0;

    function animalHello() {
        helloCount++;
        console.log(publicApi.Name + ' says hello (animalHello)');
    }

    function getHelloCount(callback) {
        callback.call(helloCount);
    }

    return publicApi;
};

var Sheep = function (name) {
    var publicApi = {
        Name: name || 'Woolie',
        IsSheep: true,
        SheepHello: sheepHello
    };

    function sheepHello() {
        publicApi.AnimalHello();
        publicApi.GetHelloCount(function() {
            console.log('i (' + publicApi.Name + ') have said hello ' + this + ' times (sheepHello anon callback)');
        });
    }

    publicApi = $.extend(new Animal(), publicApi);
    return publicApi;
};

var sheepie = new Sheep('Sheepie');
var lambie = new Sheep('Lambie');

sheepie.AnimalHello();
sheepie.SheepHello();
lambie.SheepHello();

我的问题是这种方法的缺点是什么,我没有看到?这是一个好方法吗?

My question is what are the drawbacks to this approach that I am not seeing? Is this a good approach?

谢谢!

[更新]

感谢您的回复。希望我能给予每个人赏金。这就是我在寻找的东西。基本上我的想法。我绝不会使用模块模式来构造多个实例。通常只有一对。我认为它有其优点的原因是你看到的任何小的性能退化都会在编码体验的简单性中重新获得。这些天我们写了很多代码。我们还必须重用其他人的代码,我个人感谢有人花时间创造一个漂亮优雅的模式,而不是在有意义的时候教条地坚持原型继承。

Thanks for the great responses. Wish I could give everyone the bounty. It was what I was looking for. Basically what I thought. I would never use module pattern to construct more than a few instances of something. Usually only a couple. The reason I think it has its advantages is whatever small perf degradation you see is recaptured in the simplicity of the coding experience. We have a LOT of code to write these days. We also have to reuse other peoples' code and personally I appreciate when someone has taken the time to create a nice elegant pattern rather than dogmatically adhering to prototypal inheritance when it makes sense.

推荐答案

我认为归结为性能问题。您提到性能下降很小,但这实际上取决于应用程序的规模(2只绵羊对1000只绵羊)。 不应忽视原型继承,我们可以使用功能和原型继承的混合来创建有效的模块模式。

I think it boils down to the issue of performance. You mentioned that there is small performance degradation, but this really depends on scale of the application(2 sheep vs 1000 sheep). Prototypal inheritance should not be ignored and we can create an effective module pattern using a mix of functional and prototypal inheritance.

JS - 为何使用Prototype?中所述,
之一原型的优点是你只需初始化原型成员一次,
而构造函数中的成员是为每个实例创建的

实际上,您可以直接访问原型而无需创建新对象。

As mentioned in the post JS - Why use Prototype?, one of the beauties of prototype is that you only need to initialize the prototypal members only once, whereas members within the constructor are created for each instance. In fact, you can access prototype directly without creating a new object.

Array.prototype.reverse.call([1,2,3,4]);
//=> [4,3,2,1]

function add() {
    //convert arguments into array
    var arr = Array.prototype.slice.call(arguments),
        sum = 0;
    for(var i = 0; i < arr.length; i++) {
        sum += arr[i];
    }

    return sum;
}

add(1,2,3,4,5);
//=> 15

在你的函数中,创建一个全新的
动物和羊有额外的开销每次调用构造函数时。一些成员如Animal.name是用每个实例创建的,但我们知道
,Animal.name是静态的,所以最好将它实例化一次。由于您的代码暗示Animal.name
应该在所有动物中相同,因此只需更新Animal.prototype.name就可以轻松更新所有实例的Animal.name,如果我们将
移动到原型。

In your functions, there is extra overhead to create a completely new Animal and sheep each time a constructor is invoked. Some members such as Animal.name are created with each instance, but we know that Animal.name is static so it would be better to instantiate it once. Since your code implies that Animal.name should be the same across all animals, it is easy to update Animal.name for all instance simply by updating Animal.prototype.name if we moved it to the prototype.

考虑这个

var animals = [];
for(var i = 0; i < 1000; i++) {
    animals.push(new Animal());
}



功能继承/模块模式



Functional inheritance/Module Pattern

function Animal() {

    return {
      name : 'Generic',
      updateName : function(name) {
          this.name = name;
      }
   }

}


//update all animal names which should be the same
for(var i = 0;i < animals.length; i++) {
    animals[i].updateName('NewName'); //1000 invocations !
}



vs。原型



vs. Prototype

Animal.prototype = {
name: 'Generic',
updateName : function(name) {
   this.name = name
};
//update all animal names which should be the same
Animal.prototype.updateName('NewName'); //executed only once :)

如上图所示,对于您当前的模块模式,我们会失去对$ b的效率$ b更新应该与所有成员共同使用的属性。

As shown above with your currently module pattern we lose effeciency in updating properties that should be in common to all members.

如果您对可见性感到怀疑,我会使用相同的模块化方法目前正在使用封装私人成员,但也使用
特权成员来访问这些成员他们是否需要联系。 特权成员是提供访问私有变量的接口的公共成员。最后将原型成员添加到原型中。

If you are concered about visibility, I would use the same modular method you are currently using to encapsulate private members but also use priviledged members for accessing these members should they need to be reached. Priviledged members are public members that provide an interface to access private variables. Finally add common members to the prototype.

当然要走这条路线,你需要跟踪这个。
确实在你的实现中有

Of course going this route, you will need to keep track of this. It is true that in your implementation there is



  • 无需通过$跟踪'this'指针回调函数中的.proxy(fn,this)


  • 不再使用事件处理程序等等var = this等等。每当我看到'this'时,我知道它是传递给回调的上下文,它不是我跟踪的东西,以了解我的对象实例。

,但每次创建一个非常大的对象消耗更多内存与使用某些原型继承相比。

, but you are are creating a very large object each time which will consume more memory in comparison to using some prototypal inheritance.

类比通过使用原型获得性能是通过在操作DOM时使用事件委派来提高性能。 Javascript中的事件委派

An analogy to gaining performance by using prototypes is improved performance by using event delegation when manipulating the DOM.Event Delegation in Javascript

假设您有一个大型购物清单.Yum。

Lets say you have a large grocery list.Yum.

<ul ="grocery-list"> 
    <li>Broccoli</li>
    <li>Milk</li>
    <li>Cheese</li>
    <li>Oreos</li>
    <li>Carrots</li>
    <li>Beef</li>
    <li>Chicken</li>
    <li>Ice Cream</li>
    <li>Pizza</li>
    <li>Apple Pie</li>
</ul>

假设你要记录你点击的项目。
一个实现是将事件处理程序附加到每个项目(坏),但如果我们的列表很长,则会有很多要管理的事件。

Let's say that you want to log the item you click on. One implementation would be to attach an event handler to every item(bad), but if our list is very long there will be a lot of events to manage.

var list = document.getElementById('grocery-list'),
 groceries = list.getElementsByTagName('LI');
//bad esp. when there are too many list elements
for(var i = 0; i < groceries.length; i++) {
    groceries[i].onclick = function() {
        console.log(this.innerHTML);
    }
}

另一个实现是附加一个事件处理父节点(好)并让一个父节点处理所有点击。
正如您所看到的,这类似于将原型用于常见功能并显着提高性能

Another implementation would be to attach one event handler to the parent(good) and have that one parent handle all the clicks. As you can see this is similar to using a prototype for common functionality and significantly improves performance

//one event handler to manage child elements
 list.onclick = function(e) {
   var target = e.target || e.srcElement;
   if(target.tagName = 'LI') {
       console.log(target.innerHTML);
   }
}



使用功能/原型继承的组合重写



我认为功能/原型继承的组合可以用一种易于理解的方式编写。
我使用上述技术重写了你的代码。

Rewrite using Combination of Functional/Prototypal Inheritance

I think the combination of functional/prototypal inheritance can be written in an easy understandable manner. I have rewritten your code using the techniques described above.

var Animal = function () {

    var helloCount = 0;
    var self = this;
    //priviledge methods
    this.AnimalHello = function() {
        helloCount++;
        console.log(self.Name + ' says hello (animalHello)');
    };

    this.GetHelloCount = function (callback) {
        callback.call(null, helloCount);
    }

};

Animal.prototype = {
    Name: 'Generic',
    IsAnimal: true
};

var Sheep = function (name) {

    var sheep = new Animal();
    //use parasitic inheritance to extend sheep
    //http://www.crockford.com/javascript/inheritance.html
    sheep.Name = name || 'Woolie'
    sheep.SheepHello = function() {
        this.AnimalHello();
        var self = this;
        this.GetHelloCount(function(count) {
            console.log('i (' + self.Name + ') have said hello ' + count + ' times (sheepHello anon callback)');
        });
    }

    return sheep;

};

Sheep.prototype = new Animal();
Sheep.prototype.isSheep = true;

var sheepie = new Sheep('Sheepie');
var lambie = new Sheep('Lambie');

sheepie.AnimalHello();
sheepie.SheepHello();
lambie.SheepHello();



结论



外卖是同时使用原型和功能继承来解决性能和可见性问题
最后,如果您正在处理小型JavaScript应用程序并且这些性能问题不是问题,那么
那么您的方法将是可行的方法。

Conclusion

The takeaway is to use both prototypal and functional inheritance to their advantages both to tackle performance and visibility issues. Lastly, if you are working on a small JavaScript applications and these performance issues are not a concern, then your method would be viable approach.

这篇关于使用$ .extend和模块模式的简单javascript继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆