惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
Recent Commits to openclaw:main
Recent Commits to openclaw:main
H
Heimdal Security Blog
SecWiki News
SecWiki News
H
Hacker News: Front Page
N
News | PayPal Newsroom
L
LINUX DO - 最新话题
N
News and Events Feed by Topic
TaoSecurity Blog
TaoSecurity Blog
AI
AI
C
Cybersecurity and Infrastructure Security Agency CISA
Scott Helme
Scott Helme
PCI Perspectives
PCI Perspectives
S
Securelist
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Cyberwarzone
Cyberwarzone
A
Arctic Wolf
Forbes - Security
Forbes - Security
T
Tor Project blog
Spread Privacy
Spread Privacy
WordPress大学
WordPress大学
I
Intezer
Martin Fowler
Martin Fowler
Help Net Security
Help Net Security
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Cisco Talos Blog
Cisco Talos Blog
Latest news
Latest news
博客园 - 司徒正美
W
WeLiveSecurity
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX
P
Palo Alto Networks Blog
Google DeepMind News
Google DeepMind News
IT之家
IT之家
阮一峰的网络日志
阮一峰的网络日志
V
Vulnerabilities – Threatpost
Jina AI
Jina AI
S
Security Affairs
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
Project Zero
Project Zero
T
Threatpost
P
Privacy International News Feed
人人都是产品经理
人人都是产品经理
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research

夜行人

回家路上 第一期的直播演示项目 震动检测器 正能量 在线参观CodeLab Neverland 发布 CodeLab Adapter 3.3.1 DynamicTable 之 纸糊方向盘 CodeLab DynamicTable: 一个可实施的技术方案 CodeLab Insight 发布 Alpha 版 情人节 Home Assistant 周报 && IoT 周报 (02) Joplin: 关注隐私的 Evernote 开源替代软件 浏览器的未来与 Web 传感器 Home Assistant 周报 && IoT 周报 (01) 百宝箱(01) 论自由 介绍 WebThings Home Assistant 周报 && iot 周报 (00) 百宝箱(00) 毛姆读书心得 传世之作 周末徒步 CodeLab Adapter ❤️ Jupyter/Python 航班 躲雨 夏令营途中 [译]思想--作为一种技术 The future of coding 美国之行 三门问题的程序模拟 从Python转向Pharo https://blog.just4fun.site/post/iot/iot-open-source-projects/ Python异步编程笔记 https://blog.just4fun.site/post/iot/iot-open-source-hardware-community/ 万物积木化开发者社区 CodeLab ❤️ Blender Scratch3技术分析之云变量 API(第7篇) [译]对管道(Pipes)的偏爱 [译]提出正确的问题比得到正确答案更重要 蓝牙设备与Scratch3.0 创建你的第一个Scratch3.0 Extension Scratch3技术分析之项目内部数据(第6篇) Scratch3技术分析之社区 API(第5篇) Scratch3技术分析之User API(第4篇) Scratch3技术分析之项目主页API(第3篇) Scratch3技术分析之静态资源API(第2篇) Scratch3.0、micro:bit与Windows7 https://blog.just4fun.site/post/iot/zerynth-vs-micropython/ 核聚变、方所与半宅空间 可视化编程为何是个糟糕的主意 codelab.club周末聚会 关于codelab.club '下一件大事'是一个房间 Hungry Robot - Eat everything 编程作为一种思考方式 今日简史 史蒂夫·乔布斯传 罗素自选文集 https://blog.just4fun.site/post/edx/tianjin-scratch-ai/ https://blog.just4fun.site/post/edx/richie-cms-openedx/ 徒步武功山 WebUSB与micro:bit 积木化编程与3D场景 夜宿武功山顶 scratch3-adapter接入优必选Alpha系列机器人 https://blog.just4fun.site/post/edx/video-migration-note/ scratch3-adapter重构笔记 https://blog.just4fun.site/post/edx/edx-community-members/ 两种硬件编程风格的比较 使用micro:bit自制PPT翻页笔 柏拉图对话集 scratch3.0 + micro:bit 七月电影放映计划 非营利组织的管理 Screenly--用树莓派让任何屏幕变为可编程的数字标牌 以最佳实践开始你的Django项目 micro:bit与事件驱动 为Scratch3.0设计的插件系统(上篇) OCR应用一例 近两年读过的一些好书 blockly开发之使用python驱动浏览器中的turtle(2) 牛顿新传 文学理论入门 逻辑的引擎 人生的意义 blockly开发之生成并运行js代码(1) blockly开发之hello world(0) micro:bit使用笔记 神器之Termux https://blog.just4fun.site/post/iot/micropython-notes/ Cozmo what is this Scratch的前世今生 下段旅程 我行在远方 爆裂 途中杂记 https://blog.just4fun.site/post/edx/open-edx-startup/ cozmo系列之入门 - 有性格且可编程的机器人 PaperWeekly开发笔记 创业二三事
Snap! 使用笔记: JavaScript function
种瓜 · 2023-02-28 · via 夜行人

让简单的事情保持简单,让困难的事情变得可能 – Alan Kay

前言

和 Scratch 3.0 一样, Snap! 也基于 JavaScript 构建,运行在浏览器中。

由于浏览器正在成为新的操作系统, 新的能力层出不穷:

  • Web Serial
  • Web Bluetooth
  • WebGPU
  • WebAssembly
  • The File System Access API

如何将这些强大的能力暴露给最终用户, 是我长期感兴趣的话题。Snap! 是我目前探索这些想法时最喜欢的的实验环境。

开篇引用的 Alan Kay 的话,是我最喜欢的系统设计原则之一, Snap! 在这方面做得相当出色。

良好的设计:系统应以最少的不可变零件组成;这些部分应尽可能通用;系统的所有部分都应聚拢在一个统一的框架中。 – Smalltalk 背后的设计原则

Snap! 只通过一个通用的积木,就将浏览器的所有能力暴露了出来! 这个积木就是: JavaScript function 积木。

JavaScript function 积木允许你在 Snap! 里构建JavaScript 匿名函数, 然后在 Snap! 中调用它, 调用的过程中, 会自动转换 Snap! 和 JavaScript 的数据类型。由于函数在 JavaScript 是一等公民, 而积木(block)在 Snap! 也是一等公民, 所以这一个积木就将 JavaScript 的所有能力释放给了 Snap!

简单解释下什么是一等公民(摘自 SnapManual)。

如果一种数据类型可以作为以下事物, 则它在编程语言中是一等公民(first class):

  • 变量的值
  • 程序的输入
  • 函数返回的值
  • 数据聚合的成员
  • 匿名(未命名)

怎么用?

为了使用 JavaScript function 积木, 你需要做 3 件事:

  1. 打开权限
  2. 编写 JavaScript 函数
  3. 调用 JavaScript 函数

1. 打开权限

出于安全的考虑, Snap! 默认不允许你在积木 IDE 里使用 JavaScript, 要使用 JavaScript function 积木, 你需要先打开权限:

2. 编写 JavaScript 函数

开启权限之后,就可以在 运算 积木组里找到 JavaScript function 积木。上图编写了一个匿名函数, 参数是 what, 函数体是弹出提示这个参数值。

3. 调用 JavaScript 函数

接下来我们就可以调用它。

控制积木组有两个积木可用于执行 JavaScript function:

  • 运行
  • 调用

两者的区别是前者没有返回值, 后者有返回值(正是 command 与 reporter 的区别, 与直觉一致)。

更复杂的例子, 可以右键查看 JavaScript function 的帮助:

我的用例

使用 JavaScript function 开发 Snap! 插件

Snap! 内置了很多插件(积木库)来扩展 Snap! 的能力:

在 Snap! 里你可以在 IDE 里创建积木库(和 MicroBlocks 类似)。

有时候你定义的新积木, 无法通过组合已有的积木来构建, 这时就需要在 JavaScript 先构建 primitive(原语) , 然后在 Snap! 中调用它。 MQTT 插件 就是通过调用 mqttExtension.js 中定义的 primitive 来构建的。 CodeLab Adapter EIM 插件里也有很多积木用到了底层 primitive , 可供参考。

当我构建第一个 Snap 插件: Home Assistant 插件 (使用 Snap! 驱动可编程空间), 频繁地在 JavaScript 修改代码、刷新页面,让我非常厌烦, 我更喜欢在 Snap!(和其他个人计算环境, 如 Smalltalk、MicroBlocks)中的编程体验: 一切都是活的(liveness)! 没有重载和刷新, 整个系统的状态都是连续的! 这是个人计算环境的传统。

我当时想,要是能够在 Snap! 里直接编写 primitive 就好了, 于是我尝试使用 JavaScript function 来做,发现一切都非常愉快!

具体的开发流程是这样的: 我尽可能使用 Snap! 已有的积木来定义新的积木, 当发现有些事情无法做到, 需要底层新的原语提供支持时,我就使用JavaScript function编写一个最小的匿名函数来达成我的目标,这个过程,我可以随时重新定义这个函数,只需要点击积木而无需刷新页面!等我完成整个插件,我就将这些使用 JavaScript function 编写的 JavaScript 代码移到专门放置 primitive 代码的文件里。

尽可能少地引入 primitive 是 Smalltalk 社区编写 primitive 遵循的风格, 关于 primitive 的 big ideas, 我在MicroBlocks 与 Snap!的互操作里(视频)提到过一些

这是开发 CodeLab Adapter EIM 插件时,使用 JavaScript function 写的原型,它提供了巨大的灵活性:

这是重构为 primitive 之后:

之后所有与 CodeLab Adapter 相关的插件都是这样开发的,整个插件的开发过程中,我不需要刷新一次页面! 这样意味着我的思绪总是连贯的,没有被编写、刷新模式打断。它带来了数量级的开发效率提升。

使用 JavaScript function 探索 JavaScript API

有了前边愉快而高效的插件开发经验, 我打算走得再远一点, 将 Snap! 看作一个挖掘浏览器潜力的实验环境。 所以 Snap! 现在成了我编写实验性 JavaScript 代码的环境。

这方面很好的一个案例是将 Web Serial 封装成通用积木。

一旦完成这项工作, 之后的一切探索都可以在 Snap! 中完成,这是巨大的杠杆!

MicroBlocks 插件就完全在 IDE 里完成, 无需针对性地编写任何 JavaScript 代码, MicroBlocks 串口通信协议完全在 Snap! 里实现。只需使用 JavaScipt 编写好 Web Serial 积木,之后就可以完全工作在 Snap! 环境里了。

我目前试着对 Web Bluetooth 做类似的事, 之后也准备在 WebGPU/WebAssembly 上这样做。

采用这种方式,可以将个人计算环境的好处用于探索任何的 Web APIs, 这对研究性工作有巨大好处, 诸如你可以将当前的研究工作整个保存下来(存为项目, 类似 Smalltalk 的 image), 下次加载的时候,就好像你从未离开。

一些技巧

全局对象

我喜欢使用对象来组织代码,当我开发一个复杂插件的时候(诸如CodeLab Adapter EIM插件),会把它的主体包装在一个对象里。开发期间,我会将这个对象暴露到全局, 诸如 CodeLab Adapter EIM 插件的主要对象 window.eim_client。 这样有两个好处:

  • 随时在 Chrome DevTools 里探索它们。
  • 不同的匿名函数(JavaScript function)可以共同操作一个全局对象,这使得许多事情变得简单,诸如在不同的函数之间交换信息。

跨场景 (Scene) 传递变量

有时候我们想要跨场景传递变量, 诸如我们希望使用 MicroBlocks ble 库 连接的硬件设备在多个场景中都可用.

可以使用 JavaScript function 将隐藏变量 _ble_ide_server 放到全局空间, 这样就能够在多个场景中共享

示例代码

FAQ

如何自动开启 JavaScript 扩展

从 url 末尾添加 &enableJS, 例子:

1
2
3
https://snap.codelab.club/snap.html#open:https://wwj718.github.io/post/img/mediapipe-Gesture-Recognition-cn.xml&enableJS

https://snap.codelab.club/snap.html#run:https://wwj718.github.io/post/img/mediapipe-Gesture-Recognition-cn.xml&noRun&enableJS

这个功能可能会有安全风险, 非官方功能, 只能在 https://snap.codelab.club/snap.html 使用。 对于演示体验项目可能会很有用。

JavaScript function 可访问哪些上下文(Context)?

JavaScript function 可访问的上下文与 primitive 相似(它们在许多方面都非常相似)。 大多数全局对象都可以访问:

  • List
  • Process

JavaScript function 的 this 是 SpriteMorph.

1
console.log(this); // SpriteMorph

通过 this, 可以进一步访问其他对象

1
2
3
4
5
// 参考 JavaScript function  的 help
let stage = this.parentThatIsA(StageMorph);
// this.bubble;
// this.gotoXY 
// this.direction()

如何保存状态

函数通常是无状态的, JavaScript function 是一个函数。

如果你期望在多个函数调用中, 操作同一个状态变量, 可以把它存放在 window 全局变量里, 如 window._result = "hello"

当然你也可以把它持久化存储在 localstorage 或 indexeddb, 这样即使刷新浏览器, 也还能找到这些状态。

如何往 JavaScript function 传递回调函数

有些时候, 我们需要往 JavaScript function 传递回调函数, 如:

将以上图片下载到本地, 然后拖拽到 Snap! 中, 它会变成对应脚本。

运行结果:

如何操作变量?

将设置/获取变量的积木作为一个回调函数传递

将以上图片下载到本地, 然后拖拽到 Snap! 中, 它会变成对应脚本。

运行结果:

如何传递列表

以下代码演示如何双向传递列表:

将以上图片下载到本地, 然后拖拽到 Snap! 中, 它会变成对应脚本。

运行结果:

嵌套列表

将 js 嵌套列表转化为 Snap! 嵌套列表:

将以上图片下载到本地, 然后拖拽到 Snap! 中, 它会变成对应脚本。

将 Snap! 嵌套列表转化为 js 嵌套列表:

你可以将以上两个辅助函数放到 window 命名空间下, 方便在不同的 JavaScript function 中使用它

提醒: 对于复杂的列表, 可以将其视为 json (列表是 json 支持的数据结构, 可以嵌入其中).

如何处理 JSON

使用一些外部 API 时, 经常需要处理 JSON 数据, 比如我最近在使用 OpenAI Realtime API, 在进行为了进行 Function calling, 需要发送这样的数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "type": "session.update",
  "session": {
    "tools": [
      {
        "type": "function",
        "name": "pos",
        "description": "获取 turtle 在舞台上的xy坐标.",
        "parameters": {
          "type": "object",
          "properties": {},
          "required": ["sign"]
        }
      }
    ],
    "tool_choice": "auto"
  }
}

我想在 Snap! 中编写 json 字符串, 然后传递到 js 里, 在 js 中把它还原为对象

1
2
3
4
const event = JSON.parse(event_raw);
console.log("results of a function call: ", event)

session.dc.send(JSON.stringify(event));

此外, 你也可以基于列表构建 json , 甚至嵌套 json

如何引入第三方库?

有两种引入方式:

  • 全局引入
  • 引入模块

许多旧的库, 支持全局引入, 如 mqttjs, jquery, Pyodide…

比较新的库, 可能偏好采用模块的方式分发, 如 mediapipe…

全局引入

让我们试着引入 jquery 库:

https://code.jquery.com/jquery-3.7.1.min.js

在这个例子中, 我们首先加载了 jQuery 库, 然后使用 jQuery 内置的函数缓慢地隐藏当前的 Snap! 工作区, 之后快速显示它。

项目代码, 建议从这里开始, 将其修改为你要的样子

引入模块

参考 在 Snap! 中使用 mediapipe 库

获取移动端传感器信息

参考 Sensor APIs

目前似乎只有安卓设备支持, iOS 设备一如既往的封闭。

项目代码

运行代码之前打开 JavaScript 扩展

如何引入 Python 解释器 ?

参考 Snap! 中的 Python 解释器

参考