前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >类数组借用数组方法

类数组借用数组方法

作者头像
晚晴幽草轩轩主
发布2018-03-27 15:26:01
1.2K0
发布2018-03-27 15:26:01
举报
文章被收录于专栏:静晴轩静晴轩
于Ja

何为“类数组”

JavaScript中有一些看起来像却又不是数组的对象,唤作: 类数组。一个类数组对象:

  • 具有:指向对象元素的数字(非负整数)索引下标以及length属性告诉我们对象的元素个数
  • 不具有:诸如 push forEach 以及 indexOf 等数组对象具有的方法

javascript中常见的类数组有arguments对象,DOM方法或者JQuery方法的返回结果。 比如document.getElementsByTagName()。实际上,只要有length属性,且它的属性值为number类型即可。

类数组示例:

代码语言:javascript
复制
var a = {'1':'gg','2':'love','4':'jeffjade',length:5};
Array.prototype.join.call(a,'+');//'+gg+love++jeffjade'

非类数组示例:

代码语言:javascript
复制
var c = {'1':2};

没有length属性,所以就不是类数组。

借用数组方法

法一:用数组什么方法,借助call或者apply即可,比如;

代码语言:javascript
复制
(function(){
    Array.prototype.push.call(arguments, 4);
    console.log(arguments instanceof Array);  // false
    console.log(arguments);
    //OutPut: [1,2,3,4]  //Chrome Console
    //OutPut: / { '0': 1, '1': 2, '2': 3, '3': 4 } //SublimeText NodeJs
})(1,2,3);

法二:函数反柯里化(function uncurrying)

Array.prototype上的方法原本只能用来操作array对象。但用call apply 可以把任意对象当做this传入某个方法,如此一来,方法中用到的this的地方就不再局限于原来规定的对象,而是加以泛华并且得到更广的适用性。

但是直接使用这样使用,多少是有些繁琐的。如需使用Array的shift方法,就还得写Like This:Array.prototype.shift.call(arguments);;如能将泛化this的过程提取出来,岂不方便很多?并且代码还能复用。

uncurrying的话题来自JavaScript之父Brendan Eich在2011年发表的一篇Twitter。 以下代码是uncurrying的实现方式之一@注解^

代码语言:javascript
复制
Function.prototype.uncurrying = function() {
    var self = this;
    return function() {
        var obj = Array.prototype.shift.call(arguments);
        return self.apply(obj, arguments);
    }
}

其作用如是:在类数组对象借用Array.prototype方法之前,先把Array.prototype.push.call这句代码转换为一个通用的push函数:

代码语言:javascript
复制
var push = Array.prototype.push.uncurrying();
(function(){
truepush(arguments , 4);
trueconsole.log(arguments);
true//OutPut: [1,2,3,4]  //Chrome Console
    //OutPut: / { '0': 1, '1': 2, '2': 3, '3': 4 } //SublimeText NodeJs
})(1,2,3);

通过uncurrying方式,使得Array.prototype.push.call变成了一个通用的push函数,且其函数的作用也不再仅仅局限于只能操作array对象。于使用者而言,也显得更加简洁和意图明了。

幸甚,还可以一次性地将Array.prototype上的方法“复制”到array对象上。

代码语言:javascript
复制
var ary = ['push', 'shift', 'forEach']
for (var i = 0, fn ; fn = ary[i++];) {
trueArray[ fn ] = Array.prototype[ fn ].uncurrying();
};

var obj = {
true"length": 2,
true"0":1,
true"1":2
};

Array.push(obj, 3); // 3
console.log(obj.length);
console.log(obj); //Object {0: 1, 1: 2, 2: 3, length: 3}

var first = Array.shift(obj);
console.log(first);  // 1
console.log(obj);    //Object {0: 2, 1: 3, length: 2}

Array.forEach(obj , function(i , n){
trueconsole.log(i);  // 分别输出 2 3
trueconsole.log(n);  // 分别输出 0 1
})

当然,function uncurrying还有其他实现方式@注解^,比如:

代码语言:javascript
复制
Function.prototype.uncurrying = function() {
    var self = this;
    return function() {
        // var obj = Array.prototype.shift.call(arguments);
        // return self.apply(obj, arguments);
        return Function.prototype.call.apply(self, arguments);
    }
}
代码稍做分析

就取Array.prototype.push.uncurrying()这句代码来分析下,uncurrying的时候发生了什么:

代码语言:javascript
复制
Function.prototype.uncurrying = function() {
    var self = this; // self此时是Array.prototype.push
    return function() {
        // var obj = Array.prototype.shift.call(arguments);
        // return self.apply(obj, arguments);
        return Function.prototype.call.apply(self, arguments); //法2
    }
}

var push = Array.prototype.push.uncurrying();
var obj = {
    "length":1,
    "0":1
}
push(obj , 2); //uncurrying函数接收到的arguments即'obj ,2'
console.log(obj); //Outpt: {0:1, 1:2,length:2}
function uncurrying 法一:

用法一,因为Array.prototype.shift的截断,arguments,即剩下[2]了;相当于如下代码

代码语言:javascript
复制
var obj = Array.prototype.shift.call(arguments);
return Array.prototype.push.apply(obj, arguments);//此时arguments=2;
function uncurrying 法二:

实现方式二,很有趣;可参见@stackoverflow透彻回答

代码语言:javascript
复制
return Function.prototype.call.apply(self, arguments);
//self此时是Array.prototype.push

大体如此:Function.prototype.call是一个函数;call的this指向Function.prototype;使用apply改变了this的指向到Array.prototype.push;arguments 就被给传了call。原文如下:

  1. Function.prototype.call is a function.
  2. The this pointer of call points to Function.prototype.
  3. We use apply to change the this pointer of call to Array.prototype.push.
  4. arguments is applied (not passed as a parameter) to call. The advantage of this is that we’re creating a fast unbound wrapper for push in a single line.

继续看该Answer,其文提到了bind;而bind~绑定函数,会以创建它是传入bind()方法的第一个参数作为this,传入bind()方法的第二个及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

按照bind的功能,其实在这里bind就可以替代apply, 从而可以有这种写法了咯;而这个bind”听起来”怎么那么像call呢?后面那个方法不过就是改变下前面call的this的指向,所以apply替换call也没什么不可以的嘛,测试一下:果然可以!

代码语言:javascript
复制
Function.prototype.uncurrying = function() {
    var self = this;
    return function() {
        // return Function.prototype.call.apply(self, arguments);
        return Function.prototype.call.bind(self, arguments);
        // return Function.prototype.call.call(self, arguments);
    }
}

只是,这样用的话就得为考虑浏览器的兼容性而写些Shim了.如原回答所述:

A better way to create fast unbound wrappers is as follows (note that it may not work in some older browsers, but you don’t really need to worry about that now - you may always use a shim for browsers which don’t support bind):


@JQuery的v2.1.4版本对makeArray方法实现源码:

代码语言:javascript
复制
// results is for internal usage only
truemakeArray: function( arr, results ) {
truetruevar ret = results || [];

truetrueif ( arr != null ) {
truetruetrueif ( isArraylike( Object(arr) ) ) {
truetruetruetruejQuery.merge( ret,
truetruetruetruetruetypeof arr === "string" ?
truetruetruetruetrue[ arr ] : arr
truetruetruetrue);
truetruetrue} else {
truetruetruetruepush.call( ret, arr );
truetruetrue}
truetrue}
truetruereturn ret;
true},

truemerge: function( first, second ) {
truetruevar len = +second.length,
truetruetruej = 0,
truetruetruei = first.length;

truetruefor ( ; j < len; j++ ) {
truetruetruefirst[ i++ ] = second[ j ];
truetrue}

truetruefirst.length = i;
truetruereturn first;
true}

其中isArraylike()代码实现可以参见这里

参考出处:@曾探 所著的《JavaScript设计模式与开发实践》第三章~高阶函数.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 何为“类数组”
  • 借用数组方法
    • 法一:用数组什么方法,借助call或者apply即可,比如;
      • 法二:函数反柯里化(function uncurrying)
        • 代码稍做分析
        • function uncurrying 法一:
        • function uncurrying 法二:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档