鸿蒙Harmony角落里的知识:从ECMA规范到ArkTS接口(二)

上篇介绍了typedArray.slice方法,鸿蒙Harmony角落里的知识:从ECMA规范到ArkTS接口(一)本文介绍一个返回结果和参数和slice非常类似的函数:TypedArray.prototype.subarray。

ECMA对TypedArray.prototype.subarray接口的定义:

按照ECMA的规范,TypedArray.prototype.subarray(begin, end) 方法定义如下:

  • begin: 起始索引,表示新视图的起始点。
  • end: 结束索引,代表新视图的终点(但不包括该索引本身)。

如果 end 参数省略,subarray 将默认包含从 begin 开始到原数组结尾的所有元素。如果 beginend 是负值,则它们表示从数组末尾开始的倒数索引。方法返回一个新的 TypedArray 实例,它表示原始 TypedArray 的一个连续子集。

我们可以直接查看ECMA规范中对该函数的描述:

23.2.3.30 %TypedArray%.prototype.subarray ( start, end )
	This method returns a new TypedArray whose element type is the element type of this TypedArray and whose ArrayBuffer is the ArrayBuffer of this TypedArray, referencing the elements in the interval from start (inclusive) to end (exclusive). If either start or end is negative, it refers to an index from the end of the array, as opposed to from the beginning.

It performs the following steps when called:

1. Let O be the this value.
2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
4. Let buffer be O.[[ViewedArrayBuffer]].
5. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(O, SEQ-CST).
6. If IsTypedArrayOutOfBounds(srcRecord) is true, then
	a. Let srcLength be 0.
7. Else,
	a. Let srcLength be TypedArrayLength(srcRecord).
8. Let relativeStart be ? ToIntegerOrInfinity(start).
9. If relativeStart = -∞, let startIndex be 0.
10. Else if relativeStart < 0, let startIndex be max(srcLength + relativeStart, 0).
11. Else, let startIndex be min(relativeStart, srcLength).
12. Let elementSize be TypedArrayElementSize(O).
13. Let srcByteOffset be O.[[ByteOffset]].
14. Let beginByteOffset be srcByteOffset + (startIndex × elementSize).
15. If O.[[ArrayLength]] is AUTO and end is undefined, then
	a. Let argumentsList be << buffer, 𝔽(beginByteOffset) >>.
16. Else,
	a. If end is undefined, let relativeEnd be srcLength; else let relativeEnd be ? ToIntegerOrInfinity(end).
	b. If relativeEnd = -∞, let endIndex be 0.
	c. Else if relativeEnd < 0, let endIndex be max(srcLength + relativeEnd, 0).
	d. Else, let endIndex be min(relativeEnd, srcLength).
	e. Let newLength be max(endIndex - startIndex, 0).
	f. Let argumentsList be << buffer, 𝔽(beginByteOffset), 𝔽(newLength) >>.
17. Return ? TypedArraySpeciesCreate(O, argumentsList).
This method is not generic. The this value must be an object with a [[TypedArrayName]] internal slot.

TypedArray.prototype.subarray与TypedArray.prototype.slice对比:

TypedArray.prototype.subarrayTypedArray.prototype.slice 是两个不同的 TypedArray 方法,用于生成原数组某部分的新数组,但他们在处理方式和结果的内存分配上存在差异。

首先,subarray 方法返回的新 TypedArray 对象,并不创建原 ArrayBuffer 的副本,而是创建了一个新的 TypedArray 视图,这个视图引用的是原 ArrayBuffer 中的相同内存区域。这意味着 subarray 方法生成的数组与原数组共享相同的数据存储,因此对新数组的修改会影响到原数组中相应的部分。这个方法的执行效率较高,因为它避免了复制操作,只是创建了一个指向相同内存区域的新视图。

另一方面,slice 方法会创建一个新的 ArrayBuffer,并将选定的元素从原 TypedArray 对象复制到新数组中。这意味着 slice 方法生成的新数组拥有自己的数据存储,对这个新数组的任何修改都不会影响原数组。由于涉及到内存中数据的复制操作,slice 方法在执行时的效率略低于 subarray

下面是这两个方法的对比分析:

  1. 内存共享与否
    • subarray:返回一个新 TypedArray,共享同一个 ArrayBuffer
    • slice:返回一个新 TypedArray,有自己独立的 ArrayBuffer
  2. 性能
    • subarray:性能较高,因为没有进行元素的复制,仅仅是创建了一个新的视图。
    • slice:性能较低,因为需要复制元素到新的 ArrayBuffer
  3. 修改影响
    • subarray:由于共享 ArrayBuffer,对生成的 TypedArray 的修改会影响到原数组。
    • slice:由于使用了新的 ArrayBuffer,对生成的 TypedArray 的修改不会影响到原数组。
  4. 参数处理
    • subarrayslice 方法对于 startend 参数的处理是相同的。如果 startend 是负数,它们会被解释为从数组末尾开始的索引。如果 endundefined,则操作会处理直到原数组的末尾。
  5. 返回值类型
    • 无论是 subarray 还是 slice,返回的新数组类型都与原数组相同。
  6. 错误处理
    • slice 方法在复制过程中有更严格的错误检查。例如,如果源 TypedArray 越界,它会抛出 TypeError

总的来说,选择 subarray 还是 slice 取决于你是否需要一个独立的数组副本,以及是否关心性能和内存使用。如果你只是想要一个指向相同数据的快速视图,并且不介意对这个视图的修改会影响原数据,那么 subarray 是一个好选择。而如果你需要一个完全独立的复制,不会影响原数据的副本,那么应该选择 slice 方法。

ArkTS对subarray的接口描述:

我们继续以\static_core\plugins\ets\stdlib\escompat\TypedUArrays.ets的Uint8Array定义为例:

/**
     * Creates a Uint8Array with the same underlying Buffer
     *
     * @param begin start index, inclusive
     *
     * @param end last index, exclusive
     *
     * @returns new Uint8Array with the same underlying Buffer
     */
    public subarray(begin?: number, end?: number): Uint8Array {
        return this.subarray(asIntOrDefault(begin, 0 as int), asIntOrDefault(end, this.lengthInt))
    }

    /**
     * Creates a Uint8Array with the same underlying Buffer
     *
     * @param begin start index, inclusive
     *
     * @param end last index, exclusive
     *
     * @returns new Uint8Array with the same underlying Buffer
     */
    public subarray(begin: number, end: number): Uint8Array {
        return this.subarray(begin as int, end as int)
    }

    /**
     * Creates a Uint8Array with the same underlying Buffer
     *
     * @param begin start index, inclusive
     *
     * @param end last index, exclusive
     *
     * @returns new Uint8Array with the same underlying Buffer
     */
    public subarray(begin: number, end: int): Uint8Array {
        return this.subarray(begin as int, end as int)
    }

    /**
     * Creates a Uint8Array with the same underlying Buffer
     *
     * @param begin start index, inclusive
     *
     * @param end last index, exclusive
     *
     * @returns new Uint8Array with the same underlying Buffer
     */
    public subarray(begin: int, end: number): Uint8Array {
        return this.subarray(begin as int, end as int)
    }

    /**
     * Creates a Uint8Array with the same underlying Buffer
     *
     * @param begin start index, inclusive
     *
     * @param end last index, exclusive
     *
     * @returns new Uint8Array with the same underlying Buffer
     */
    public subarray(begin: int, end: int): Uint8Array {
        const len: int = this.length as int
        const relStart = normalizeIndex(begin, len)
        const relEnd = normalizeIndex(end, len)
        let count = relEnd - relStart
        if (count < 0) {
            count = 0
        }
        return new Uint8Array(this.buffer, relStart * Uint8Array.BYTES_PER_ELEMENT as int, count)
    }

    /**
     * Creates a Uint8Array with the same Buffer
     *
     * @param begin start index, inclusive
     *
     * @returns new Uint8Array with the same Buffer
     */
    public subarray(begin: number): Uint8Array {
        return this.subarray(begin as int, this.lengthInt)
    }

    /**
     * Creates a Uint8Array with the same Buffer
     *
     * @param begin start index, inclusive
     *
     * @returns new Uint8Array with the same Buffer
     */
    public subarray(begin: int): Uint8Array {
        return this.subarray(begin, this.lengthInt)
    }

ArkTS对subarray的接口定义如上,可以看到基本覆盖了ECMA的规范的方方面面。此外,我们还可以看到,subarray提供了对int类型参数的支持,而TS是仅仅只支持number类型的参数的。

测试用例:

要验证我们的 subarray 方法实现是否正确,我们可以编写单元测试用例。这里我们使用了ArkTS和Jest测试框架,来确保我们的实现与ECMA规范一致。

const success = 0;
const fail = 1;
function testSubarrayWithOutParam(): int {
  let source: number[] = [10, 20, 30, 40, 50, 60];
  let ss = new ArrayBuffer(source.length as int * 1);

  let origin: Uint8Array;

  try {
    origin = new Uint8Array(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let target: Uint8Array;

  try {
    target = origin.subarray();
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != origin.length as int) {
    console.log("Array length mismatch on slice");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = 0; i< origin.length as int; i++) {
    let tv = target[i] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  origin= new Uint8Array(0);
  if (origin.length as int != 0){
    return fail;
  }

  try {
    target = origin.subarray();
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != 0){
    return fail;
  }
  return success;
}

function testSubarrayOneParam(): int {
  let source: number[] = [10, 20, 30, 40, 50, 60];
  let ss = new ArrayBuffer(source.length as int * 1);

  let origin: Uint8Array;

  try {
    origin = new Uint8Array(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let subarrayStart: int = 1;
  let subarrayEnd: int = origin.length as int;

  let target: Uint8Array;

  try {
    target = origin.subarray(subarrayStart);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != origin.length as int - subarrayStart) {
    console.log("Array length mismatch on subarray One Params" + target.length);
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  subarrayStart = 0;
  try {
    target = origin.subarray(undefined);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != origin.length as int) {
    console.log("Array length mismatch on subarray One Params" + target.length);
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  //The subarray method returns a view of the original array, so modifications made to the subarray will affect the original array, and vice versa.
  target[subarrayStart] = 1;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  origin[subarrayStart] = 2;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  return success;
}

function testSubarrayTwoParams(): int {
  let source: number[] = [10, 20, 30, 40, 50, 60, 70, 80];
  let ss = new ArrayBuffer(source.length as int * 1);

  let origin: Uint8Array;

  try {
    origin = new Uint8Array(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let subarrayStart: int = 2;
  let subarrayEnd: int = 4;

  let target: Uint8Array;

  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != subarrayEnd - subarrayStart) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  subarrayStart = 0;
  subarrayEnd = origin.length as int;
  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != subarrayEnd - subarrayStart) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.subarray(new Number(subarrayStart), undefined);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != subarrayEnd - subarrayStart) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.subarray(undefined, undefined);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != subarrayEnd - subarrayStart) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.subarray(undefined, new Number(subarrayEnd));
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != subarrayEnd - subarrayStart) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = subarrayStart; i< subarrayEnd; i++) {
    let tv = target[i - subarrayStart] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.subarray(0, 0);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  return success;
}

function testSubarrayTwoParamsWithOtherNumber(): int {
  let source: number[] = [10, 20, 30, 40, 50, 60, 70, 80];
  let ss = new ArrayBuffer(source.length as int * 1);

  let origin: Uint8Array;

  try {
    origin = new Uint8Array(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let subarrayStart: int = 4;
  let subarrayEnd: int = 2;

  let target: Uint8Array;

  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  subarrayStart = -1;
  subarrayEnd = origin.length as int;
  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != subarrayEnd - (origin.length + subarrayStart)) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = (origin.length + subarrayStart) as int; i< subarrayEnd; i++) {
    let tv = target[i - (origin.length + subarrayStart)] as number;
    let ov = origin[i] as number;
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  subarrayStart = 0;
  subarrayEnd = -origin.length as int;
  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != (origin.length + subarrayEnd) - subarrayStart) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  return success;
}

function testSubarrayOneLengthTwoParams(): int {
  let source: number[] = [10];
  let ss = new ArrayBuffer(source.length as int * 1);

  let origin: Uint8Array;

  try {
    origin = new Uint8Array(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }
  
  let subarrayStart: int = 4;
  let subarrayEnd: int = 2;

  let target: Uint8Array;

  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  subarrayStart = 2;
  subarrayEnd = 4;
  try {
    target = origin.subarray(subarrayStart, subarrayEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on subarray2");
    return fail;
  }

  return success;
}

这些测试用例检查了 subarray 方法在正常条件下和非正常条件下的行为,对比slice函数的测试用例,我们使用通过修改源数组影响目标数组数值的方式验证内存共享的功能。

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?但是又不知道从哪里下手,而且学习时频繁踩坑,最终浪费大量时间。所以本人整理了一些比较合适的鸿蒙**(HarmonyOS NEXT)**学习路径和一些资料的整理供小伙伴学习

点击领取→ 纯血鸿蒙Next全套最新学习资料(安全链接,放心点击

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
**一、**鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含: (ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)****...等技术 知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

二、HarmonyOS Next 最新全套视频教程

三、《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .......

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ......

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ......

四、大厂面试必问面试题

五、鸿蒙南向开发技术

六、鸿蒙APP开发必备

七、鸿蒙生态应用开发白皮书V2.0PDF

完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料****

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。

相关推荐
JerryXZR11 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
problc26 分钟前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_91530 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
待磨的钝刨3 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
sanzk3 小时前
华为鸿蒙应用开发
华为·harmonyos
SoraLuna7 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
前端青山8 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
ClkLog-开源埋点用户分析8 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos
mg6688 小时前
鸿蒙系统的优势 开发 环境搭建 开发小示例
华为·harmonyos