Object.prototype.toString( )输出"[object DataTransferItemList]"的背后故事

176 阅读3分钟

背景

大家好,我是抹茶。
昨天群里讨论了一个DataTransferItemList对象,用console.log(Object.prototype.toString.call(clipboardItems))打印输出如下,

image.png

敲重点,DataTransferItemList是什么东西?为何Object.prototype.toString()会输出这样结果?本文将围绕这两问题进行展开。

一、DOM接口中的DataTransferItemList

根据MDN官方 的说法,DataTransferItemList 对象是一组代表被拖动项的DataTransferItem 对象的列表。在拖动操作期间,每个DragEvent 都有一个 dataTransfer 属性,该属性是 DataTransferItemList。

HTML 的拖拽接口有 DragEvent、 DataTransfer、 DataTransferItem 和DataTransferItemList, 所以DataTransferItemList 是用来操作 HTML 文档中拖拽数据的一个 DOM 接口,是浏览器针对DOM文档提供的功能,而不是ECMAScript里的内置对象。

它拥有1个属性和4个方法。

属性

DataTransferItemList.length (只读)
无符号长整型 :列表中拖动项的数量。

方法

1.DataTransferItemList.add( )
向拖动项列表中添加新项 (File对象或string),该方法返回一个 DataTransferItem 对象。

2.DataTransferItemList.remove( )
根据索引删除拖动项列表中的对象。

3.DataTransferItemList.clear( )
清空拖动项列表。

4.DataTransferItemList.DataTransferItem( )
取值方法:返回给定下标的DataTransferItem对象。

二、JS的组成

上面提到了DOM接口和ECMAScript,这两者是什么关系呢? 答案是等同于平级,他们都是JS的组成部分,在浏览器环境中完整的JS由ECMAScript、DOM、BOM三部分组成

JavaScript的组成.jpeg

再来看Object.prototype.toString()为何输出的是"[object DataTransferItemList]"

三、Object.prototype.toString( )的输出依据

MDN对该方法的官方描述是

toString() 方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的类型转换的逻辑。

划重点,表示该对象的字符串派生类对象

从而可以推断DataTransferItemList是一个派生类对象

const arr = [1, 2, 3];

arr.toString(); // "1,2,3"
Object.prototype.toString.call(arr); // "[object Array]"

Object.prototype.toString() 返回 "[object Type]",这里的 Type 是对象的类型。如果对象有 Symbol.toStringTag 属性,其值是一个字符串,则它的值将被用作 Type。许多内置的对象,包括 Map 和 Symbol,都有 Symbol.toStringTag。

Symbol.toStringTag 是一个字符串值属性,用于创建对象的默认字符串描述。它由 Object.prototype.toString() 方法内部访问。

image.png

const map = new Map();
console.log(Object.prototype.toString.call(map), '\n');// [object Map] 

const arr = new Array(1);
console.log(Object.prototype.toString.call(arr), '\n'); // [object Array] 

arr[Symbol.toStringTag] = 'other';
console.log(Object.prototype.toString.call(arr), '\n'); // [object other]  

在上面的demo中,在没有自定义[Symbol.toStringTag]前,对arr对象进行Object.prototype.toString()访问返回的是其原型链上的Array对象[[Class]]属性,在设置[Symbol.toStringTag]后,其优先级大于内置的[[Class]]属性。

一些早于 ES6 的对象没有 Symbol.toStringTag,但内部有不能修改[[Class]]属性(默认等同于内置对象名),它会被Object.prototype.toString()访问,用来标识对象的类型。 它们包括:

  • Array
  • Function(它的 typeof 返回 "function")
  • Error
  • Boolean
  • Number
  • String
  • Date
  • RegExp
const arr = new Array(1);
console.log(Object.prototype.toString.call(arr), '\n'); // [object Array] 

const fn = new Function();
console.log(Object.prototype.toString.call(fn), '\n'); // [object Function] 

const e = new Error('Invalid');
console.log(Object.prototype.toString.call(e), '\n'); // [object Error] 

const bol = new Boolean(true);
console.log(Object.prototype.toString.call(bol), '\n'); // [object Boolean] 

const num = new Number(1);
console.log(Object.prototype.toString.call(num), '\n'); // [object Number] 

const str = new String('加油');
console.log(Object.prototype.toString.call(str), '\n'); // [object String] 

arguments 对象返回 "[object Arguments]"。其他所有内容,包括用户自定义的类,除非有一个自定义的 Symbol.toStringTag,否则都将返回 "[object Object]"

function aruTest () {
  console.log(Object.prototype.toString.call(arguments), '\n');
}
aruTest();// [object Arguments] 

const test = { 0: 1 };
console.log(Object.prototype.toString.call(test), '\n'); // [object Object] 

在 null 和 undefined 上调用 Object.prototype.toString() 分别返回 "[object Null]""[object Undefined]"


console.log(Object.prototype.toString.call(null), '\n'); // [object Null] 

console.log(Object.prototype.toString.call(undefined), '\n'); // [object Undefined] 

结合上面的测试结果,我们可以得出以下的规律

Object.prototype.toString.jpeg

总结

  1. 除了ECMAScript规范定义的JS内置对象、方法外,宿主环境(浏览器、Node)还会在JS执行过程中注入一些新的对象(或者说接口), DataTransferItemList就是浏览器往JS引擎中注入的DOM接口中的一小块内容。
  2. 浏览器环境中,完整的JS组成由ECMAScript、DOM、BOM三部分组成。
  3. Object.prototype.toString()方法会返回标识对象类型的字符串,对内置对象构造出来的变量,返回的是内置函数的不可修改的[[Class]]属性,如果设置了[Symbol.toStringTag],其优先级是最高的。

参考资料