背景
大家好,我是抹茶。
昨天群里讨论了一个DataTransferItemList对象,用console.log(Object.prototype.toString.call(clipboardItems))
打印输出如下,
敲重点,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三部分组成
再来看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() 方法内部访问。
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]
结合上面的测试结果,我们可以得出以下的规律
总结
- 除了ECMAScript规范定义的JS内置对象、方法外,宿主环境(浏览器、Node)还会在JS执行过程中注入一些新的对象(或者说接口), DataTransferItemList就是浏览器往JS引擎中注入的DOM接口中的一小块内容。
- 浏览器环境中,完整的JS组成由ECMAScript、DOM、BOM三部分组成。
- Object.prototype.toString()方法会返回标识对象类型的字符串,对内置对象构造出来的变量,返回的是内置函数的不可修改的
[[Class]]
属性,如果设置了[Symbol.toStringTag]
,其优先级是最高的。