首先,我们要考虑一下它的源文件布局,也就是决定代码如何拆分到独立的文件中去。为什么要这么做呢?还记得上期结尾处我提到这个组件会使用“外部代码”吗?为了区分代码的用途,决定将代码至少分成两部分:外部代码文件和 Smart Queue 文件。
区分用途只是其一,其二,分散到独立文件有利于代码的维护。试想,以后的某一天你决定要在现有的队列管理基本功能之上,添加一些新的扩展功能,或是把它包装成某个实现特定任务的组件,而又希望保持现有功能(内部实现)和调用方式(对外接口)不变,那么将新的代码写到单独的文件是最好的选择。
嗯,下期会重点谈谈文件布局的话题,现在要开始切入正题了。第一步,当然是要为组件创建自己的命名空间,组件所有的代码都将限制在这个顶层命名空间内:
<span style="COLOR: #0000c0">var</span> SmartQueue = window.SmartQueue || {}; SmartQueue.version = <span style="COLOR: #008000">'0.1'</span>;
初始化的时候,如果碰到命名空间冲突就把它拉过来用。通常这个冲突是由重复引用组件代码导致的,因此“拉过来用”会将对象以同样的实现重写一次;最坏的情况下,如果碰巧页面上另一个对象也叫 SmartQueue, 那不好意思了,我会覆盖你的实现——如果没有进一步的命名冲突,基本上两个组件可以相安无事地运行。同时顺便给它一个版本号。
接着,按三个优先级为 SmartQueue 创建三个队列:
<span style="COLOR: #0000c0">var</span> Q = SmartQueue.Queue = [[], [], []];
每个都是空数组,因为还没有任务加进去嘛。又顺便给它建个“快捷方式”,后面要访问数组直接写 Q[n] 就可以啦。
接下来,我们的主角 Task 隆重登场——怎么 new 一个 Task, 定义在这里:
<span style="COLOR: #0000c0">var</span> T = SmartQueue.Task = <span style="COLOR: #b000b0">function</span>(fn, level, name, dependencies) { <span style="COLOR: #c00000">if</span>(<span style="COLOR: #c00000">typeof</span> fn !== FUNCTION) { <span style="COLOR: #c00000">throw</span> <span style="COLOR: #c00000">new</span> <span style="COLOR: #606060">Error</span>(<span style="COLOR: #008000">'Invalid argument type: fn.'</span>); } <span style="COLOR: #0000c0">this</span>.fn = fn; <span style="COLOR: #0000c0">this</span>.level = _validateLevel(level) ? level : LEVEL_NORMAL; <span style="COLOR: #707070"> // detect type of name</span> <span style="COLOR: #0000c0">this</span>.name = <span style="COLOR: #c00000">typeof</span> name === STRING && name ? name : <span style="COLOR: #008000">'t'</span> + _id++; <span style="COLOR: #707070"> // dependencies could be retrieved as an 'Object', so use instanceof instead.</span> <span style="COLOR: #0000c0">this</span>.dependencies = dependencies <span style="COLOR: #c00000">instanceof</span> <span style="COLOR: #606060">Array</span> ? dependencies : []; };
里面的具体细节就不说了,有必要的注释,一般我们的代码也能做到自我描述,后面代码也是这样。这里告诉客户(使用者):你想新建一个 SmartQueue.Task 实例,就要至少传一个参数给这个构造函数(后 3 个都可以省略进行缺省处理),否则抛出异常伺候。
但是这还不够,有时候,客户希望从已有 Task 克隆一个新实例,或是从一个“残废体”(具有部分 Task 属性的对象)修复出“健康体”(真正的 Task 对象实例),通过上面的构造方式就有点不爽了——客户得这样写:
<span style="COLOR: #0000c0">var</span> task1 = <span style="COLOR: #c00000">new</span> SmartQueue.Task(obj.fn, <span style="COLOR: #008000">1</span>, <span style="COLOR: #008000">''</span>, obj.dependencies);
我很懒,我只想传 fn 和 dependencies 两个属性,不想做额外的事情。好吧,我们来重构一下构造函数:
<span style="COLOR: #0000c0">var</span> _setupTask = <span style="COLOR: #b000b0">function</span>(fn, level, name, dependencies) { <span style="COLOR: #c00000">if</span>(<span style="COLOR: #c00000">typeof</span> fn !== FUNCTION) { <span style="COLOR: #c00000">throw</span> <span style="COLOR: #c00000">new</span> <span style="COLOR: #606060">Error</span>(<span style="COLOR: #008000">'Invalid argument type: fn.'</span>); } <span style="COLOR: #0000c0">this</span>.fn = fn; <span style="COLOR: #0000c0">this</span>.level = _validateLevel(level) ? level : LEVEL_NORMAL; <span style="COLOR: #707070"> // detect type of name</span> <span style="COLOR: #0000c0">this</span>.name = <span style="COLOR: #c00000">typeof</span> name === STRING && name ? name : <span style="COLOR: #008000">'t'</span> + _id++; <span style="COLOR: #707070"> // dependencies could be retrieved as an 'Object', so use instanceof instead.</span> <span style="COLOR: #0000c0">this</span>.dependencies = dependencies <span style="COLOR: #c00000">instanceof</span> <span style="COLOR: #606060">Array</span> ? dependencies : []; }; <span style="COLOR: #0000c0">var</span> T = SmartQueue.Task = <span style="COLOR: #b000b0">function</span>(task) { <span style="COLOR: #c00000">if</span>(arguments.length > <span style="COLOR: #008000">1</span>) { _setupTask.apply(<span style="COLOR: #0000c0">this</span>, arguments); } <span style="COLOR: #c00000">else</span> { _setupTask.call(<span style="COLOR: #0000c0">this</span>, task.fn, task.level, task.name, task.dependencies); } <span style="COLOR: #707070"> // init context/scope and data for the task.</span> <span style="COLOR: #0000c0">this</span>.context = task.context || window; <span style="COLOR: #0000c0">this</span>.data = task.data || {}; };
如此一来,原来的构造方式可以继续工作,而上面的懒人可以这样传入一个“残废体”:
<span style="COLOR: #0000c0">var</span> task1 = <span style="COLOR: #c00000">new</span> SmartQueue.Task({<span style="COLOR: #c00000">fn</span>: obj.fn, <span style="COLOR: #c00000">dependencies</span>: obj.dependencies});
当构造函数收到多个参数时,按之前的方案等同处理;否则,视唯一的参数为 Task 对象或“残废体”。这里通过 JavaScript 中的 <font face="新宋体">apply</font>
/<font face="新宋体">call</font>
方法将新实例传给重构出来的 <font face="新宋体">_setupTask</font>
方法,作为该方法的上下文 (context, 也有称为 scope), <font face="新宋体">apply</font>
/<font face="新宋体">call</font>
是 JavaScript 在方法之间传递上下文的法宝,要用心体会哦。同时,允许用户定义 <font face="新宋体">task.fn</font>
在执行时的上下文,并将自定义的数据传递给执行中的 fn.
经典的 JavaScript 对象三段式是什么?
- 定义对象的构造函数
- 在原型上定义属性和方法
- new 对象,拿来用
所以,下面要为 <font face="新宋体">SmartQueue.Task</font>
对象的原型定义属性和方法。上期分析过 Task (任务)有几个属性和方法,部分属性我们已经在 <font face="新宋体">_setupTask</font>
中定义了,下面是原型提供的属性和方法:
T.prototype = { <span style="COLOR: #c00000">enabled</span>: <span style="COLOR: #008000">true</span>, <span style="COLOR: #c00000">register</span>: <span style="COLOR: #b000b0">function</span>() { <span style="COLOR: #0000c0">var</span> queue = Q[<span style="COLOR: #0000c0">this</span>.level]; <span style="COLOR: #c00000">if</span>(_findTask(queue, <span style="COLOR: #0000c0">this</span>.name) !== -<span style="COLOR: #008000">1</span>) { <span style="COLOR: #c00000">throw</span> <span style="COLOR: #c00000">new</span> <span style="COLOR: #606060">Error</span>(<span style="COLOR: #008000">'Specified name exists: '</span> + <span style="COLOR: #0000c0">this</span>.name); } queue.push(<span style="COLOR: #0000c0">this</span>); }, <span style="COLOR: #c00000">changeTo</span>: <span style="COLOR: #b000b0">function</span>(level) { <span style="COLOR: #c00000">if</span>(!_validateLevel(level)) { <span style="COLOR: #c00000">throw</span> <span style="COLOR: #c00000">new</span> <span style="COLOR: #606060">Error</span>(<span style="COLOR: #008000">'Invalid argument: level'</span>); } level = parseInt(level, <span style="COLOR: #008000">10</span>); <span style="COLOR: #c00000">if</span>(<span style="COLOR: #0000c0">this</span>.level === level) { <span style="COLOR: #c00000">return</span>; } Q[<span style="COLOR: #0000c0">this</span>.level].remove(<span style="COLOR: #0000c0">this</span>); <span style="COLOR: #0000c0">this</span>.level = level; <span style="COLOR: #0000c0">this</span>.register(); }, <span style="COLOR: #c00000">execute</span>: <span style="COLOR: #b000b0">function</span>() { <span style="COLOR: #c00000">if</span>(<span style="COLOR: #0000c0">this</span>.enabled) { <span style="COLOR: #707070"> // pass context and data</span> <span style="COLOR: #0000c0">this</span>.fn.call(<span style="COLOR: #0000c0">this</span>.context, <span style="COLOR: #0000c0">this</span>.data); } }, <span style="COLOR: #c00000">toString</span>: <span style="COLOR: #b000b0">function</span>() { <span style="COLOR: #0000c0">var</span> str = <span style="COLOR: #0000c0">this</span>.name; <span style="COLOR: #c00000">if</span>(<span style="COLOR: #0000c0">this</span>.dependencies.length) { str += <span style="COLOR: #008000">' depends on: ['</span> + <span style="COLOR: #0000c0">this</span>.dependencies.join(<span style="COLOR: #008000">', '</span>) + <span style="COLOR: #008000">']'</span>; } <span style="COLOR: #c00000">return</span> str; } };
如你所见,逻辑非常简单,也许你已经在一分钟内扫过了代码,嘴角不经意间露出一丝心领神会。不过,这里要说的是简单而且通常最不被重视的 <font face="新宋体">toString</font>
方法。在一些高级语言中,为自定义对象实现 <font face="新宋体">toString</font>
方法被作为最佳实践准则而推荐,为什么呢?因为 <font face="新宋体">toString</font>
可以很方便地在调试器中提供有用的信息,可以方便地将对象基本信息写入日志;在统一的编程模式中,实现 <font face="新宋体">toString</font>
可以让你少写一些代码。
嗯,我们继续推进,我们要实现 SmartQueue 的具体功能。上期分析过,SmartQueue 只有一个实例,因此我们决定直接在 SmartQueue 下面创建方法:
SmartQueue.init = <span style="COLOR: #b000b0">function</span>() { Q.forEach(<span style="COLOR: #b000b0">function</span>(queue) { queue.length = <span style="COLOR: #008000">0</span>; }); };
这里用到 JavaScript 1.6 为 Array 对象提供的遍历方法 <font face="新宋体">forEach</font>
. 之所以这样写是因为我们假定“外部代码”已经在前面运行过了。设置 Array 对象的 <font face="新宋体">length</font>
属性为 <font face="新宋体">0</font>
导致,它被清空并且释放所有的项(数组单元)。
最后一个方法 <font face="新宋体">fire</font>
, 是整个组件最主要的方法,它负责对所有任务队列进行排序,并逐个执行。由于代码稍长了一点,这里只介绍排序使用的算法和实现方式,完整代码在这里。
<span style="COLOR: #0000c0">var</span> _dirty = <span style="COLOR: #008000">true</span>, <span style="COLOR: #707070">// A flag indicates weather the Queue need to be fired.</span> _sorted = [], index; <span style="COLOR: #707070">// Sort all Queues.</span> <span style="COLOR: #707070">// ref: <a href="http://en.wikipedia.org/wiki/Topological_sorting"><font color="#0000ff">http://en.wikipedia.org/wiki/Topological_sorting</font></a></span> <span style="COLOR: #0000c0">var</span> _visit = <span style="COLOR: #b000b0">function</span>(queue, task) { <span style="COLOR: #c00000">if</span>(task._visited >= <span style="COLOR: #008000">1</span>) { task._visited++; <span style="COLOR: #c00000">return</span>; } task._visited = <span style="COLOR: #008000">1</span>; <span style="COLOR: #707070"> // find out and visit all dependencies.</span> <span style="COLOR: #0000c0">var</span> dependencies = [], i; task.dependencies.forEach(<span style="COLOR: #b000b0">function</span>(dependency) { i = _findTask(queue, dependency); <span style="COLOR: #c00000">if</span>(i != -<span style="COLOR: #008000">1</span>) { dependencies.push(queue[i]); } }); dependencies.forEach(<span style="COLOR: #b000b0">function</span>(t) { _visit(queue, t); }); <span style="COLOR: #c00000">if</span>(task._visited === <span style="COLOR: #008000">1</span>) { _sorted[index].push(task); } }, _start = <span style="COLOR: #b000b0">function</span>(queue) { queue.forEach(<span style="COLOR: #b000b0">function</span>(task) { _visit(queue, task); }); }, _sort = <span style="COLOR: #b000b0">function</span>(suppress) { <span style="COLOR: #c00000">for</span>(index = LEVEL_LOW; index var queue = Q[index]; _sorted[index] = []; _start(queue); <span style="COLOR: #c00000">if</span>(!suppress && queue.length > _sorted[index].length) { <span style="COLOR: #c00000">throw</span> <span style="COLOR: #c00000">new</span> <span style="COLOR: #606060">Error</span>(<span style="COLOR: #008000">'Cycle found in queue: '</span> + queue); } } };
我们将按任务指定的依赖关系对同一优先级内的任务进行排序,确保被依赖的任务在设置依赖的任务之前运行。这是一个典型的深度优先的拓扑排序问题,维基百科提供了一个深度优先排序算法,大致描述如下:

图片来自维基百科
- 访问待排序的每一个节点
- 如果已经访问过了,则返回
- 否则标记为已访问
- 找出它连接(在这里是依赖)的每个节点
- 跳到内层1递归访问这些节点
- 访问完了就把当前节点加入已排序列表
- 继续访问下一个
如果 A 依赖 B, B 依赖 C, C 依赖 A, 那么这 3 个节点形成了循环依赖。 文中指出这个算法并不能检测出循环依赖。通过标记节点是否已访问,可以解决循环依赖造成的递归死循环。我们来分析一下循环依赖的场景:
从节点 A 出发的时候,它被标记为已访问,当从节点 C 再回到节点 A 的时候,它已经被访问过了。不过这个时候 C 并不知道 A 是否在自己的上游链上,所以不能直接判定发生了循环依赖,因为 A 可能是其他已“处理”(跑完了内层递归)过的节点。如果我们知道节点是不是第一次被访问过,就可以判断是哪一种情况。
改造一下上面的算法,将“是否已访问”改成“访问计数” (<font face="新宋体">task._visited++</font>
)。仅当节点被访问过 1 次的时候 (<font face="新宋体">task._visited === 1</font>
),才将其加入到已排序列表,全部遍历完之后,如果待排序的节点数比已排序的多 (<font face="新宋体">queue.length > _sorted[index].length</font>
),则表明待排序中多出的节点发生了循环依赖。
至此,队列管理组件的编码实现已经完成。什么?怎么使用?很简单啦:
<span style="COLOR: #0000c0">var</span> t1 = <span style="COLOR: #c00000">new</span> SmartQueue.Task(<span style="COLOR: #b000b0">function</span>() { alert(<span style="COLOR: #008000">"Hello, world!"</span>); }), t2 = <span style="COLOR: #c00000">new</span> SmartQueue.Task(<span style="COLOR: #b000b0">function</span>() { alert(<span style="COLOR: #008000">"High level task has name"</span>); }, <span style="COLOR: #008000">2</span>, <span style="COLOR: #008000">'myname'</span>); t1.register(); t2.register(); SmartQueue.fire();
更多功能,如任务的依赖,等待你去发掘哦。
本期贴出的代码都是一些局部片段,部分 helper 方法代码没有贴出来。查看完整的代码请访问这里。后面我们将介绍如何管理组件文件,以及构建组件,下期不见不散哦。

실제 세계에서 JavaScript의 응용 프로그램에는 서버 측 프로그래밍, 모바일 애플리케이션 개발 및 사물 인터넷 제어가 포함됩니다. 1. 서버 측 프로그래밍은 Node.js를 통해 실현되며 동시 요청 처리에 적합합니다. 2. 모바일 애플리케이션 개발은 재교육을 통해 수행되며 크로스 플랫폼 배포를 지원합니다. 3. Johnny-Five 라이브러리를 통한 IoT 장치 제어에 사용되며 하드웨어 상호 작용에 적합합니다.

일상적인 기술 도구를 사용하여 기능적 다중 테넌트 SaaS 응용 프로그램 (Edtech 앱)을 구축했으며 동일한 작업을 수행 할 수 있습니다. 먼저, 다중 테넌트 SaaS 응용 프로그램은 무엇입니까? 멀티 테넌트 SAAS 응용 프로그램은 노래에서 여러 고객에게 서비스를 제공 할 수 있습니다.

이 기사에서는 Contrim에 의해 확보 된 백엔드와의 프론트 엔드 통합을 보여 주며 Next.js를 사용하여 기능적인 Edtech SaaS 응용 프로그램을 구축합니다. Frontend는 UI 가시성을 제어하기 위해 사용자 권한을 가져오고 API가 역할 기반을 준수하도록합니다.

JavaScript는 현대 웹 개발의 핵심 언어이며 다양성과 유연성에 널리 사용됩니다. 1) 프론트 엔드 개발 : DOM 운영 및 최신 프레임 워크 (예 : React, Vue.js, Angular)를 통해 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축합니다. 2) 서버 측 개발 : Node.js는 비 차단 I/O 모델을 사용하여 높은 동시성 및 실시간 응용 프로그램을 처리합니다. 3) 모바일 및 데스크탑 애플리케이션 개발 : 크로스 플랫폼 개발은 개발 효율을 향상시키기 위해 반응 및 전자를 통해 실현됩니다.

JavaScript의 최신 트렌드에는 Typescript의 Rise, 현대 프레임 워크 및 라이브러리의 인기 및 WebAssembly의 적용이 포함됩니다. 향후 전망은보다 강력한 유형 시스템, 서버 측 JavaScript 개발, 인공 지능 및 기계 학습의 확장, IoT 및 Edge 컴퓨팅의 잠재력을 포함합니다.

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

Python은 데이터 과학 및 기계 학습에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 간결한 구문 및 풍부한 라이브러리 생태계로 유명하며 데이터 분석 및 웹 개발에 적합합니다. 2. JavaScript는 프론트 엔드 개발의 핵심입니다. Node.js는 서버 측 프로그래밍을 지원하며 풀 스택 개발에 적합합니다.

JavaScript는 이미 최신 브라우저에 내장되어 있기 때문에 설치가 필요하지 않습니다. 시작하려면 텍스트 편집기와 브라우저 만 있으면됩니다. 1) 브라우저 환경에서 태그를 통해 HTML 파일을 포함하여 실행하십시오. 2) Node.js 환경에서 Node.js를 다운로드하고 설치 한 후 명령 줄을 통해 JavaScript 파일을 실행하십시오.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경
