function函数的用法_1. jQuery 工厂函数加原型共享创建对象模式

本文解析了jQuery的源码结构,介绍了jQuery如何作为一个工厂模式运作,并详细解释了其共享原型的设计方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

首先声明一下,该文档用于对 jQuery 源码的讲解,对于学习原生 js 我们很多时候都不知道自己的定位到底是什么,掌握程度真的算得上是大神了吗?看源码是一种有效的方式来认清自己,向大佬学习的过程。

本专栏会依次循序渐进的讲解 jQuery 的源码。

进入源码

(function(global,factory){"use strict";if(typeofmodule==="object"&&typeofmodule.exports==="object"){module.exports=global.document?factory(global,true):function(w){if(!w.document){thrownewError("jQuery requires a window with a document");}returnfactory(w);};}else{factory(global);}// Pass this if window is not defined yet
})(typeofwindow!=="undefined"?window:this,function(window,noGlobal){});

进入源码后最外层的框架就是这样了,可以看到整体是一个自执行函数,然后在函数体内做了一些判断运行环境的操作,以便于适用于 规范。

可以看到如果是浏览器运行环境那么自执行的第二个参数的 noGlobal 参数是 undefined,翻到源码最下面可以看到:

if(typeofnoGlobal==="undefined"){window.jQuery=window.$=jQuery;}returnjQuery;

这里如果是浏览器运行环境下就会向 window 全局上定义一个 jQuery$)的变量。拿到了内部的变量 jQuery 的引用。

或许你现在已经迫不及待的想看看 内部变量 jQuery 究竟是什么了,但是我们需要学习到的是,我们在自己写代码、造轮子的时候应该学习这种模式,全部的业务逻辑写到了一个函数中,这样在库内部声明的变量就不会污染全局。最后为用户暴露出一个统一的接口——jQuery

这样我们就可以在外部直接使用 jQuery 或者 $ 了。

jQuery 是什么

接下来就让我们再次进入源码,看看 jQuery 这个内部的变量究竟是什么。

在看之前,我门可以先来大胆的猜测一下。试想,我们在使用 jQuery 的时候的用法:

$('div').css('background', 'pink');

这样简简单单的代码就可以获取到了页面中的所有 div 标签并设置粉色的背景色。

从这里我们可以看到我们拿到 $ 变量是把它当作一个函数在调用,所以它可能是下面的写法:

// 写法1
varjQuery=function(){};// 写法2
functionjQuery(){}

我们还可以使用到 jQuery 上面的一些属性或者方法,例如(),对于以上两种方法,都可以实现,但是我认为第一种写法更好一些。

另外我们可以在控制台打印一下 $('div')

可以看到这其实是一个对象,也就是说调用 $(),方法会返回一个对象。在 javascript 创建对象需要使用 new 操作符加构造函数来创建,我们可以先理解 jQuery 就是一个构造函数,但是为什么这里并没有使用 new 关键字呢?

我们带着自己的揣测和疑问进入源码:

varjQuery=function(selector,context){returnnewjQuery.fn.init(selector,context);};

直到看到这里我们就一目了然,这不就是一个工厂模式 吗?

工厂模式

这里普及一个创建型的设计模式——工厂模式。

工厂模式就是允许我们使用函数返回值的形式构造一个对象,使用它就几点好处:

  • 使用者不需要知道构造函数是谁
  • 不需要使用 new 操作符

实现一个简易的工厂模式:

functionPerson(name){this.name=name;}functioncreatePerson(name){returnnewPerson(name);}constperson=createPerson('jerry');console.log(person);

可以看到我们在使用的时候并不知道内部的构造函数是谁,也并没有使用 new 操作符。

内部实现

回到 jQuery 的源码,这个时候我们就已经知道了为什么我们在使用的时候会那么调用了。那么问题来了,源码内部的真实的构造函数究竟是谁呢?

我们看到工厂函数的内部返回结果为 new .init( selector, context );所以我们顺藤摸瓜, 又是什么呢?

查找源码,我们可以看到:

jQuery.fn=jQuery.prototype={get:function(){},each:function(){},map:function(){}// ...
};

也就是说 其实是拿到了 的引用,那么原型对象上的 init 函数又是什么呢?

varinit=jQuery.fn.init=function(selector,context,root){// 处理参数的代码
};

到这里目前的疑问就已经解决完了。

总结一下就是 jQuery 实际是一个工厂函数,内部实例化了 jQuery 原型对象上的 init 构造方法。

这里你可能会有一个疑问,我们为什么不这样写:

varjQuery=function(selector,context){returnnewjQuery(selector,context);};

我们这样,函数的返回值不就直接是一个 jQuery 的实例对象了吗?

事实上这样是会报错的,试想我们使用 new 操作符创建对象会发生什么:

  • 创建一个对象
  • 绑定 this 指向新创建的对象
  • 执行构造函数内部的代码
  • 使该对象的 _proto_ 属性指向构造函数的 prototype

出问题的是第三个环节,我们会执行该构造函数,那么如果直接 return new jQuery( selector, context );那么函数执行栈不就溢出了吗,所以就报错了。

共享原型

如果你已经为 jQuery 的作者的这样的设计感动吃惊,那么我会告诉你,今天的主角才刚刚要来临了!!!

细心的我们已经发现了,在 对象上有很多的方法:geteachmap等等。我们只是给 jQuery 的原型对象上添加了这些方法 可是为什么上面的 init 的实例对象也可以使用这些方法呢?我们百思不得其解,直到看到了这么一行代码:

init.prototype=jQuery.fn;

现在,你可以尖叫了!!!

这就是共享原型,这样设计之后,我们只需要为 jQuery 的原型对象上添加方法,init 的实例对象都可以使用这些方法了。

图解

通过这张图我相信你肯定可以更好的理解这一共享原型的设计了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值