gpt4 book ai didi

javascript - 使用 $.extend 和模块模式的简单 javascript 继承

转载 作者:行者123 更新时间:2023-12-03 21:45:55 25 4
gpt4 key购买 nike

几年来我一直想知道人们如何看待使用模块模式式构造函数模式而不使用正常的原型(prototype)继承来进行继承。为什么程序员不对非单例 js 类使用模块模式?对我来说优点是:

  • 非常清晰的公共(public)和私有(private)范围(易于理解代码和 API)
  • 无需在回调中通过 $.proxy(fn, this) 跟踪“this”指针
  • 不再有 var that = this 等事件处理程序等。每当我看到“this”时,我就知道它是传递给回调的上下文,它不是我跟踪以了解我的对象的东西实例。

缺点:

  • 性能下降较小
  • Doug Crockford 可能会“摇手指”吗?

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

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();

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

谢谢!

[更新]

感谢您的精彩回复。希望我能给大家赏金。这就是我一直在寻找的东西。基本上就是我所想的。我永远不会使用模块模式来构造多个实例。通常只有一对。我认为它有其优点的原因是,您看到的任何小的性能下降都会在编码体验的简单性中重新体现。这些天我们有很多代码要写。我们还必须重用其他人的代码,就我个人而言,我很欣赏有人花时间创建一个漂亮而优雅的模式,而不是在有意义时教条地遵循原型(prototype)继承。

最佳答案

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

正如帖子JS - Why use Prototype?中提到的,原型(prototype)的优点之一是您只需初始化原型(prototype)成员一次,而构造函数中的成员是为每个实例创建的。事实上,您可以直接访问原型(prototype),而无需创建新对象。

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它到原型(prototype)。

考虑一下

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

函数继承/模块模式

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 !
}

对比原型(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 :)

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

如果您担心可见性,我会使用您当前用于封装私有(private)成员的相同模块化方法,但也会使用 priviledged members如果需要联系这些成员,可以联系他们。 特权成员是提供访问私有(private)变量的接口(interface)的公共(public)成员。最后将公共(public)成员添加到原型(prototype)中。

当然,走这条路,你需要跟踪这一点。 确实,在您的实现中存在

  • 无需在回调中通过 $.proxy(fn, this) 跟踪“this”指针
  • 不再有 var that = this 等事件处理程序等。每当我看到“this”时,我就知道它是被传递到回调中的上下文,它不是我跟踪以了解我的对象实例的东西。

,但是您每次都会创建一个非常大的对象,与使用某些原型(prototype)继承相比,这将消耗更多内存

事件委托(delegate)作为类比

通过使用原型(prototype)获得性能的一个类比是通过在操作 DOM 时使用事件委托(delegate)来提高性能。 Event Delegation in Javascript

假设您有一个很大的购物 list 。嗯。

<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>

假设您想要记录您单击的项目。一种实现是为每个项目附加一个事件处理程序(不好),但如果我们的列表很长,就会有很多事件需要管理。

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);
}
}

另一种实现方式是将一个事件处理程序附加到父级(好),并让该父级处理所有点击。正如您所看到的,这类似于使用原型(prototype)来实现常见功能,并且显着提高了性能

//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);
}
}

使用功能/原型(prototype)继承组合进行重写

我认为函数/原型(prototype)继承的组合可以用一种易于理解的方式编写。我已经使用上述技术重写了您的代码。

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();

结论

要点是利用原型(prototype)继承和函数继承的优势来解决性能和可见性问题。最后,如果您正在开发小型 JavaScript 应用程序,并且这些性能问题不是问题,那么你的方法将是可行的方法。

关于javascript - 使用 $.extend 和模块模式的简单 javascript 继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16659326/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com