为什么 MDC 原型函数是这样编写的

Why are the MDC prototype functions written this way?

本文关键字:MDC 原型 函数 为什么      更新时间:2023-09-26

在 MDC 中,有很多代码片段旨在在不支持它们的浏览器中实现对新 ECMAScript 标准的支持,例如 Array.prototype.map 函数:

if (!Array.prototype.map)
{
  Array.prototype.map = function(fun /*, thisp */)
  {
    "use strict";
    if (this === void 0 || this === null)
      throw new TypeError();
    var t = Object(this);
    var len = t.length >>> 0;
    if (typeof fun !== "function")
      throw new TypeError();
    var res = new Array(len);
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in t)
        res[i] = fun.call(thisp, t[i], i, t);
    }
    return res;
  };
}

使用此函数而不是

function(fun, thisp)
{
  // same code, just without the "var thisp = arguments[1];" line:
  "use strict";
  if (this === void 0 || this === null)
    throw new TypeError();
  var t = Object(this);
  var len = t.length >>> 0;
  if (typeof fun !== "function")
    throw new TypeError();
  var res = new Array(len);
  for (var i = 0; i < len; i++)
  {
    if (i in t)
      res[i] = fun.call(thisp, t[i], i, t);
  }
  return res;
}

var t = Object(this);而不是var t = this;var len = t.length >>> 0;而不是var len = t.length;

var len = t.length >>> 0;    
这个问题

很好地涵盖了这个问题,基本上它确保数字是一个非负的 32 位 int。

至于对象构造函数:

var t = Object(this);

如果 null 或未定义,它将返回一个空对象。 来自对象上的 MDC 文档

对象构造函数创建一个 给定值的对象包装器。如果 值为空或未定义,它 将创建并返回一个空 对象,否则,它将返回一个 对应于 给定值。

它们都只是进行纠错的快速方法。

编辑:我对thisp部分想得太辛苦了。 我假设使用参数数组是确保参数默认为 undefined 的一种方法,但无论如何他们自己这样做。迈克·霍费尔(Mike Hofer)在评论中说得对。 这是Mozilla的编码风格,用于指示可选参数。 如果将 null 或未定义作为第一个参数传入,则 Function.call 默认为全局。来自 MDC Docs on Function.call

这个Arg: 确定内部的值 乐趣。如果 thisArg 为 null 或未定义, 这将是全局对象。 否则,这将等于 Object(thisArg) (这是这个Arg if thisArg 已经是一个对象,或者 字符串、布尔值或数字(如果此Arg) 是 的基元值 对应类型)。因此,它是 总是正确的类型这个== 函数执行时的"对象"。

var t = Object(this);

基于 ES5 规范。它明确指出this应传递到Object构造函数中。

如果你仔细观察ES5规范中指定的确切算法,那么Mozilla提供的方法几乎完全反映了它(它受到ES3功能的限制)。这就是这段代码似乎有一些怪癖的原因。

以下是 ES5 规范:

当用一个调用地图方法时 或两个参数,以下步骤 被采取:

  1. 设 O 是调用 ToObject 的结果,将此值作为参数传递。
  2. 让 lenValue 是使用参数 "length" 调用 O 的 [[Get]] 内部方法的结果。
  3. 让 len 成为 ToUint32(lenValue)。
  4. 如果 IsCallable(callbackfn) 为 false,则抛出 TypeError 异常。
  5. 如果提供了 thisArg,则让 T 是 thisArg;否则让 T 未定义。
  6. 设 A 是一个新数组,就像由表达式 new Array(len) 创建一样,其中 Array 是具有该名称的标准内置构造函数,len 是 len 的值。
  7. 设 k 为 0。
  8. 重复,而 k

    1. 设 Pk 为 ToString(k)。
    2. 让 kPresent 是使用参数 Pk 调用 O 的 [[HasProperty]] 内部方法的结果。
    3. 如果 kPresent 为真,则
      • 让 kValue 是使用参数 Pk 调用 O 的 [[Get]] 内部方法的结果。
      • 让 mappedValue 是调用 [[Call]] 内部方法的 callbackfn 的结果,其中 T 为包含 kValue、k 和 O 的此值和参数列表。
      • 调用 A 的 [[DefineOwnProperty]] 内部方法,参数为 Pk, Property描述符 {[[Value]]: mappedValue, [[Writable]]: true, [[Enumerable]]: true,[[可配置]]:真}和假。
      • 将 k 增加 1。
  9. 返回 A。

设 O 是调用 ToObject 的结果,将此值作为参数传递。

请注意,第一步明确表示您应该调用Object(this)

让 lenValue 成为调用的结果 O 的 [[Get]] 内部方法 参数"长度"。

让伦成为 ToUint32(lenValue).

步骤 2 和 3 特别说获取t.length然后调用ToUint32,此处>>> 0实现。

规范中提到的实际签名是

Array.prototype.map ( callbackfn [ , thisArg ] )

在上面的签名中,callbackfn是一个必需的参数,[ ]是一个只包含一个thisArg的可选参数数组。

Mozilla在他们的function(fun /*, thisp */) {定义中反映了这一点,以指定thisp是一个可选参数,很明显,从函数签名而不是查看代码来看就是这种情况。