---
title: 'External'
description: External 对象保存原生值到 JavaScript 对象中。
---

# External

[`External`](https://nodejs.org/api/n-api.html#napi_create_external)
与在 [Class](./class) 内部中使用的 [`Object Wrap`](https://nodejs.org/api/n-api.html#object-wrap) 非常相似。

`Object Wrap` 附加一个原生值到一个 JavaScript 对象上，并且可以在这个对象被 GC 回收时通知你，
`External` 创建一个空的 JavaScript 对象，它在内部保存了原生值。只会在将对象传回 Rust 时生效：

**lib.rs**

```rust
use napi::bindgen_prelude::*;
use napi_derive::napi;

#[napi]
fn create_source_map(length: u32) -> External<Buffer> {
  External::new(vec![0; length as usize].into())
}
```

⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️

**index.d.ts**

```ts
export class ExternalObject<T> {
  readonly '': {
    readonly '': unique symbol
    [K: symbol]: T
  }
}

export function createSourceMap(length: number): ExternalObject<Buffer>
```

当你想要返回一个包含一些与原生 Rust 代码交互的方法的 JavaScript 对象时，`External` 是非常有用的，
这里是一个真实的例子：

https://github.com/h-a-n-a/magic-string-rs/blob/v0.3.0/node/src/lib.rs#L96-L103

https://github.com/h-a-n-a/magic-string-rs/blob/v0.3.0/node/index.js#L7-L23

**lib.rs**

```rust
impl MagicString {
  #[napi(ts_return_type = "{ toString: () => string, toUrl: () => string }")]
  pub fn generate_map(
    &mut self,
    options: Option<magic_string::GenerateDecodedMapOptions>,
  ) -> Result<External<SourceMap>> {
    let external = create_external(self.0.generate_map(options.unwrap_or_default())?);
    Ok(external)
  }

  /// @internal
  #[napi]
  pub fn to_sourcemap_string(&mut self, sourcemap: External<SourceMap>) -> Result<String> {
    Ok((*sourcemap.as_ref()).to_string()?)
  }

  /// @internal
  #[napi]
  pub fn to_sourcemap_url(&mut self, sourcemap: External<SourceMap>) -> Result<String> {
    Ok((*sourcemap.as_ref()).to_url()?)
  }
}
```

首先，`generate_map` 方法返回一个 `External` 对象，然后 JavaScript 函数在闭包中保存 `External` 对象：

**index.js**

```ts
module.exports.MagicString = class MagicString extends MagicStringNative {
  generateMap(options) {
    const sourcemap = super.generateMap({
      file: null,
      source: null,
      sourceRoot: null,
      includeContent: false,
      ...options,
    })

    const toString = () => super.toSourcemapString(sourcemap)
    const toUrl = () => super.toSourcemapUrl(sourcemap)

    return {
      toString,
      toUrl,
    }
  }
}
```
