You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AppleScript与JXA的对象相等性:Keynote当前幻灯片定位问题

问题描述

我需要通过脚本获取Keynote活动演示文稿中选中的幻灯片,但仅用幻灯片编号不可行——所有隐藏幻灯片的编号都是-1。

AppleScript里这段代码能正常运行:

tell application "Keynote"
    log count slides of front document
    repeat with i from 1 to count slides of front document
        if item i of slides of front document is current slide of front document then return i
    end repeat
end tell 

但因为这是大型项目的一部分,用AppleScript会非常麻烦,所以尝试用JXA实现,比如下面这段代码(假设选中的是演示文稿第一张幻灯片):

var a = Application("Keynote").documents[0].slides[0];
var c = Application("Keynote").documents[0].currentSlide();

console.log(c.slideNumber() == a.slideNumber()) // True
console.log(c === a) // False

try {
    console.log(c.id())
} catch(e) {
    // Error: Error: Can't get object. (-1728)
    console.log("Tried: c.id()")
    console.log(e)
}

console.log(typeof c.isEqual) // function
try {
    console.log(c.isEqual(a))
} catch(e) {
    // jsscripts/keynote_slides_count.js: execution error: Error: Error: Can't convert types. (-1700)
    console.log("Tried: c.isEqual(a)")
    console.log(e)
} 

请问该如何解决?为什么这些方法都无效?JXA里有没有适用于无ID对象的有效比较方法?


解决方案与原因解析

为什么现有方法无效?

  • ===比较失败:JXA中的Apple事件对象是「代理对象」,并非原生JS对象。即使两个代理指向同一个Keynote幻灯片,它们是内存中独立的JS实例,用===比较自然返回false。
  • id()方法报错:Keynote的JXA/AppleScript接口并未给幻灯片暴露id属性,调用该方法会触发「无法获取对象」的错误。
  • isEqual()调用失败:isEqual是Cocoa原生对象的方法,但JXA代理对象无法直接作为参数传入——必须先转换为Cocoa原生对象才能调用,直接传JXA实例会触发类型转换错误。

可行的解决方法

方法1:复刻AppleScript的遍历匹配逻辑

直接在JXA里实现和AppleScript一致的遍历逻辑,使用JXA代理对象自带的equals方法判断是否为当前幻灯片:

const keynote = Application("Keynote");
const doc = keynote.documents[0];
const currentSlide = doc.currentSlide();

for (let i = 0; i < doc.slides.length; i++) {
    const slide = doc.slides[i];
    // 用JXA的equals方法比较代理对象是否指向同一应用内对象
    if (slide.equals(currentSlide)) {
        console.log(`当前幻灯片索引(从0开始):${i}`);
        console.log(`当前幻灯片序号(从1开始):${i + 1}`);
        break;
    }
}

这个方法能正确处理隐藏幻灯片的情况,逻辑直观易懂。

方法2:利用objectSpecifier比较

JXA代理对象的objectSpecifier()方法会返回一个描述对象在应用内位置的标识符,指向同一应用对象的代理,其objectSpecifier是相等的:

const keynote = Application("Keynote");
const doc = keynote.documents[0];
const currentSlide = doc.currentSlide();

const targetIndex = doc.slides.findIndex(slide => 
    slide.objectSpecifier() === currentSlide.objectSpecifier()
);

if (targetIndex !== -1) {
    console.log(`当前幻灯片索引(从0开始):${targetIndex}`);
}

这个方法更简洁,利用JS数组的findIndex方法快速定位目标幻灯片。

总结

JXA中比较Apple事件代理对象,不能用原生JS的===,也不能直接调用Cocoa的isEqual,必须使用JXA提供的equals方法,或者比较它们的objectSpecifier标识符。上述两种方法都能解决你的问题,可根据代码风格选择。

内容的提问来源于stack exchange,提问作者simone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 13:43:14