出奇目的,将函数的其实参数转换成数组的不二法门

JavaScript 特殊目的 Array-Like Objects 详解

2016/06/26 · JavaScript
· Javascript,
underscore

本文小编: 伯乐在线 –
韩子迟
。未经小编许可,禁止转发!
欢迎参加伯乐在线 专辑小编。

这篇文章拖了有两周,明天来跟我们你一言我一语 JavaScript 中一类特殊的靶子 ->
Array-Like Objects。

(本文节选自 underscore 源码解读连串文章,完整版请关心

这篇小说拖了有两周,明日来跟大家你一言我一语 JavaScript 中一类极度的靶子 ->
Array-Like Objects。

值得庆幸的是,大家能够通过数组的 slice 方法将 arguments
对象转换成真正的数组:
var args = Array.prototype.slice.call(arguments);
对于slice 方法,ECMAScript 262 中 15.4.4.10 Array.prototype.slice
(start, end) 章节有备注:

值得庆幸的是,我们得以因此数组的 slice 方法将 arguments
对象转换成真正的数组:
var args = Array.prototype.slice.call(arguments);
对此slice 方法,ECMAScript 262 中 15.4.4.10 Array.prototype.slice
(start, end) 章节有备注:

Array-Like

JavaScript 中整整皆为目的,那么如何是 Array-Like
Objects?顾名思义,就是像数组的目的,当然,数组本身就是目的嘛!稍微有点基础的同室,一定知道
arguments 就是 Array-Like Objects 的一种,能像数组一样用 [] 去访问
arguments 的元素,有 length 属性,可是却不可以用一些数组的主意,如
push,pop,等等。

那么,什么样的要素是 Array-Like Objects?大家来探望 underscore
中对其的概念。

JavaScript

var MAX_ARRAY_INDEX = Math.pow(2, 53) – 1; var getLength =
property(‘length’); var isArrayLike = function(collection) { var length
= getLength(collection); return typeof length == ‘number’ && length
>= 0 && length <= MAX_ARRAY_INDEX; };

1
2
3
4
5
6
var MAX_ARRAY_INDEX = Math.pow(2, 53) – 1;
var getLength = property(‘length’);
var isArrayLike = function(collection) {
  var length = getLength(collection);
  return typeof length == ‘number’ && length >= 0 && length <= MAX_ARRAY_INDEX;
};

很简短,不是数组,可是有 length 属性,且属性值为非负 Number
类型即可。至于 length 属性的值,underscore 给出了一个上限值
MAX_ARRAY_INDEX,其实是 MAX_SAFE_INTEGER(感谢 @HangYang 同学提出)
,因为这是 JavaScript 中能精确表示的最大数字。

心想还有何样同时能满意以上标准的?NodeList,HTML
Collections,仔细思忖,甚至还有字符串,或者有所 length
属性的目的,函数(length 属性值为形参数量),等等。

(本文节选自 underscore 源码解读系列小说,完整版请关注
https://github.com/hanzichi/underscore-analysis)

复制代码 代码如下:

复制代码 代码如下:

Array-Like to Array

局地时候,必要将 Array-Like Objects 转为 Array
类型,使之能用数组的片段方法,一个非凡简单残酷并且包容性杰出的不二法门是新建个数组,然后循环存入数据。

我们以 arguments 为例。

function fn() { // Uncaught TypeError: arguments.push is not a function
// arguments.push(4); var arr = []; for (var i = 0, len =
arguments.length; i < len; i++) arr[i] = arguments[i];
arr.push(4); // [1, 2, 3, 4] } fn(1, 2, 3);

1
2
3
4
5
6
7
8
9
10
11
12
function fn() {
  // Uncaught TypeError: arguments.push is not a function
  // arguments.push(4);
 
  var arr = [];
  for (var i = 0, len = arguments.length; i < len; i++)
    arr[i] = arguments[i];
 
  arr.push(4); // [1, 2, 3, 4]
}
 
fn(1, 2, 3);

不过这不是最优雅的,更优雅的解法大家自然都通晓了,use
Array.prototype.slice(IE9- 会有难题)。

function fn() { var arr = Array.prototype.slice.call(arguments);
arr.push(4); // arr -> [1, 2, 3, 4] } fn(1, 2, 3);

1
2
3
4
5
6
function fn() {
  var arr = Array.prototype.slice.call(arguments);
  arr.push(4); // arr -> [1, 2, 3, 4]
}
 
fn(1, 2, 3);

抑或可以用 [] 代替 Array.prototype 节省多少个字节。

function fn() { var arr = [].slice.call(arguments); arr.push(4); //
arr -> [1, 2, 3, 4] } fn(1, 2, 3);

1
2
3
4
5
6
function fn() {
  var arr = [].slice.call(arguments);
  arr.push(4); // arr -> [1, 2, 3, 4]
}
 
fn(1, 2, 3);

假使非得追求品质,用 []
会新建个数组,品质肯定没有前者,可是出于发动机的优化,那点距离基本可以忽略不计了(所以众多框架用的就是后世)。

缘何如此可以变换?大家简要了然下,主要的原因是 slice 方法只须求参数有
length
属性即可。首先,slice
方法得到的结果是一个 新的数组,通过 Array.prototype.slice.call
传入的参数(若是为 a),即便没有 length 属性,或者 length 属性值不是
Number 类型,或者为负,那么直接回到一个空数组,否则再次来到a[0]-a[length-1] 组成的数组。(具体可以看下 v8 源码

理所当然,ES6 提供了更便利的法门。

var str = “helloworld”; var arr = Array.from(str); // [“h”, “e”, “l”,
“l”, “o”, “w”, “o”, “r”, “l”, “d”]

1
2
3
var str = "helloworld";
var arr = Array.from(str);
// ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]

出奇目的,将函数的其实参数转换成数组的不二法门。小结下,假诺要把 Array-Like Objects 转为 Array,首选
Array.prototype.slice,可是由于 IE 下 Array.prototype.slice.call(nodes)
会抛出荒唐(because a DOM NodeList is not a JavaScript
object),所以包容的写法如下。(但还有某些要专注的是,要是是 arguments
转为 Array,最好别用 Array.prototype.slice,V8 下会很慢,具体可以看下
幸免修改和传递 arguments 给别的方法 —
影响优化

function nodeListToArray(nodes){ var arr, length; try { // works in
every browser except IE arr = [].slice.call(nodes); return arr; }
catch(err){ // slower, but works in IE arr = []; length =
nodes.length; for(var i = 0; i < length; i++){ arr.push(nodes[i]);
} return arr; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function nodeListToArray(nodes){
  var arr, length;
 
  try {
    // works in every browser except IE
    arr = [].slice.call(nodes);
    return arr;
  } catch(err){
    // slower, but works in IE
    arr = [];
    length = nodes.length;
 
    for(var i = 0; i < length; i++){
       arr.push(nodes[i]);
     }  
 
    return arr;
  }
}

Array-Like

JavaScript 中整整皆为目的,那么什么样是 Array-Like
Objects?顾名思义,就是像数组的对象,当然,数组本身就是目的嘛!稍微有点基础的同窗,一定领会arguments 就是 Array-Like Objects 的一种,能像数组一样用 [] 去访问
arguments 的元素,有 length 属性,可是却无法用一些数组的主意,如
push,pop,等等。

那就是说,什么样的元素是 Array-Like Objects?大家来探望 underscore
中对其的定义。

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property('length');
var isArrayLike = function(collection) {
  var length = getLength(collection);
  return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};

很粗略,不是数组,然而有 length 属性,且属性值为非负 Number
类型即可。至于 length 属性的值,underscore 给出了一个上限值
MAX_ARRAY_INDEX,其实是 MAX_SAFE_INTEGER(感谢 @HangYang 同学提议)
,因为那是 JavaScript 中能精确表示的最大数字。

考虑还有何样同时能满意以上标准的?NodeList,HTML
Collections,仔细思考,甚至还有字符串,或者具有 length
属性的对象,函数(length 属性值为形参数量),等等。

The slice function is intentionally generic; it does not require that
its this value be an Array object. Therefore it can be transferred to
other kinds of objects for use as a method. Whether the slice function
can be applied successfully to a host object is
implementation-dependent.

The slice function is intentionally generic; it does not require that
its this value be an Array object. Therefore it can be transferred to
other kinds of objects for use as a method. Whether the slice function
can be applied successfully to a host object is
implementation-dependent.

Others

许多时候,某个方法您以为接收的参数是数组,其实类数组也是可以的。

Function.prototype.apply()
函数接收的第三个参数,其实也能够是类数组。

var obj = {0: 4, length: 2}; var arr = [1, 2, 3];
Array.prototype.push.apply(arr, obj); console.log(arr); // [1, 2, 3, 4,
undefined]

1
2
3
4
var obj = {0: 4, length: 2};
var arr = [1, 2, 3];
Array.prototype.push.apply(arr, obj);
console.log(arr); // [1, 2, 3, 4, undefined]

Array-Like to Array

有的时候,须求将 Array-Like Objects 转为 Array
类型,使之能用数组的有的艺术,一个非凡简单凶狠并且包容性卓绝的格局是新建个数组,然后循环存入数据。

我们以 arguments 为例。

function fn() {
  // Uncaught TypeError: arguments.push is not a function
  // arguments.push(4);

  var arr = [];
  for (var i = 0, len = arguments.length; i < len; i++)
    arr[i] = arguments[i];

  arr.push(4); // [1, 2, 3, 4]
}

fn(1, 2, 3);

只是那不是最优雅的,更优雅的解法我们一定都知晓了,use
Array.prototype.slice(IE9- 会有题目)。

function fn() {
  var arr = Array.prototype.slice.call(arguments);
  arr.push(4); // arr -> [1, 2, 3, 4]
}

fn(1, 2, 3);

要么可以用 [] 代替 Array.prototype 节省多少个字节。

function fn() {
  var arr = [].slice.call(arguments);
  arr.push(4); // arr -> [1, 2, 3, 4]
}

fn(1, 2, 3);

假若非得追求品质,用 []
会新建个数组,质量肯定没有前者,不过由于发动机的优化,那点距离基本得以忽略不计了(所以重重框架用的就是后者)。

为什么如此可以变换?大家大致精通下,主要的原因是 slice 方法只须要参数有
length
属性即可。首先,slice
方法得到的结果是一个 新的数组,通过 Array.prototype.slice.call
传入的参数(借使为 a),要是没有 length 属性,或者 length 属性值不是
Number 类型,或者为负,那么直接再次来到一个空数组,否则再次来到a[0]-a[length-1] 组成的数组。(具体能够看下 v8 源码
https://github.com/v8/v8/blob/master/src/js/array.js\#L621-L660)

自然,ES6 提供了更省事的法门。

var str = "helloworld";
var arr = Array.from(str); 
// ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]

小结下,倘使要把 Array-Like Objects 转为 Array,首选
Array.prototype.slice,不过由于 IE 下 Array.prototype.slice.call(nodes)
会抛出荒谬(because a DOM NodeList is not a JavaScript
object),所以包容的写法如下。(但还有一些要注意的是,如若是 arguments
转为 Array,最好别用 Array.prototype.slice,V8 下会很慢,具体可以看下
防止修改和传递 arguments 给别的措施 —
影响优化

function nodeListToArray(nodes){
  var arr, length;

  try {
    // works in every browser except IE
    arr = [].slice.call(nodes);
    return arr;
  } catch(err){
    // slower, but works in IE
    arr = [];
    length = nodes.length;

    for(var i = 0; i < length; i++){
       arr.push(nodes[i]);
     }  

    return arr;
  }
} 

《Pro JavaScript Design Patterns》(《JavaScript 设计形式》)的笔者Dustin Diaz 曾提议:

《Pro JavaScript Design
Patterns》(《JavaScript
设计方式》)的作者 Dustin Diaz 曾指出:

Read More

  • How to convert a array-like object to
    array?
  • Advanced Javascript: Objects, Arrays, and Array-Like
    objects
  • JavaScript quirk 8: array-like
    objects
  • 怎么将函数的实际参数转换成数组
  • how does Array.prototype.slice.call()
    work?

打赏协理自己写出越来越多好作品,谢谢!

打赏小编

Others

多多时候,某个方法你觉得接收的参数是数组,其实类数组也是足以的。

Function.prototype.apply()
函数接收的第三个参数,其实也得以是类数组。

var obj = {0: 4, length: 2};
var arr = [1, 2, 3];
Array.prototype.push.apply(arr, obj);
console.log(arr); // [1, 2, 3, 4, undefined]

复制代码 代码如下:

复制代码 代码如下:

打赏接济我写出越多好文章,谢谢!

澳门葡京 1

1 赞 3 收藏
评论

Read More

  • How to convert a array-like object to
    array?
  • Advanced Javascript: Objects, Arrays, and Array-Like
    objects
  • JavaScript quirk 8: array-like
    objects
  • 什么将函数的骨子里参数转换成数组
  • how does Array.prototype.slice.call()
    work?

instead of…
var args = Array.prototype.slice.call(arguments); //
怿飞注:下称方法一
do this…
var args = [].slice.call(arguments, 0); // 怿飞注:下称方法二

instead of…
var args = Array.prototype.slice.call(arguments); //
怿飞注:下称方法一
do this…
var args = [].slice.call(arguments, 0); // 怿飞注:下称方法二

关于小编:韩子迟

澳门葡京 2

a JavaScript beginner
个人主页 ·
我的作品 ·
9 ·
   

澳门葡京 3

但两者的习性差别真的存在吗?经过个人不难测试发现:

但双方的属性差别真的留存呢?经过个人不难测试发现:

在 arguments.length
较小的时候,方法二性质上稍有一点点优势,而在arguments.length
较大的时候,方法一却又稍有优势。

在 arguments.length
较小的时候,方法二特性上稍有一点点优势,而在arguments.length
较大的时候,方法一却又稍有优势。

最后附上方法三,最老土的法子:

最后附上方法三,最老土的点子:

复制代码 代码如下:

复制代码 代码如下:

var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}

var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}

然而对于日常来说,个人指出照旧选拔第三种方式,但其它缓解方案,没有最好的,只有最合适:

只是对此平时来说,个人提议如故利用第三种办法,但任何缓解方案,没有最好的,唯有最合适:

复制代码 代码如下:

复制代码 代码如下:

var args = [].slice.call(arguments, 0);
理由有二:

var args = [].slice.call(arguments, 0);
理由有二:

相似的函数的 arguments.length 都在 10 以内,方法二有优势;
艺术二的代码量上也比第一种少,至少可以减小一点字节 ^^

相似的函数的 arguments.length 都在 10 以内,方法二有优势;
方式二的代码量上也比第一种少,至少可以减小一点字节 ^^

什么样将 NodeList
(比如:document.getElementsByTagName(‘div’))转换成数组呢?

如何将
NodeList
(比如:document.getElementsByTagName(‘div’))转换成数组呢?

解决方案大致如下:

化解方案大概如下:

复制代码 代码如下:

复制代码 代码如下:

function nodeListToArray(nodes){
var arr, length;
try {
// works in every browser except IE
arr = [].slice.call(nodes);
return arr;
} catch(err){
// slower, but works in IE
arr = [];
length = nodes.length;
for(var i = 0; i < length; i++){
arr.push(nodes[i]);
}
return arr;
}
}

function nodeListToArray(nodes){
var arr, length;
try {
// works in every browser except IE
arr = [].slice.call(nodes);
return arr;
} catch(err){
// slower, but works in IE
arr = [];
length = nodes.length;
澳门葡京 ,for(var i = 0; i < length; i++){
arr.push(nodes[i]);
}
return arr;
}
}

何以 IE 中 NodeList 不可以运用 [].slice.call(nodes) 方法转换呢?
In Internet Explorer it throws an error that it can’t run
Array.prototype.slice.call(nodes) because a DOM NodeList is not a
JavaScript object.

为什么 IE 中 NodeList 不可以使用 [].slice.call(nodes) 方法转换呢?
In Internet Explorer it throws an error that it can’t run
Array.prototype.slice.call(nodes) because a DOM NodeList is not a
JavaScript object.

slice 方法将
arguments 对象转换成真正的数组: var args =
Array.prototype.slice.call(arguments); 对于slice 方法,E…

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website