Photoshop CC JavaScript参考手册实战:ExtendScript脚本与批处理应用
2026/9/18 5:14:51 网站建设 项目流程

简介:《Adobe Photoshop CC JavaScript Scripting Reference》是Adobe官方发布的2019年版JavaScript脚本编程参考,适用于Windows和Macintosh平台,面向希望通过脚本实现自动化批处理、定制扩展功能的Photoshop深度用户和插件开发者。文档系统覆盖Photoshop脚本对象模型、Application/Document/Layer等核心对象,以及图层、通道、蒙版、选区、滤镜、色彩模式等API控制方法,并深入讲解事件驱动编程模型,帮助脚本对用户操作和文档事件做出动态响应。同时提供JavaScript基础语法讲解和多个任务示例,便于初学者理解变量、流程控制与函数在实际脚本中的应用。该PDF为单文件文档,压缩包约2.19MB,虽为英文原版,但章节结构清晰、术语完整,适合具备一定JavaScript基础的读者进阶查阅。目前已有1595人浏览学习,是学习Photoshop脚本开发和自动化工作流的实用参考。

1. 一份 2019 年的 PDF,为什么现在还有人翻出来用

Photoshop 的自动化脚本写过几年的老手都知道,官方文档最实用的一直不是那几千页的说明,而是按版本号打包的 JavaScript 参考手册。photoshop-cc-javascript-ref-2019.pdf就是 Adobe 随 Photoshop CC 2019 发布的那份 ExtendScript 与 DOM API 文档,内容涵盖appdocumentlayerFileFolder及 Action Manager 相关接口。很多人以为 CC 版本过时了,实际上 2019 之后的大多数批处理脚本,仍然在这份参考的覆盖范围之内。

这份 PDF 解决的是三类问题:不知道某个对象有哪些属性、不知道某个方法要传什么参数、脚本跑起来报错却看不懂错误码。它特别适合那些维护旧脚本、做批量导出、以及把 Photoshop 当作批处理引擎的工程师。正文不会复述整份文档,而是讲一套在这份参考上快速做事的方法:怎么读对象模型、怎么写可复现的脚本、遇到Error: An unknown error occurred时按什么顺序排查。你手上有没有这份 PDF 都无所谓,按本文的代码和参数说明,照样能把事情做出来。

2. 读懂 Photoshop CC JavaScript 引用手册:DOM、BatchPlay 与 ExtendScript 的边界

2.1 这份指南在 2019 版里到底收录了什么

先明确一个很多人搞混的点:photoshop-cc-javascript-ref-2019.pdf不是 ExtendScript 语言参考,而是 Photoshop 的 DOM(文档对象模型)参考。它和 ExtendScript 是两层东西。DOM 层负责操作文档、图层、通道、路径、导出选项;ExtendScript 层负责语法、数据类型、File/FolderSocket等宿主能力。调试时如果报错信息提到ReferenceError: $ is not defined,那属于 ExtendScript 语法层问题;如果报Error 24: Object is invalid,那属于 DOM 层对象失效,两者排查方向完全不同。

2019 版对应的是 API Version 2019,里面有几个值得注意的版本特征。app.version会返回如20.0这类的四位版本号,而app.ref.appVersion返回的是用户可见的2019字符串。脚本里判断运行环境时,我一般先读app.version.charAt(0)再比对主版本。这套判断逻辑在后续的 2020、2021 里依然兼容,但如果你拿到的是简体中文版手册,要注意app.preferencesrulerUnits的默认值受到界面语言和首选项影响,直接读属性有时候不准。

能力域手册中的主要对象常见用途
文档操作app.activeDocumentDocument新建、打开、保存、导出
图层操作LayerLayerSet遍历、复制、合并、设置效果
选区与通道SelectionChannel读取像素、存储选区
文件系统FileFolderopenDialog批量文件处理
动作模拟ActionManagerexecuteAction调用菜单命令与插件功能

2.2 从 docRef 到 app.activeDocument:先厘清对象模型

用这本手册最快的切入方式不是从头读,而是先理解它给出的docRef约定。手册里大量示例以var doc = app.activeDocument;开头,后续所有操作都挂在这个 doc 上。这样做有两个实际原因:一是避免每次访问app.activeDocument时触发一次宿主调用,性能差异在小脚本里无所谓,但在千张图片的批处理里会被放大;二是中间如果执行了doc.close(),再访问app.activeDocument可能已经指向别的文档,脚本就错乱了。

对象模型有一条清晰的层级链:app->document->layer/channel/historyState-> 属性与动作。写脚本时遵循一个原则:尽量拿到对象引用后就存成局部变量,不要反复从根节点往下查。下面是一个最小可跑的示例:

#target photoshop // 遍历当前文档所有图层的名称 var doc = app.activeDocument; var layers = doc.layers; for (var i = 0; i < layers.length; i++) { $.writeln(layers[i].name); }

这段代码的作用是把当前选中文档的所有顶层图层名打印到控制台。#target photoshop指示 ExtendScript 引擎把脚本路由到 Photoshop 宿主,而不是 Illustrator 或其他应用程序;doc.layers返回的是一个数组对象,layers.length给出顶层图层数量。注意这里只遍历了顶层图层,如果文档里有图层组,组内图层不会出现在这个数组里,必须递归处理,这在后面章节展开。

2.3 引用了手册却不生效:先分清 ExtendScript 与 ScriptUI

2.3.1 两种运行环境与调试入口

Photoshop 的 JavaScript 脚本可以运行在两种环境:一是 ExtendScript Toolkit(旧版)或 Visual Studio Code 搭配插件,二是 Photoshop 内置的“脚本事件管理器”。2019 版把脚本菜单路径放在“文件 > 脚本”下,脚本文件放对目录后可以出现在菜单底部。手动运行脚本的方式是File > Scripts > Browse,选到.jsx文件即可。调试时我一般不用菜单,而是用以下方式建立快捷入口:

$.writeln("script start: " + new Date()); $.writeln("photoshop version: " + app.version);

$.writeln是 ExtendScript 提供的全局方法,输出会出现在 ESTK 或 VS Code 调试控制台里。排版时需要注意,这行代码如果以//注释开头会被视为注释,不会有输出。常见错误是把$误写成$_,导致抛ReferenceError,这属于运行时环境差异,不是 Photoshop 本身的问题。

2.3.2 一个最小可跑脚本的解剖
#target photoshop #target engine "ecma3" var doc = app.documents.add(800, 600, 72, "ref-test", NewDocumentMode.RGB); var layer = doc.artLayers.add(); layer.name = "my-layer"; app.activeDocument = doc;

#target engine "ecma3"指定使用 ECMAScript 3 语法解析,这是 ExtendScript 的根本约束。后续代码里不能使用letconst、箭头函数、模板字符串这类 ES6 语法,否则在 ESTK 里直接报语法错误。手册中所有示例代码都是 ES3 风格,这是很多新手照搬现代 JavaScript 写法后脚本无法运行的原因。NewDocumentMode.RGB是枚举值,对应新建文档的颜色模式;如果不传这个参数,文档会用上次默认模式创建,可能产生预期外的颜色空间。

提示:2019 版及之前版本对 ES3 的兼容是刚性的,Array.prototype.forEach可用,但Array.from不可用。判断一个语法是否安全,就看它是否出现在 ES3 规范里。

3. 照着 ref 写脚本:文档操作、图层遍历与参数传入

3.1 遍历图层的三种写法与性能差异

图层遍历是批处理脚本里出现频率最高的需求。手册里Layer对象同时存在layers属性和artLayers属性,前者返回包含图层组在内的混合集合,后者只返回普通图层。三种写法的选择依据是你要不要处理嵌套结构。

第一种是最简单的顺序遍历,适合只处理顶层普通图层的场景:

var doc = app.activeDocument; var layers = doc.artLayers; for (var i = 0; i < layers.length; i++) { if (layers[i].kind == LayerKind.TEXT) { layers[i].textItem.contents = "updated"; } }

这段代码把所有顶层普通文本框内容改成updatedLayerKind.TEXT是一个枚举常量,比较每个图层的kind属性。逻辑上要注意,遍历的同时修改textItem.contents并不会改变图层数量,所以这里是安全的;但如果遍历过程中要删除或移动图层,就必须倒序遍历,否则数组下标会错位。

第二种是递归遍历,处理图层组嵌套:

function processLayers(container) { for (var i = 0; i < container.layers.length; i++) { var layer = container.layers[i]; if (layer.typename == "LayerSet") { processLayers(layer); } else { $.writeln(layer.name); } } } var doc = app.activeDocument; processLayers(doc);

递归写法的关键在于判断typename。手册里LayerLayerSet都在layers集合中,typename属性可以帮助区分。要注意container.layers返回的是索引从 0 开始的数组,而且数组长度是动态读取的,在递归过程中如果脚本逻辑修改了图层结构,index会重新计算,导致部分图层被跳过,这是递归遍历最常见的踩坑点。

第三种是使用doc.layers配合for...in遍历,这个写法不推荐,因为for...in会枚举原型链上的可枚举属性,结果里混入非图层字段,且顺序不可控。我写脚本时只在前两种之间选择,优先递归。

3.2 把“参数怎么设”翻译成可读的代码

查阅手册时大家最关心的就是方法签名里的参数说明。拿doc.exportDocument来说,它接收两个参数:exportFileExportOptions子类对象。不同导出格式对应不同的 Options 类,手册里给的是ExportOptionsSaveForWebExportOptionsPNG24等。参数设错时最常见的报错是Error 25: Parameter is not valid,这通常意味着 Options 对象里给了一个不在枚举值范围内的常量。

写参数设置代码时要学会用“局部对象 + 属性赋值”的模式:

var doc = app.activeDocument; var file = new File("/path/to/output.png"); var options = new ExportOptionsSaveForWeb(); options.format = SaveDocumentType.PNG; options.PNG8 = false; options.quality = 100; options.transparency = true; doc.exportDocument(file, ExportType.SAVEFORWEB, options);

这段代码把当前文档以 PNG 格式导出。new ExportOptionsSaveForWeb()在手册里有明确的构造函数说明,它是ExportOptions的子类,专门服务于ExportType.SAVEFORWEBoptions.quality = 100看着直观,实际上ExportOptionsSaveForWebquality只在导出 JPEG 时才有意义,PNG 模式下会被忽略。许多从旧版脚本迁移过来的代码把options.PNG8 = false当成立即生效的设置,其实它只对后续的exportDocument调用有效。参数设置完成后,exportDocument是同步阻塞操作,大图导出期间 UI 会卡住,这是正常现象。

3.3 批量处理时的 Document 与 Action 混用

批量场景下,纯 DOM 操作往往慢,因为每个属性访问都走一次宿主桥接。手册里虽然没有直接给出性能对比,但从executeAction的说明可以推断:如果某个操作在 Adobe 的“动作”面板里能录制,就存在对应的 ActionDescriptor 调用方式。用 Action Manager 写批量脚本比 DOM 快,但代码可读性差。

var batchFile = new Folder("/path/to/images").getFiles("*.jpg"); for (var i = 0; i < batchFile.length; i++) { var doc = app.open(batchFile[i]); doc.resizeImage(800, null, 72, ResampleMethod.BICUBIC); doc.close(SaveOptions.SAVECHANGES); }

这里的doc.resizeImage是 DOM 方法,相当于在 UI 里执行“图像大小”对话框。参数顺序是宽度、高度、分辨率、重采样方式,null表示按比例缩放时由 Photoshop 根据原图自动计算高度。很多脚本死在这里是因为没注意SaveOptions.SAVECHANGES配合doc.close时,会弹出覆盖确认对话框,导致脚本挂起等待用户输入。正确做法是在脚本开头设置app.displayDialogs = DialogModes.NO;,关闭所有对话框。

app.displayDialogs = DialogModes.NO; // 所有后续操作不弹对话框

这一行是几乎所有批量脚本的标配。DialogModes.NO枚举值把 Photoshop 的交互模式切换为静默模式,否则close时弹窗会阻塞执行。注意这个设置是全局状态,如果脚本后续还想弹出某个对话框,需要临时改回DialogModes.ALL

4. 文件读写、批处理与运行时报错排查

4.1 File 与 Folder 对象:跨平台路径要避开的坑

手册里的FileFolder是 ExtendScript 的全局对象,它们不依赖 Photoshop,但批处理脚本打交道最多。跨平台兼容性集中在路径分隔符上:Windows 用反斜杠\,macOS 用斜杠/。ExtendScript 两者都接受,但不做自动归一化。new File("/path/to/file")在 Windows 上会被解析为当前盘符下的\path\to\file,往往不是想要的位置。

一个可靠做法是用Folder.selectDialogFile.openDialog让用户选路径,避免硬编码:

var inputFolder = Folder.selectDialog("选择源图片文件夹"); var outFolder = Folder.selectDialog("选择导出文件夹"); var files = inputFolder.getFiles(/\.(jpe?g|png)$/i);

getFiles接受正则表达式,注意i标志在 ExtendScript 里可用,但写法上要把整个正则作为参数传入。返回的数组元素是File对象,不是字符串;用file.fsName获取系统原生路径,用file.name获取文件名,用file.parent获取所在文件夹。这三个属性在拼接输出路径时非常常用。检查脚本为什么找不到文件时,先打印这三个值中的一个,确认当前工作目录和实际文件位置是否一致。

目录操作上有一个容易忽略的点:Folder对象的exists属性在路径不存在时返回false,但create()方法不会自动创建多级父目录。需要递归创建时,用Folder("/a/b/c").create()前先确认/a/b已存在,否则会返回false且不产生目录。手册里对create的说明只有一句“Creates a folder”,没有提多级目录行为,所以我在批处理脚本里干脆每次都把路径结构保证好。

4.2 报错信息怎么读:-25、-3 与常见错误码

Photoshop 的 ExtendScript 报错格式统一为Error: 错误信息 (错误码),其中错误码是关键。手册在开头部分给出了一张错误码表,但实际运行中最常遇到的就这么几个:

错误码含义常见触发场景
-25参数无效ExportOptions的枚举值传了不存在的字符串
-3文件未找到File对象指向不存在的路径
-36未定义名称访问了doc.layers中不存在的图层名
-205用户取消Folder.selectDialog被点击了取消
Error 24对象无效文档已关闭但仍持有其引用

-25的排查思路是逐条检查传给方法的参数类型和枚举值。比如doc.resizeImage(800, null, 72, ResampleMethod.BICUBIC)null是允许的,但如果把第二个参数改成0,某些版本会直接报参数错误,因为 0 在语义上不等于“不指定”。Error 24则是最阴间的一个,它一般出现在跨函数传递Document引用时:一个函数里close()了文档,另一个函数还在用之前存下的引用修图。排查时先看脚本里所有doc.close()之后的代码是否还引用了同一个 doc。

还有一种Error: An unknown error occurred没有错误码,多半发生在app.open打开损坏文件时,或者doc.exportDocument的输出路径没有写入权限。对于输出路径无权限的情况,先检查目标文件夹是否在实际存在的位置,不要假设脚本运行目录就是当前工程目录。

4.3 用日志与断点验证脚本行为

脚本报错时不要盯着消息看,直接加日志。最实用的是下面这段包装:

var logFile = new File("~/Desktop/script-log.txt"); function log(msg) { logFile.open("a"); logFile.writeln(new Date().toLocaleString() + " " + msg); logFile.close(); }

手动运行脚本时$.writeln输出到控制台,但批处理场景下控制台不可见,写到日志文件才能留痕。log()函数每次调用打开文件追加一行再关闭,日志行数多时性能差,但排错期间无妨。正式使用时可以改成把内容累积到数组、最后一次性write

关于断点,ESTK 里可以在行号处打断点,但前提是脚本文件路径里不含中文或空格,否则断点经常失效。VS Code 配 ExtendScript 插件后调试体验稍好,但要确认插件使用的宿主版本能匹配 Photoshop 2019 的远程调试端口。更快的替代方案是故意制造异常,用throw new Error("阶段标记 " + i)来定位是循环里的哪一次崩溃。

try { doc.exportDocument(file, ExportType.SAVEFORWEB, options); } catch (e) { log("导出失败: " + e + " file=" + file.fsName); }

把可能失败的调用放进try/catch里,日志里会带上文件路径。多文件批处理时,一个文件失败不能中断整个批次,这个模式几乎是必须的。

5. 顺着引用手册反向查方法:一个提高效率的搜索模式

不需要通读整本 PDF,而是把手册当作查表工具,用“目标功能 -> 英文动词 -> 手册条目”这个顺序反向检索。想出“把图层复制到新文档”这个动作,先拆成主对象Layer、动作duplicate,然后在手册索引里找duplicate。手册的目录结构是按对象字母排序的,layer.duplicatedocument.duplicate是两个不同条目,前者多一个目标文档参数。

这个方法最高效的一个变体,是直接看手册里每个方法下面的 “Example” 段落。2019 版手册在主要方法后面都带示例代码,这些示例往往比正文描述更能解释参数意图。看layer.move(relativeObject, ElementPlacement.PLACEATBEGINNING)时,先跑一遍示例,再改成自己需要的参数。这里的ElementPlacement枚举有四个值:PLACEATBEGINNINGPLACEATENDPLACEATBEFOREPLACEATAFTER,前两个是绝对位置,后两个关联到relativeObject,弄混时图层顺序会和预期相反。

排查“为什么脚本能跑但结果不对”这类问题时,手册帮不上太多忙,但有一个模式值得养成:把操作涉及的对象ref在操作前打印出来。比如:

var layer = doc.artLayers.add(); $.writeln(ref(layer));

ref()是 ExtendScript 提供的全局函数,返回对象的标识字符串,能确认操作的是不是同一个图层。它输出的格式类似[object ArtLayer],如果打印内容是[object Object]undefined,说明引用已经失效。每次在对象操作前后各打印一次,对比引用是否变化,能定位一大半的“操作没生效”问题。这是往 5 年以上经验级别靠的那类技巧,表面上只多了一行,实际上把排错成本减半了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询