我的最终目标是能够做到这样的事情:
MyVar(parameter).functionToPerform();
足够愚蠢,即使在阅读了如何声明变量之后,查看jQuery代码,......我仍然无法理解它。
这是我到目前为止所尝试过的,但它失败了:
var MyClass = function(context) {
this.print = function(){
console.log("Printing");
}
this.move = function(){
console.log(context);
}
};
var test = new MyClass();
test.print(); // Works
console.log('Moving: ' + test('azerty').move() ); // Type property error
答案 0 :(得分:15)
在我写这篇文章时,Squeegy的答案得票率最高:7。但这是错误的,因为__proto__
是非标准的,Internet Explorer(甚至版本8)不支持。但是,摆脱__proto__
并不能让它在IE 6中运行。
这(稍微简化)是jQuery实际执行的方式(even try it on IE 6),它还包括静态方法和方法链接的示例。当然,有关jQuery如何实现的所有细节,您必须自己检查jQuery source code。
var MyClass = function(context) {
// Call the constructor
return new MyClass.init(context);
};
// Static methods
MyClass.init = function(context) {
// Save the context
this.context = context;
};
MyClass.messageBox = function(str) {
alert(str);
};
// Instance methods
MyClass.init.prototype.print = function() {
return "Printing";
};
MyClass.init.prototype.move = function() {
return this.context;
};
// Method chaining example
MyClass.init.prototype.flash = function() {
document.body.style.backgroundColor = '#ffc';
setInterval(function() {
document.body.style.backgroundColor = '';
}, 5000);
return this;
};
$('#output').append('<li>print(): '+ MyClass().print() +'</li>');
$('#output').append('<li>flash().move():'+ MyClass('azerty').flash().move() +'</li>');
$('#output').append('<li>context: '+ MyClass('azerty').context +'</li>');
MyClass.messageBox('Hello, world!');
请注意,如果您需要“私有”数据,则必须将MyClass.init
中的实例方法(在该函数内部声明的变量)放在this.print = function() { ... };
中,而不是使用MyClass.init.prototype
答案 1 :(得分:13)
jQuery()
既是具有全局方法的模块,也是构造函数。如果需要,它会自动调用构造函数。如果我们未使用new
关键字调用,则this
将不会使用MyClass
构建。我们可以检测到它并在构造函数模式下调用该函数。完成后,this
将成为MyClass
的实例,我们可以开始向其中添加内容。
var MyClass = function(context) {
// if the function is called without being called as a constructor,
// then call as a constructor for us.
if (this.__proto__.constructor !== MyClass) {
return new MyClass(context);
}
// Save the context
this.context = context;
// methods...
this.print = function() {
return "Printing";
}
this.move = function() {
return this.context;
}
};
$('#output').append('<li>print(): '+ MyClass().print() +'</li>');
$('#output').append('<li>move():'+ MyClass('azerty').move() +'</li>');
$('#output').append('<li>context: '+ MyClass('azerty').context +'</li>');
答案 2 :(得分:1)
当你做
时var test = new MyClass()
您创建的对象具有两个属性move
和print
。由于test
语句,对象new
不再是函数。所以调用test()
是错误的。
答案 3 :(得分:1)
每次在jQuery中调用$
时,它都会返回一个新的jQuery.init
对象。 jQuery.init
对象具有随后被调用的函数。
function test(type)
{
switch (type)
{
case 'azerty':
return new type.a();
case 'qwerty':
default:
return new type.b();
}
}
test.a = function()
{
//a object defined
};
test.a.prototype.move = function()
{
//move function defined for the a object
};
etc...
我只是即时输入,所以可能需要一些调整
这样您就可以拨打test('azerty').move();
。更重要的是:我希望你能看到正在使用的一般结构。
编辑添加:
要继续链接jQuery中的函数,请确保在每个函数调用结束时返回this
对象:
test.a.prototype.move = function()
{
//move function defined for the a object
return this;
};
答案 4 :(得分:0)
您可以将'azerty'值作为参数传递给MyClass构造函数并将测试('azerty')。move()更改为test.move()
<script type="text/javascript">
var MyClass = function(context) {
this.print = function(){
console.log("Printing");
}
this.move = function(){
console.log(context);
return context;
}
};
var test = new MyClass('azerty');
test.print(); // Works
console.log('Moving: ' + test.move() ); // Type property error
</script>
答案 5 :(得分:0)
首先,jQuery使用的模式更接近于 Monad , Factory 或两者的组合。然而,这就是我在我的项目中所使用的内容,因为模式本身与您想要利用的任何类别都松散耦合:
;(function (undefined) {
if (undefined) return;
var ENV = this;
var Class = function Class() {
var thus = this;
function find(data) {
console.log('@find #data', data);
return this;
}
function show(data) {
console.log('@show #data', data);
return this;
}
// export precepts
this.find = find;
this.show = show;
return this;
};
var Namespace = ENV['N'] = new (function Namespace(Class) {
var thus = this;
var Ns = Class.apply(function Ns(data) {
if (this instanceof N) {
return new Namespace(Class);
}
return Ns.find.apply(Ns, arguments);
});
return Ns;
})(Class);
}).call(window || new function Scope() {});
var n = N('#id').show(450);
var m = new N();
m('#id')('.curried').show('slow');
console.log(n !== m); // >> true
基本上,您可以将其用作函数,对象,并使用new
关键字构造另一个唯一对象/函数。您可以使用它来强制执行仲裁器方法(默认情况下,如上面的find
方法),或者根据输入的参数使用不同的方法。例如,您可以执行以下操作:
var elementsList = N('#id1')('#id2')('#otherSpecialElement').each(fn);
- 或 -
var general = N('.things');
var specific = general('.specific')('[data-more-specific]').show();
例如,上面将累积多个元素的节点列表(第一个表达式),或向下钻取到一个特定元素(第二个)。
希望这有帮助
答案 6 :(得分:0)
我最近参与了一项练习,要求仅使用ES6重新创建类似JQuery的库。
从上面的观点我能够创建实例化逻辑,使得可以在选择器上调用方法,例如类名。
当使用选择器调用$ T的实例时,会在特定选择器上创建$ TLib的新实例,该实例将包含可在选择器上使用的所有方法以及上下文中的元素。
我在下面的例子中添加了几个可链接的方法,它们允许你在一个元素中添加一个CSS类,并在一次调用中删除相同的类,例如:
$T('.class-selector').addClass('green').removeClass('green);
对于那些想要引导类似事物的人:
const $T = (selector) => {
// returns the HTML elements that match the selector
const elements = document.querySelectorAll(selector);
return new $TLib(elements, selector);
};
class $TLib {
constructor (elements) {
this._elements = elements;
}
addClass (className) {
this._elements.forEach((element) => element.classList.add(className));
return this;
}
removeClass (className) {
this._elements.forEach((element) => element.classList.remove(className));
return this;
}
toggleClass (className) {
this._elements.forEach((element) => {
const classList = element.classList;
(classList.contains(className)) ? classList.remove(className) : classList.add(className);
});
return this;
}
}
答案 7 :(得分:0)
到目前为止,提供的解决方案似乎并未反映jQuery的确切结构(或jQuery随时间而改变)。
我最近想创建一个对象(类似于jQuery),并遇到了这个问题。所以这是我的答案:
// dummy text element
var p = document.createElement("p");
p.innerText = "Lorem ipsum...";
// jQuery-like object
var CustomObject = function (element) {
return new CustomObject.prototype.init(element);
};
// constructor
CustomObject.prototype.init = function (element) {
this.el = element;
this.text = element.innerText;
};
// instance methods
CustomObject.prototype.log = function () {
console.log(this.text);
// by returning "this" at the end of instance methods
// we make these methods chainable
return this;
};
CustomObject.prototype.add2body = function (delay) {
document.body.appendChild(this.el);
return this;
};
// all the instance methods are added to CustomObject, not to CustomObject.init (constructor)
// calling CustomObject() returns a CustomObject.init object, not CustomObject object
// so by default, instance methods are not accessible
// to fix this, you need to assign the prototype of CustomObject to CustomObject.prototype.init
CustomObject.prototype.init.prototype = CustomObject.prototype;
// testing
var obj = CustomObject(p).add2body().log();