ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Ruby 与 JavaScript 核心机制对比指南:Block、函数、对象模型与事件驱动

Ruby 与 JavaScript 核心机制对比指南:Block、函数、对象模型与事件驱动 Ruby 与 JavaScript 核心机制对比指南Block、函数、对象模型与事件驱动【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum本文是 The Odin Project 开放课程GitHub 仓库cu/curriculum旧版 JavaScript 路径中的一篇专题笔记基于 comparing_to_ruby.md 展开。它以Ruby 学习者转学 JavaScript为视角系统对比两种语言在代码块、函数、对象、单线程模型与隐式接收者this/self上的异同。读完本文你将理解 Ruby 的 Block/Proc/Lambda 与 JavaScript 匿名函数之间的对应关系、函数作为一等公民的传递方式以及为什么事件驱动是 JavaScript 在浏览器中生存的根基。为什么需要这样一篇对比文档原文档开篇就给出了一句高度凝练的概括JavaScript 几乎处处都在使用函数而且它必须花大量时间等待事情发生Javascript uses functions eeeeeeverywhere and it has to wait a lot for things to happen。这句话点出了 Ruby 与 JavaScript 在编程哲学上的两大分水岭函数中心主义JavaScript 的几乎所有抽象作用域、复用、异步、模块都建立在函数之上等待模型JavaScript 大量依赖回调、事件与异步机制来处理等待 I/O 完成这件事。对于已经完成 Ruby 基础课程 的学习者而言这篇对比文档的作用不是重讲语法而是建立跨语言的思维映射在 Ruby 中遇到的BlockProcself对象分别对应 JavaScript 中的什么原文档也坦诚说明即使没学过 Ruby对比两种语言也能帮你抓住编程语言之间普遍的相似性。Ruby 的 Block 与 JS 的匿名函数两段可传递逻辑的异同原文档的学习目标首先提出的问题是Ruby 的 Block 在多大程度上像 JavaScript 的匿名函数Ruby 侧Block 是方法的附带代码块在 Ruby 进阶构建块课程 的 Blocks, Procs, and Lambdas 一节中课程要求学习者弄清这些问题Block 有哪两种写法、yield做什么、如何给 Block 传参、如何判断是否传入了 Block。Ruby 的 Block 有两种声明方式花括号形式与do...end形式且必须由方法通过yield或显式block参数来接收# 花括号形式 [1, 2, 3].map { |n| n * n } # [1, 4, 9] # do...end 形式 [1, 2, 3].each do |n| puts n end # yield 将控制权交给 Block def twice yield if block_given? yield if block_given? end twice { puts hello } # 打印两次 hello关键差异在于Ruby 的 Block 本身不是对象。它不能脱离方法独立存在、不能赋值给变量这也是 Ruby 需要 Proc、Lambda 与Method对象来物化代码块的原因——正如课程学习成果所问什么是 procproc 与 block 有何区别何时用 proc 而不是 blockJavaScript 侧匿名函数本身就是一等公民JavaScript 没有Block这一特殊语法概念取而代之的是函数表达式。一个匿名函数可以立即作为参数传入也可以赋值给变量// 匿名函数作为参数 [1, 2, 3].map(function (n) { return n * n; }); // [1, 4, 9] // 匿名函数赋值给变量函数表达式 const square function (n) { return n * n; };事件驱动课程 专门用 jQuery 的点击事件拆解了匿名函数的用法$(#link).click(function(){ /* ... */ })与先定义具名函数function doStuff(){...}再传入$(#link).click(doStuff)在本质上是同一回事区别仅在于匿名函数无法被复用。该课程的结论可作为通用准则不打算复用的逻辑写成匿名函数需要复用的逻辑具名化。相似与差异的总结维度Ruby BlockJS 匿名函数是否为对象否需转为 Proc/Lambda是函数即对象天然一等公民如何接收方法通过yield/block高阶函数直接以参数接收能否赋值给变量不能需用 Proc/Lambda/Method 包装能函数表达式 / 箭头函数典型场景迭代器each、map、select回调、数组方法map、filter、forEach两者共享的核心心智模型是把一段逻辑作为值传递给另一段代码由接收方决定何时执行它。这正是 Ruby 的each/map与 JS 的数组方法能够一一对应的原因。函数如何保存到变量并四处传递原文档第二个核心问题是JavaScript 的函数如何像 Ruby 的方法那样被保存到变量并传递Ruby 中方法这个概念比较特殊方法默认挂在对象上要让方法本身变成一个可传递的值需要借助显式的包装对象。在进阶课程的学习成果中这一点体现为一组问题什么是 LambdaLambda 与 Proc 有何区别什么是Method大写 M它允许你做哪些有趣的事square -(n) { n * n } # Lambda square.call(5) # 25 add proc { |a, b| a b } # Proc add.call(3, 4) # 7JavaScript 则简单直接得多函数就是值。你可以用函数表达式、箭头函数保存它再把它作为参数、返回值到处传递——这也正是 深入函数课程 中模块模式module pattern与命名空间思想的基石既然函数能保存为变量就能把一组相关的函数与变量组织进一个对象/闭包中实现代码的模块化与命名空间隔离。// 函数保存到变量 const square (n) n * n; // 函数作为参数传递回调 function applyTwice(fn, value) { return fn(fn(value)); } applyTwice(square, 3); // 81 // 函数作为返回值闭包/模块模式的基础 function makeCounter() { let count 0; return function () { count 1; return count; }; }这里的关键认知是把函数当值传递在 JavaScript 中是零成本的默认行为而在 Ruby 中需要显式的 Proc/Lambda/Method 包装。两门语言最终都能实现同样的效果只是语法与心智负担不同。单线程与事件驱动Ruby 模式在浏览器中的致命短板原文档提出了一个非常尖锐的问题Ruby 的单线程特性在浏览器中会在哪里真正吃亏并给出了提示——想想事件驱动Think event-driven!。问题背景Ruby 程序在典型的使用方式下是同步 阻塞的调用一个慢操作时当前线程会停下来等待结果返回再继续执行下一条语句。这种模型在服务器端脚本中是可以接受的但在浏览器里会直接导致灾难性的体验UI 冻结——页面无法响应点击、滚动、输入因为 JavaScript 主线程被卡住了。这正是单线程模型在浏览器环境中的真正软肋。JavaScript 的答案事件驱动与回调JavaScript 同样运行在单线程上但它通过事件驱动 回调绕开了阻塞问题。事件驱动课程 给出了回调的经典定义回调就是一个作为参数传给另一个函数的函数。该课程用一段最简代码说明其本质function firstFunction(callback) { callback(hello); } function secondFunction(string) { console.log(string); } firstFunction(secondFunction); // 控制台输出 hello课程强调了一个反直觉的要点回调的返回值往往不重要重要的是回调内部执行的副作用数据不是被return回来而是通过回调向前抛送供未来某个时刻消费。这也解释了为什么事件监听器如click自身的返回值远不如其中的回调函数重要。AJAX 课程 则把这一模型延伸到了网络层AJAX 在等待服务器响应期间不会阻塞浏览器执行其他活动——你给 AJAX 函数传入一个回调服务器响应到达后再触发回调继续后续逻辑。这正是 Ruby 同步模型所不具备的能力也是Ruby 的单线程在浏览器里会吃亏的完整答案// 事件驱动注册回调等待未来触发 $(#link).click(function () { // 用户点击时才执行主线程无需阻塞等待 }); // 异步 I/O发起请求后立即返回响应到达时执行回调 fetch(/api/data) .then((res) res.json()) .then((data) console.log(data));一句话总结这一节的对比Ruby 与 JavaScript 都是单线程的但 JavaScript 用事件循环 回调把等待变成了非阻塞若把 Ruby 的同步阻塞思维搬进浏览器页面就会冻结——这就是事件驱动四个字对 Web 开发的真正意义。对象模型Ruby 对象与 JS 对象的相似性原文档问到JavaScript 的对象与 Ruby 的对象有多像答案是在动态键值集合 方法附着于对象这个层面两者非常相似。Ruby 的 Hash/对象用键存取数据、方法通过self访问自身状态JavaScript 的对象字面量同样用键值对描述数据函数作为属性挂载后即成为方法。仓库中的 对象与原型课程 系统讲解了 JS 对象与原型链的机制可与之对照# Ruby属性与方法绑定在对象上 class Dog attr_accessor :name def initialize(name) name name def bark #{name} says woof! end// JavaScript对象字面量 方法 const dog { name: Rex, bark() { return ${this.name} says woof!; }, };从语法到心智模型两门语言的对象都遵循数据 行为的封装思路主要差异在于继承体系Ruby 是类继承JS 是原型链继承与对象创建的显式程度。对学习者来说把 Ruby 的对象 状态 方法心智迁移到 JS 上是顺畅的。this与self相似的名字不同的语义原文档的最后一个学习目标是this与 Ruby 的self有何相似又有何不同相同点都指当前接收者在 Ruby 中self表示当前上下文中的接收者receiver方法内指调用该方法的对象类定义中指类本身。在 JavaScript 的方法式调用中this也指向调用该方法的对象——这是两者最直观的相似之处。class Dog def bark #{self.name} says woof! # self 就是调用 bark 的那个实例 end endconst dog { name: Rex, bark() { return ${this.name} says woof!; // 方法式调用时 this 指向 dog }, }; dog.bark(); // Rex says woof!不同点this取决于如何调用而不是定义在哪里作用域与闭包课程 明确指出了 JavaScriptthis的核心难点函数的调用方式会直接影响this的取值How does the way you call a function (e.g. function style, method style...) affect its scope (andthis)。同样是函数用函数式调用、用方法式调用、用call/apply/bind显式绑定this会指向完全不同的对象function show() { console.log(this); } show(); // 函数式调用this 为全局对象严格模式下为 undefined obj.show(); // 方法式调用this 指向 obj show.call(obj); // call显式把 this 绑定为 obj const bound show.bind(obj); // bind永久绑定 this这正是this与 Rubyself的关键分歧Ruby 的self在绝大多数情况下稳定可预期而 JS 的this是调用时绑定call-site binding的。作用域课程还收录了两个经典应对技巧用bind把函数绑定到指定作用域或在闭包中定义var that this;来捕获外层this——后者在回调嵌套中尤其常见因为在普通函数回调里this会丢失外层上下文。后来引入的箭头函数词法this正是对这一痛点的语法级修复。// 经典技巧用 that 捕获外层 this function Widget() { const that this; this.items []; fetchData(function (data) { that.items data; // 回调里 this 会变所以用 that }); }学习路径建议与总结对照表原文档在其 Assignment 环节要求学习者对照外部资料完成练习并以Additional Resources补充扩展内容。本仓库不再包含这些外部资源但提供了完整的替代学习路径建议按如下顺序阅读先掌握 Ruby 侧的代码块与闭包阅读 Ruby 进阶构建块课程重点看 Blocks, Procs, and Lambdas 部分再掌握 JS 侧的函数与作用域深入函数课程模块模式、apply/call与 作用域与闭包课程this、bind、that技巧最后理解浏览器中的异步模型事件驱动课程回调、事件与 AJAX 课程非阻塞请求。核心对照速查对比维度RubyJavaScript一段可传递的逻辑Block非对象需 Proc/Lambda/Method 物化匿名函数 / 函数表达式本身即对象接收方式方法内yield或block参数高阶函数参数直接接收函数作为值需要显式包装proc、lambda、method原生一等公民天然可保存、传递、返回并发模型同步阻塞为主单线程下等待会卡住单线程 事件循环回调/异步非阻塞隐式接收者self由上下文稳定决定this由调用方式决定可用call/apply/bind显式绑定对象模型类继承对象承载状态与行为原型链继承对象字面量 方法最终记住原文档的那句总结即可建立整体认知JavaScript 是函数主义语言一切皆围绕函数展开同时它又是等待型语言回调、事件与异步是它在浏览器单线程环境中的生存方式。带着这两条主线去对比 Ruby两门语言的语法差异就不再是零散知识点而是一套可迁移的编程心智模型。【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表