Open lessfish opened 8 years ago
楼主辛苦了,受益匪浅,帮我对Lodash的理解又进了一步
@Shepherdog 楼主有时间也看看 Lodash,哈哈
对于 ([1, 2, 3]).chain() 为什么可以执行思考了半天 ,chain函数没有传参 但实际通过result(this, func.apply(, args)) 已经传递过去了。 感觉自己的逻辑思维或者思考方式是否不对,对于这种函数总是理解起来很困难, 楼主,可否指点一二。
并不是 ([1, 2, 3]).chain() 吧,应该是 _([1, 2, 3]).chain() 吧?@kongzhexin
哈哈哈,上面的,你想了半天为什么可以执行,你倒是去执行看看啊!根本执行不动的东西你想那么久干嘛!
2016-12-02 19:52 GMT+08:00 子迟 notifications@github.com:
并不是 ([1, 2, 3]).chain() 吧,应该是 _([1, 2, 3]).chain() 吧?
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/hanzichi/underscore-analysis/issues/27#issuecomment-264438005, or mute the thread https://github.com/notifications/unsubscribe-auth/ABGB74S0BM564MT-iKHSDK3Gko6fmxpAks5rEAZygaJpZM4Kl95x .
-- GitHub: https://github.com/alsotang
初次使用github 评论 ,打出来正确 显示有问题 ,其实我想问的是思路 @hanzichi
@kongzhexin 没看懂你的意思。_
看做构造方法,然后 _([1, 2, 3])
返回构造对象,对象的原型链上有 chain
这个方法,这不就能执行了?
还在学习中,谢谢楼主。
感谢,现在正在研究中。
@hanzichi 请教楼主,下面的代码有什么作用?百思不得其解,难道method.apply还不够吗?
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
@kongzhexin 时间过去很久了,不知道你有没有弄懂。
_([1, 2, 3]).chain()
这行代码分两部分,第一个部分([1, 2, 3])你应该知道,就是通过`var = function() {}构造函数创建了一个对象,然后把[1, 2, 3]包装到了里面,对应的是_wrapped字段,最终返回了这个新建的对象。 第二部分实际就是调用了上面创建的对象的chain实例方法。chain方法是通过mixin添加到了
var = function() {}`的原型中,因此通过OOP的方式执行chain的时候,实际执行的是以下代码:
.prototype[name] = function() { // 参考_.mixin的实现
// 这里的this就是通过underscore构造函数创建的对象,
// 它的_wrapped属性指向的就是源对象,或者称为被包装对象
var args = [this.wrapped];
// 拼接为完整参数
push.apply(args, arguments);
return result(this, func.apply(, args));
};
那么这里关键就是func.apply,实际执行的又是.chain()了。在执行.chain的时候,传递的第一个也是唯一一个参数就是args中的第一个元素,也就是被包装的那么源对象,那么这里就是[1, 2, 3]
了。
后面的过程就是常规执行_.chain()了,你应该懂的。
@hanzichi 还有一点,result方法里面直接_.chain(obj)
不行吗?为什么一定要_(obj).chain()
感觉饶了一圈啊?我试了下好像也没啥问题。
@rookiePrgrmer if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; 是为了兼容IE毒瘤。
为什么 不能去掉 result函数, 并将
// 执行 func 方法
// 支持链式操作
return result(this, func.apply(_, args));
改为 以下这样呢?(这样更省事,少了一个函数,如果有_chain则直接修改_wrapped,返回原有实例)
let result = func.apply(this, args);
if (this._chain) {
this._wrapped = result;
result = this;
}
return result;
因为 这样会有风险 导致原有的实例属性被更改, 比如 以下代码:
var a = _('abc').chain();
var b = a.toUpperCase();
//此时 a 和b 的 _wrapped都会变为 "ABC"
多谢楼主分享,感谢
前言
终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度。十一月,多事之秋,最近好多事情搞的楼主心力憔悴,身心俱疲,也想尽快把这个系列完结掉,也好了却一件心事。
本文预计是解读系列的倒数第二篇,最后一篇那么显然就是大总结了。楼主的 Underscore 系列解读完整版地址 https://github.com/hanzichi/underscore-analysis
常规调用
之前写的文章,关注点大多在具体的方法,具体的知识细节,也有读者留言建议楼主讲讲整体架构,这是必须会讲的,只是楼主把它安排在了最后,也就是本文,因为楼主觉得不掌握整体架构对于具体方法的理解也是没有大的问题的。
Underscore 大多数时候的调用形式为
_.funcName(xx, xx)
,这也是 文档中 的调用方式。最简单的实现方式,我们可以把
_
看做一个简单的对象:在 JavaScript 中,一切皆对象,实际上,源码中的
_
变量是一个方法:为什么会是方法?我们接下去看。
OOP
Underscore 支持 OOP 形式的调用:
这其实是非常经典的「无 new 构造」,
_
其实就是一个 构造函数,_([1, 2, 3])
的结果就是一个对象实例,该实例有个_wrapped
属性,属性值是[1, 2, 3]
。实例要调用each
方法,其本身没有这个方法,那么应该来自原型链,也就是说_.prototype
上应该有这个方法,那么,方法是如何挂载上去的呢?方法挂载
现在我们已经明确以下两点:
_
是一个函数(支持无 new 调用的构造函数)_
的属性有很多方法,比如_.each
,_.template
等等我们的目标是让
_
的构造实例也能调用这些方法。仔细想想,其实也不难,我们可以遍历_
上的属性,如果属性值类型是函数,那么就将函数挂到_
的原型链上去。源码中用来完成这件事的是
_.mixin
方法:_.mixin 方法可以向 Underscore 库增加自己定义的方法:
同时,Underscore 也加入了一些 Array 原生的方法:
链式调用
Underscore 也支持链式调用:
乍一看似乎有 OOP 和非 OOP 两种链式调用形式,其实只是一种,
_.chain([1, 2, 3])
和_([1, 2, 3]).chain()
的结果是一样的。如何实现的?我们深入chain
方法看下。我们看下
_.chain([1, 2, 3])
的结果,将参数代入函数中,其实就是对参数进行无 new 构造,然后返回实例,只是实例多了个_chain
属性,其他的和直接_([1, 2, 3])
一模一样。再来看_([1, 2, 3]).chain()
,_([1, 2, 3])
返回构造实例,该实例有chain
方法,调用方法,为实例添加_chain
属性,返回该实例对象。所以,这两者效果是一致的,结果都是转为了 OOP 的形式。说了这么多,似乎还没讲到正题上,它是如何「链」下去的?我们以如下代码为例:
当调用
map
方法的时候,实际上可能会有返回值。我们看下_.mixin
源码:result
是一个重要的内部帮助函数(Helper function ):如果需要链式操作(实例会有带有 _chain 属性),则对运算结果调用
chain
函数,使之可以继续链式调用。小结
Underscore 整体架构,或者说是基础实现大概就是这个样子,代码部分就讲到这了,接下去系列解读最后一篇,讲讲这段时间(几乎也是历时半年了)的一些心得体会吧,没钱的就捧个人场吧!