前端JavaScript基础训练系列一百五十九:具有 then 方法的鸭子类型

在 Promise 领域,一个重要的细节是如何确定某个值是不是真正的 Promise。或者更直接地
说,它是不是一个行为方式类似于 Promise 的值?
既然 Promise 是通过 new Promise(…) 语法创建的,那你可能就认为可以通过 p instanceof

Promise 来检查。但遗憾的是,这并不足以作为检查方法,原因有许多。
其中最主要的是,Promise 值可能是从其他浏览器窗口(iframe 等)接收到的。这个浏览 器窗口自己的 Promise 可能和当前窗口 /frame 的不同,因此这样的检查无法识别 Promise 实例。

还有,库或框架可能会选择实现自己的 Promise,而不是使用原生 ES6 Promise 实现。实际 上,很有可能你是在早期根本没有 Promise 实现的浏览器中使用由库提供的 Promise。

讨论 Promise 决议过程的时候,你就会了解为什么有能力识别和判断类似于 Promise 的值是否是真正的 Promise 仍然很重要。不过,你现在只要先记住我的话,知道这 一点很重要就行了。
因此,识别 Promise(或者行为类似于 Promise 的东西)就是定义某种称为 thenable 的东 西,将其定义为任何具有 then(…) 方法的对象和函数。我们认为,任何这样的值就是 Promise 一致的 thenable。

根据一个值的形态(具有哪些属性)对这个值的类型做出一些假定。这种类型检查(type check)一般用术语鸭子类型(duck typing)来表示——“如果它看起来像只鸭子,叫起来 像只鸭子,那它一定就是只鸭子”(参见本书的“类型和语法”部分)。于是,对 thenable 值的鸭子类型检测就大致类似于:

    if (
         p !== null &&
         (
             typeof p === "object" ||
             typeof p === "function"
) &&
typeof p.then === "function" ){
// 假定这是一个thenable! }
else {
// 不是thenable
}

除了在多个地方实现这个逻辑有点丑陋之外,其实还有一些更深层次的麻烦。 如果你试图使用恰好有 then(…) 函数的一个对象或函数值完成一个 Promise,但并不希望

它被当作 Promise 或 thenable,那就有点麻烦了,因为它会自动被识别为 thenable,并被按 照特定的规则处理(参见本章后面的内容)。
即使你并没有意识到这个值有 then(…) 函数也是这样。比如: var o = { then: function(){} };

// 让v [[Prototype]]-link到o var v = Object.create( o );
      v.someStuff = "cool";
      v.otherStuff = "not so cool";
v.hasOwnProperty( "then" );

v 看起来根本不像 Promise 或 thenable。它只是一个具有一些属性的简单对象。你可能只是
想要像对其他对象一样发送这个值。
但你不知道的是,v 还 [[Prototype]] 连接(参见《你不知道的 JavaScript(上卷)》的 “this 和对象原型”部分)到了另外一个对象 o,而后者恰好具有一个 then(…) 属性。所以
thenable 鸭子类型检测会把 v 认作一个 thenable。 甚至不需要是直接有意支持的:

     Object.prototype.then = function(){};
      Array.prototype.then = function(){};
      var v1 = { hello: "world" };
      var v2 = [ "Hello", "World" ];

v1 和 v2 都会被认作 thenable。如果有任何其他代码无意或恶意地给 Object.prototype、 Array.prototype 或任何其他原生原型添加 then(…),你无法控制也无法预测。并且,如果 指定的是不调用其参数作为回调的函数,那么如果有 Promise 决议到这样的值,就会永远 挂住!真是疯狂。

但是别忘了,在 ES6 之前,社区已经有一些著名的非 Promise 库恰好有名为 then(…) 的方 法。这些库中有一部分选择了重命名自己的方法以避免冲突(这真糟糕!)。而其他的那 些库只是因为无法通过改变摆脱这种冲突,就很不幸地被降级进入了“与基于 Promise 的 编码不兼容”的状态。

标准决定劫持之前未保留的——听起来是完全通用的——属性名 then。这意味着所有值 (或其委托),不管是过去的、现存的还是未来的,都不能拥有 then(…) 函数,不管是有意 的还是无意的;否则这个值在 Promise 系统中就会被误认为是一个 thenable,这可能会导
致非常难以追踪的 bug。
// false

我并不喜欢最后还得用 thenable 鸭子类型检测作为 Promise 的识别方案。还 有其他选择,比如 branding,甚至 anti-branding。可我们所用的似乎是针 对最差情况的妥协。但情况也并不完全是一片黯淡。后面我们就会看到, thenable 鸭子类型检测还是有用的。只是要清楚,如果 thenable 鸭子类型误 把不是 Promise 的东西识别为了 Promise,可能就是有害的。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值