你不知道的JavaScript中卷 2.1数组时有个疑问。具体是这样的:

通过“借用”数组的方法可以很方便的处理字符串。可以“借用”数组的非变更方法,但不能“借用”数组的可变更方法。

用代码来描述就是:

var a = 'foo';
// 数组的非变更方法,即不改变原有数组的方法
var b = Array.prototype.join.call(a, '-');
var c = Array.prototype.map.call(a, v => v.toUpperCase()).join()
var d = Array.prototype.slice.call(a);
console.log(b); // 'f-o-o'
console.log(c); // 'FOO'
console.log(d); // ['f', 'o', 'o'] // 数组的可变更方法,即能够改变原有数组的方法
var e = Array.prototype.reverse.call(a);
// chrome: Uncaught TypeError: Cannot assign to read only property '0' of object '[object String]'

刚开始比较疑惑:

  • 为什么字符串可以通过这种方式,使用数组方法呢
  • 为什么所谓的非变更方法可以这样用而可变更方法 不能呢

当我看到Uncaught TypeError: Cannot assign to read only property '0' of object '[object String]'这个报错时算是有些明白了。

  1. 我们知道,call 的第一个参数是作为上下文对象的,但这里直接传入的是字符串变量a,看到[object String] 时可以知道,这里使用了字符串的封装对象String
var f = new String(a);
console.log(f); // String {0: f, 1: 0, 2: 0, length: 3,}
// 类数组对象大多有两个特征,属性名是0,1,2...,有length属性

数组的非变更方法 是不改变原数组,并返回一个新数组的。那么每次调用必定产生一个新数组,并遍历上下文对象,把对应索引上的值赋给新数组:

fn(){
var newArray = [];
for(var i = 0,len = f.length; i < len; i++){
newArray[i] = f[i];
}
return newArray;
}

这就解释了第一个问题,为什么可以通过Array.prototype.xxx.call() 这种方式操作字符串。

  1. 注意到报错中的read only,我想到了对象每个属性的描述对象
Object.getOwnPropertyDescriptors(f);
/**
{
0: {value: "f", writable: false, enumerable: true, configurable: false},
1: {value: "o", writable: false, enumerable: true, configurable: false},
2: {value: "o", writable: false, enumerable: true, configurable: false},
length: {value: 3, writable: false, enumerable: false, configurable: false}
}
*/

可以看到只有 enumerable: true其余均为 false, 每个属性只能被枚举,而不能被更改,而数组的可变更方法reverse等,均要直接操作上下文对象(数组或类数组)。但[Object String]的属性是只读的,不能更改,不能配置。这就解释了第二个问题。

虽然最初的疑惑算是有了个比较合理的解释了,但其实还是有很多问题的:

  • 书中说 Array.prototype.reverse(a) 返回值是 字符串foo的封装对象,但我的chrome会报错,试了firefox 、edge、ie11、ie10、ie9都会报错,只有ie8会返回[object String]{0: undefined, 1: undefined, 2: undefined},其他浏览器没有测试。
  • 既然类数组的结构可以这样使用数组的方法,那么部署了Iterator遍历器接口的数据结构是不是也可以这么用,如果可以,调用数组方法时的逻辑是否和类数组一样?如果不可以又是为什么...等等。不过,这感觉扯的有点远了...也有些没有意义,既然都有Iterator了,一般情况下是在ES6环境下,我一个扩展运算符[...a]或者直接Array.from()不就生成一个数组了么,干嘛要费劲巴拉的用call

这里虽然给出了两个问题的解释,但都是从自己的理解出发的,总感觉有些未尽之意或者难以再深入下去,有自己理解的朋友大家可以交流下。有不对的地方也欢迎指正。

原文链接:https://www.jianshu.com/p/0362b6cd90d6

Array.prototype.xxx.call()处理字符串的疑惑的更多相关文章

  1. Array.prototype.slice.call引发的思考

    概述 今天在看书的时候看到Array.prototype.slice.call(arguments),有点看不懂,所以认真研究了一下,记录下来,供以后开发时参考,相信对其他人也有用. call 每一个 ...

  2. Array.prototype.removeBeginWithVal(删除数组内以某值开头的字符串对象)

    Array扩展方法: //author: Kenmu //created time: 2015-03-16 //function: 删除数组内以某值开头的字符串对象 Array.prototype.r ...

  3. 前端性能优化:使用Array.prototype.join代替字符串连接

    来源:GBin1.com 有一种非常简单的客户端优化方式,就是用Array.prototype.join代替原有的基本的字符连接的写法.在这个系列的第一篇中,我在代码中使用了基本字符连接: htmlS ...

  4. js基础进阶--关于Array.prototype.slice.call(arguments) 的思考

    欢迎访问我的个人博客:http://www.xiaolongwu.cn Array.prototype.slice.call(arguments)的作用为:强制转化arguments为数组格式,一般出 ...

  5. IE下Array.prototype.slice.call(params,0)

    i8 不支持 Array.prototype.slice.call(params,0) params可以是 HTMLCollection.类数组.string字符串

  6. 详解 Array.prototype.slice.call(arguments)

    首先,slice有两个用法,一个是String.slice,一个是Array.slice,第一个返回的是字符串,第二个返回的是数组 在这里我们看第二个方法 1.在JS里Array是一个类 slice是 ...

  7. Array.prototype.slice.call(document.querySelectorAll('a'), 0)

    Array.prototype.slice.call(document.querySelectorAll('a'), 0)的作用就是将一个DOM NodeList 转换成一个数组. slice()方法 ...

  8. Javascript中Array.prototype.map()详解

    map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数.callback 每次执行后的返回值组合起来形成一个新数组. callback 函数只会在有值的索引上被调用:那些从来没被赋 ...

  9. Array.prototype.slice.call(arguments) 类数组转成真正的数组

    Array.prototype.slice.call(arguments)   我们知道,Array.prototype.slice.call(arguments)能将具有length属性的对象转成数 ...

随机推荐

  1. littleFS在RT1052移植笔记

    环境:rt1052单片机+16G tf卡 为什么使用littleFS? NXP官方SDK支持! 先进的损耗平衡功能(wear leveling)可提供最长的快闪及内存寿命及最大的使用量.因为FALSH ...

  2. 地址解析协议(ARP)

    地址解析协议(ARP) 地址解析协议(ARP)是指网络地址和MAC地址之间的转换 当一台主机需要向另一台主机发送数据时,需要知道目的主机的ip地址外还需要知道目的主机的mac地址.源主机首先会在自己的 ...

  3. Arm-Linux 移植 jpeg库

    背景: jpeg库的使用可以提高显示效率. host平台 :Ubuntu 16.04 arm平台 : S5P6818   jpeg    :v9c arm-gcc :4.8.1 主机准备: 运行以下脚 ...

  4. webpack css文件编译、自动添加前缀、剥离

    1.css文件编译 webpack默认只能编译js文件,引入css需要loader支持 // css文件写入js中 npm i style-loader -D // css文件loader npm i ...

  5. vue cli3 项目优化

    vue-cli3 Prefetch (官网内容) <link rel="prefetch"> 是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空 ...

  6. 对称加密,非对称加密,数字签名,https

    对称加密和非对称加密 对称加密 概念:加密秘钥和解密秘钥使用相同的秘钥(即加密和解密都必须使用同一个秘钥) 特点:一对一的双向保密通信(每一方既可用该秘钥加密,也可用该秘钥解密,非对称加密是多对一的单 ...

  7. 用ASP.NET Web API技术开发HTTP接口(二)

    在第一部分,我们创建了一个基本的ASP.NET Web API项目,新建成功了数据表,然后添加了一些测试数据,最后创建了API控制器,用json格式把数据表里面的内容成功输出到浏览器上.接下来我们将继 ...

  8. 在论坛中出现的比较难的sql问题:32(row_number函数+子查询 sql循环取差值)

    原文:在论坛中出现的比较难的sql问题:32(row_number函数+子查询 sql循环取差值) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路. sql循环取差值,该怎 ...

  9. SQL Server2008本地数据库调用SP发送邮件

    一.首先要对本地数据库做配置 1.通过使用数据库邮件配置向导和sp_configure存储过程配置启用数据库邮件: 注:服务器名称填写发送服务器的路径或者IP,电子邮件地址为寄件者地址 配置好数据库邮 ...

  10. R_数据操作_初级_03

    数据的输入:详见(http://cran.r-project.org/doc/manuals/R-data.pdf下载的R Data Import/Export手册②) 1.键盘输入:使用edit() ...