Nodejs环境下,为什么jQuery的构造方法要放在jQuery.fn.init里?
Nodejs环境下,为什么jQuery的构造方法要放在jQuery.fn.init里?
如题,为什么要这么做呢?为了配合上面的语句,还得加上下面这句很绕的语句:
jQuery.fn.init.prototype = jQuery.fn;
为什么不能这么干呢?
(function(){
var jQuery = function(){
return new F();
};
var F = function(){
return this;
};
F.fn = F.prototype = {
jquery : 1.0,
test : function(){
console.log('test');
return this;
}
};
jQuery.fn = jQuery.prototype = F.prototype;
window.jQuery = window.$ = jQuery;
})();
var a = $('body').test('li');
console.log('');
$.fn.extend = function(){console.log('extended');}
a.extend();
这样层次清晰,而且兼顾到插件扩展,求大神赐教。
把你的这个定义里面的F
换成jQuery.fn.init
,不就等价了吗?
在Node.js环境中,虽然你提到的是一个与浏览器环境相关的概念,但理解jQuery构造方法的实现原理仍然有助于更好地理解JavaScript中的原型继承机制。jQuery将构造方法放在jQuery.fn.init
中的原因主要与原型链的设计有关,以确保jQuery对象能够正确地支持链式调用(chaining)。
为什么使用 jQuery.fn.init
在原始的jQuery源码中,jQuery.fn.init
是jQuery构造函数的真正实现。这样做有几个好处:
- 链式调用:通过将构造函数的实现放在
init
方法中,并将其原型指向jQuery.fn
,可以确保每个方法调用都能返回当前的jQuery对象实例,从而支持链式调用。 - 原型继承:这种方式允许在jQuery对象上添加公共方法(例如
test()
),这些方法可以在所有jQuery对象实例上共享。
示例代码
让我们通过一个简化版的jQuery来理解这一点:
(function() {
var jQuery = function(selector) {
// 使用new操作符时,会调用此函数
return new jQuery.fn.init(selector);
};
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
jquery: "1.0",
init: function(selector) {
// 初始化逻辑
console.log('初始化选择器:', selector);
return this; // 返回当前jQuery对象实例
},
test: function() {
console.log('执行测试方法');
return this; // 返回当前jQuery对象实例,支持链式调用
}
};
// 将init方法的原型指向jQuery.fn
jQuery.fn.init.prototype = jQuery.fn;
window.jQuery = window.$ = jQuery;
})();
// 使用jQuery
var a = $('body').test();
在这个例子中:
jQuery.fn.init
是实际的构造函数。jQuery.fn.init.prototype = jQuery.fn
确保了init
方法的原型指向了jQuery的原型对象,使得所有的jQuery实例都共享这些方法。- 每个方法(如
test()
)都返回this
,这使得方法调用可以链接在一起。
对比你的代码
你的代码尝试直接将 F.prototype
赋值给 jQuery.prototype
,虽然这也实现了某些功能,但它没有正确地设置原型链,特别是没有正确处理 jQuery.fn.init
的原型。这可能会影响链式调用的正确性,以及方法在所有实例上的共享。
通过这种方式,jQuery能够提供更健壮和灵活的对象模型,支持复杂的DOM操作和事件处理。
[@bnuhero](/user/bnuhero) 我的代码结构可以实现jQuery的要求了啊,为什么要把F换成jQuery.fn.init呢?这样多不直观啊
两者的效果区别只在于,jquery多做了把F挂到$.fn上。所以你的代码加一句jQuery.fn.init = F;如楼上说,就等价了。这样的好处是在访问不到F的地方,可以调用jQuery.fn.init。
我也一直有这疑问 同求
[@nighca](/user/nighca) 但是没有必要访问F啊,要扩展有两种,意识jQuery.extend;二是jQuery.fn.extend。现在两者都可以被扩展啊。
[@nighca](/user/nighca) 当然,如果要扩展jQuery,必须加上jQuery.extend = F.extend = F.fn.extend = function extend(){};如果是为了避免还有可能出现的这种情况,那么可以解释为什么要把F挂到jQuery.fn上
- 其实楼主的F和jQuery.fn.init是相等的; 实现功能是和jq一样的, 只是jq的把构造函数放进原型;
- 如果非要说原因, 个人理解jq这样写整体结构清晰, 先是入口构造函数,紧跟着是原型部分(原型里面init是初始化), 但是不好理解;
- 乍一看确实挺绕, 我也是看了好久才明白怎么回事
jQuery = function( selector, context ) { return new jQuery.fn.init( ); } jQuery.fn=JQuery.prototype = { init: function() {}, … }
- 最后重置原型, 和楼主的 jQuery.prototype = F.prototype;一个意思 jQuery.fn.init.prototype = jQuery.fn;
[@yu521088](/user/yu521088) 将F的引用挂在fn上,也就是间接地挂在$这个对外暴露的变量上。那么在外部就可以访问到并使用F,比如F.apply(obj),或者obj instance of F,总之,这个函数作为jQuery中重要的一个函数(所有jQuery对象的constructor),大量重要逻辑在其中,将其暴露出来是可能发挥出一些作用的。以上为个人理解。
你后面的话我没看懂…
[@yu521088](/user/yu521088) jq要扩展方法直接这样也不麻烦:
jQuery.extend = jQuery.fn.extend=function(){ }
jQuery.extend是扩展到jquery上, jQuery.fn.extend扩展到jquery实例上,也就是原型上面的 而你的也跟jq一样:
jQuery.extend = jQuery.fn.extend; 不需要这样: jQuery.extend = F.extend = F.fn.extend;
这样就可以了
[@nighca](/user/nighca) 其实jQuery.extend = F.extend;这样就可以对外暴露F这个变量了吧。你要对F做的扩展都可以通过jQuery.extend实现啊。
我后面那句话是想说,如果有大量的需要在内部扩展F的语句,比如:
F.extend({
one: ...
});
F.extend({
two: ...
});
F.extend({
three: ...
});
那为了把这些方法暴露出来,需要同时扩展jQuery和F:
var one = {
one: ...
},
two = {
two: ...
},
three = {
three: ...
};
jQuery.extend(one);
F.extend(one);
jQuery.extend(two);
F.extend(two);
jQuery.extend(three);
F.extend(three);
[@nighca](/user/nighca) 其实就是怎么暴露F的问题,jQuery是把F通过jQuery.fn.init暴露了,依我现在的实现,每一次扩展F,必须同时扩展jQuery。其实也可以这么做吧:
F.fn.extend = function(){
// 现在jQuery的extend方法
}
jQuery.extend = function(){
F.fn.extend.apply(F, arguments.slice);
}
其实jQuery.extend = F.extend;这样就可以对外暴露F这个变量了吧。你要对F做的扩展都可以通过jQuery.extend实现啊。
做不到吧,比如我在外部(jQuery使用者角度)这样用:
var a = jQuery.fn.init.apply({});
如果没挂在jQuery.fn上,怎么拿得到这个函数啊。
另外,我还是没理解你的意思,jQuery里边现在有三个东西:jQuery(即$,返回值为一个Constructor的实例的函数), Constructor(jQuery对象的构造函数,记为Constructor,即F,亦即jQuery.fn.init),jQuery.fn(是Constructor的prototype)。
jQuery的主要扩展就两个地方:
-
往jQuery上挂
jQuery.extend({ xxx: function(){} })
使用方式:$.xxx()或jQuery.xxx()
-
往jQuery.fn上挂
jQuery.fn.extend({ xxx: function(){} })
使用方式:$(‘div’).xxx()
并没有需要扩展F(Constructor)的场景啊。
[@yu521088](/user/yu521088) 你平时用prototype么?
[@yu521088](/user/yu521088) 要明白jQuery和jQuery.fn的区别 你上面jQuery.extend = function(){} 扩展的当然是jQuery, 你要扩展jqeury的实例(也就是F)就必须这样
jQuery.fn.extend=function(){…}; // 认真看,中间多了个fn
用你的代码: 同时扩展到jquery和jquery实例(即F)上面
$.extend=$.fn.extend = function(){ console.log(this); console.log(‘extended’); } $(‘body’).extend();//这个方法是jqeury的实例,是F.fn上面的 this的结果是: F $.extend();//这个方法是jquery本身 this的结果是: jQuery
其实上面就对应了我们jquery中不同类型的api 比如: $.ajax() 和 $(’#id’)的区别, 前者对应的是jquery.extend, 后者对应jquery.fn.extend
[@jeffZhong](/user/jeffZhong) ,首先,我明白$和$.fn的区别,也明白$.extend和$.fn.extend的区别。你的这段代码:
jQuery.fn.extend=function(){…}; // 认真看,中间多了个fn 等同于我写的: F.fn.extend = function(){ // 现在jQuery的extend方法 } 因为在jQuery.fn = F.fn。
而我要同时满足$.extend和$.fn.extend,我可以这么实现:
jQuery.extend = F.fn.extend = function(){}; 这样,在外部,我既可以$.extend({‘ajax’: function(){}})来扩展jQuery实现$.ajax,也可以$.fn.extend({‘some’:function(){}})来扩展jQuery.fn实现$(’#id’).some();
但是这么做,当我们调用$.extend的时候,实际上是扩展的内部的jQuery这个变量,而我觉得更合理的代码实现应该做到这样:在外部调用$.extend的时候,不应该是对内部jQuery变量的扩展,而是对F的扩展。所以才有了这段代码:
jQuery.extend = function(){ F.fn.extend.apply(F, arguments.slice); }
还有,下面这段代码中的this分别是F和jQuery是正确的逻辑啊:
$.extend=$.fn.extend = function(){
console.log(this);
console.log('extended');
}
$('body').extend();//这个方法是jqeury的实例,是F.fn上面的
this的结果是: F
$.extend();//这个方法是jquery本身
this的结果是: jQuery
[@kingapple](/user/kingapple) prototype用来实现继承啊
[@yu521088](/user/yu521088) 额,平时用到多么?
[@kingapple](/user/kingapple) 多啊,你要写一个类,把这个类的非公有属性放在自己的属性中,把公有的方法和属性放在prototype对象中啊
[@yu521088](/user/yu521088) 你看看jquery的源码就知道了,不太了解你要达到什么效果
[@yu521088](/user/yu521088) 你说的公有,是子类可继承的意思吧?
[@jeffZhong](/user/jeffZhong) 其实我只是单纯的不明白为什么jQuery要把用这么绕的方式实现:
new jQuery.fn.init();
jQuery.fn.init.prototype = jQuery.fn;
[@kingapple](/user/kingapple) 子类可继承是一方面。另一方面是,定义一个Man类,每个Man实例都会有自己的名字,年龄,性别,这些属性对每个Man实例都是不同的,不能公用。但是像run(), eat()这些方法,可以是每个Man实例都共享的。
[@yu521088](/user/yu521088) 理解为你是要禁用扩展jquery本身的功能, 如果别人真要扩展在jquery上面, 不用你的
jQuery.extend({aa:function(){...}})
这个方法,直接向下面这样扩展呢
$.aa=function(){...}
[@yu521088](/user/yu521088) prototype里面可以放公共变量,类似于静态变量?
[@yu521088](/user/yu521088) 其实你的方式实现了和jquery差不多的功能, 同时扩展插件方面也是和jq一样的效果的. , 只是解决方式不一样而已
[@yu521088](/user/yu521088) 还有本来人家jq中 $.extend就是扩展jquery本身, $.fn.extend就是扩展jquery实例, 而我只是不理解你干嘛要禁用掉扩展jq本身, 两个都留着多好, 想扩展哪个就扩哪个 :)
[@jeffZhong](/user/jeffZhong) 恩,好像这个解释比较说的通,如果用这样的方法扩展$: `$.extend({‘a’:function(){}}); 而用
$.b = function(){
//这里访问不到this.a
}
在b里就访问不到this.a 了,因为这里的this是jQuery,而用extend扩展的是F。
但是我也可以这么干啊:
jQuery.extend = F.fn.extend = function(){}
这样,扩展jQuery就可以了,可以用$.extend 也可以用$.newMethod。
[@yu521088](/user/yu521088) 但是你要知道fn是jquery下面的属性,在外部是可以访问得到的, 而你的F是内部的, 在外面是没法访问到的, 如果我外部扩展类库的方法是可以的
$.fn.extend({a:function(){...}})
而你的
F.fn.extend(a:function(){...})
你外面根本不能这样调,会报错的,因为F是内部的变量 你的类库总不能每次人家要扩展插件了,都要在你内部扩展吧
[@yu521088](/user/yu521088) js的实现好蛋疼,还是Java的直观:)
[@jeffZhong](/user/jeffZhong) 内部有一句话
jQuery.fn = jQuery.prototype = F.prototype;
所以在外面我可以通过$.fn.extend(a:function(){…})来扩展F.fn
还有本来人家jq中 $.extend就是扩展jquery本身, $.fn.extend就是扩展jquery实例, 而我只是不理解你干嘛要禁用掉扩展jq本身, 两个都留着多好, 想扩展哪个就扩哪个 :)
我的想法是内部的jQuery这个变量是暴露给外面的桥梁,也就是说这个jQuery变量可以随便取什么名字,比如cQuery,而jQuery对象其实是F的实例,所有实例的方法代码其实都在F.fn下,所以我这里的F其实应该就是John大神代码里的jQuery变量,所以我想让所有对jQuery本身的扩展放在F下面 :) 感觉有点代码洁癖的样子,恩,是这样的 LOL
[@kingapple](/user/kingapple) 对的,这就是js神(dan)奇(teng)的地方。
[@yu521088](/user/yu521088) js没有接口的概念吧?要实现复杂模式,就变成天书了:(
[@yu521088](/user/yu521088) 也就是说本来就已经实现了的功能, 像jq那样
$.fn.extend({a:function(){...}}); 扩展实例
$.extend({a:function(){...}}); 扩展本身
那你还什么不满足,你这是要闹那样呢?
[@jeffZhong](/user/jeffZhong) 我只是想知道jQuery为什么不像我这么干,要把F放到jQuery.fn.init下 %>_<%
[@kingapple](/user/kingapple) 尼玛又见到你了…真水啊…
最近我也在读jquery源码,也思考了这个问题。这个实现方式感觉很晦涩。 我也百度了不少文章没有找到讲的特别清楚的。 从这个问题还可以引申出 为什么不用 jQuery.init = function(){//构造函数 },应该可以实现相同的功能。并且同样可以暴露到全局作用域。 大家应该知道js中的prototype 主要是为了实现继承。那么jQuery.fn.init可以被所有的jQuery实例对象调用。 另外 我在这个地址找到了正确答案:http://stackoverflow.com/questions/18782973/why-is-the-init-function-in-jquery-prototype-and-not-in-jquerys-closure
在Node.js环境中使用jQuery需要通过特定的方式来实现,因为jQuery最初是为浏览器环境设计的。在这个过程中,jQuery的构造方法被封装在jQuery.fn.init
中是为了更好地支持原型继承和插件扩展。
在浏览器环境中,jQuery.fn
通常指向jQuery的原型对象,而jQuery.fn.init
则是jQuery构造函数。将构造方法放在jQuery.fn.init
中,可以更好地实现原型链继承,使得自定义方法能够通过原型链访问到构造函数中的方法。
以下是简化后的代码示例:
(function() {
var jQuery = function(selector) {
return new jQuery.fn.init(selector);
};
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
jquery: '1.0',
init: function(selector) {
// 这里实现初始化逻辑
console.log('Init called with selector:', selector);
return this;
},
test: function() {
console.log('test method called');
return this;
}
};
jQuery.fn.init.prototype = jQuery.fn; // 确保原型链正确
window.jQuery = window.$ = jQuery;
})();
var a = $('body').test();
在这个例子中,jQuery.fn.init
作为构造函数,确保了原型链的正确性,并且可以通过jQuery.fn
访问所有原型方法,例如test
方法。
至于为什么不能直接在全局作用域中定义构造函数并将其原型赋值给jQuery.fn
,这样做可能会导致原型链不正确,从而影响插件的扩展性和方法的继承。因此,在Node.js环境中使用jQuery时,应该遵循jQuery的设计模式,以确保正确性和兼容性。