-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
underscore 系列之链式调用 #57
Comments
您好, this.elements = [this.elements[num]]; |
@xxxgitone 非常感谢指出~ o( ̄▽ ̄)d |
本条评论用作修改日志。
修改 感谢 @xxxgitone |
楼主能分享一下你是如何阅读源码的吗? |
@Soyn 刚开始是因为写 JavaScript 专题系列会涉及到去重、查找数组元素、数组扁平化等等功能,所以研究了 underscore 中这些功能的实现方式,然后写 underscore 系列才开始正式读源码。 因为 underscore 它是一个功能函数库,所以首先要搞明白的就是那么多的函数,是如何组织的?这其实就是这个系列第一篇和第二篇的内容。 接下来是阅读内部函数如 cb、optimizeCb、restArgs、shallowProperty、deepGet,因为这些函数作为内部函数会被多次用到。 最后再是跟着兴趣,你想了解哪些函数的实现就去研究哪些函数的实现方式。 在具体研究一些函数的实现方式时,可以参考一些已经写过的源码分析的文章是如何解读的,可以事半功倍: |
我读underscore代码的时候,第一遍是直接看源码,很多地方看的云里雾里,后面看第二遍的时候,看underscore的单元测试加源码,但是读完之后很快就忘了,和楼主相比实在是惭愧。 |
@Soyn 不敢当啦,因为要写文章,很多有疑问的地方都要想明白,要不然,被人问了,答错了就尴尬了…… |
跟着大神再看一遍underscore,昨天看到你在知乎回答的如何学习前端,我打算按照推荐的步骤开始学 |
@FrontToEnd 哈哈,谢谢提醒,补一下我的回答 怎样系统地自学前端?,其实这几个系列就是按照这样的思路去写的~ |
为什么不直接return this? |
instance._chain ? _(obj).chain() : obj; 什么情况下才会返回obj??instance._chain 是肯定有值的呀 |
感谢感谢,十分感谢!! |
@wulinsheng123 应该是当你不需要 链式调用 的时候吧。 |
_(obj).chain() 这里不是又创建了一次实例吗? 那么两个实例将不共享同一个属性, 按照道理应该是 |
@wudao370859172 应该是因为像push,shift这些函数本身都有返回的值,return this会改变这些方法调用时的返回值的~ |
@Gloomysunday28 但是函数的返回值不是instance,而是obj呀。考虑代码 |
@mqyqingfeng 博主,你二版的代码 |
没有_chain()过的话.instance._chain是undefined |
你这个写法收集不了 函数返回值 |
问题1:我只是在最终版的闭包里加了一个push方法和console.log(.chain([1]).push(2)); |
看了一晚上,debug了源码对自己提的三个问题有了一些了解。 |
我感觉这个版本的链式实现有点冗余了,不停的创建了新的实例,感觉没必要,事实上push直接返回this就好了,顺便写下我的code function _(obj) {
// eslint-disable-next-line new-cap
if (!(this instanceof _)) return new _(obj)
this._wraps = obj
}
// 遍历所有的函数 返回函数名称数组
_.functions = function (obj) {
const funcs = []
for (const key in obj) {
if (typeof obj[key] === 'function') funcs.push(key)
}
return funcs
}
_.chain = function (obj) {
const instance = _(obj)
instance._chain = true
return instance
}
_.prototype.push = function (num) {
this._wraps.push(num)
return this
}
_.prototype.shift = function () {
this._wraps.shift()
return this
}
_.prototype.value = function () {
return this._wraps
}
// 将underscore的函数放入到新的实例中去
_.mixin = function (obj) {
_.functions(obj).forEach((name) => {
const func = (_[name] = obj[name])
_.prototype[name] = function (arg) {
// 这里 context是underscore 是因为this需要访问underscore的成员变量和函数
// 如果是this的话, allarg 的参数会有问题
const allArg = [this._wraps].concat([arg])
return func.apply(_, allArg)
}
})
return _
}
_.mixin(_)
// underscore.version('yakir')
// underscore('laige').version('add').version()
const res = _.chain([1, 2, 3]).push(4).shift().push(9).value()
console.log(res) |
@jxccc1998 不清楚你要问的是什么,新增的函数基本都是直接挂载 |
function JQuery(selector) {
this.elements = [];
var nodeLists = document.getElementsByTagName(selector);
for (var i = 0; i < nodeLists.length; i++) {
this.elements.push(nodeLists[i]);
}
return this;
}
JQuery.prototype = {
eq: function(num){
this.elements = [this.elements[num]];
return this;
},
css: function(prop, val) {
this.elements.forEach(function(el){
el.style[prop] = val;
})
return this;
},
show: function() {
this.css('display', 'block');
return this;
}
} @mqyqingfeng 很奇怪为啥这段代码原型上没有constructor为啥也能正常实例化 |
前言
本文接着上篇《underscore 系列之如何写自己的 underscore》,阅读本篇前,希望你已经阅读了上一篇。
jQuery
我们都知道 jQuery 可以链式调用,比如:
我们写个简单的 demo 模拟链式调用:
jQuery 之所以能实现链式调用,关键就在于通过
return this
,返回调用对象。再精简下 demo 就是:_.chain
在 underscore 中,默认不使用链式调用,但是如果你想使用链式调用,你可以通过 _.chain 函数实现:
我们看看 _.chain 这个方法都做了什么:
我们以 [1, 2, 3] 为例,_.chain([1, 2, 3]) 会返回一个对象:
该对象的原型上有着 underscore 的各种方法,我们可以直接调用这些方法。
但是问题在于原型上的这些方法并没有像 jQuery 一样,返回 this ,所以如果你调用了一次方法,就无法接着调用其他方法了……
但是试想下,我们将函数的返回值作为参数再传入
_.chain
函数中,不就可以接着调用其他方法了?写一个精简的 Demo:
然而这也太复杂了吧,难道 chain 这个过程不能是自动化的吗?如果我是开发者,我肯定希望直接写成:
所以我们再优化一下实现方式:
我们在每个函数中,都用 chainResult 将函数的返回值包裹一遍,再生成一个类似以下这种形式的对象:
该对象的原型上有各种函数,而这些函数的返回值作为参数传入了 chainResult,该函数又会返回这样一个对象,函数的返回值就保存在 _wrapped 中,这样就实现了链式调用。
_.chain
链式调用原理就是这样,可是这样的话,我们需要对每个函数都进行修改呀……幸运的是,在 underscore 中,所有的函数是挂载到
_
函数对象中的,_
.prototype 上的函数是通过_.mixin
函数将_
函数对象中的所有函数复制到_.prototype
中的。所以为了实现链式调用,我们还需要对上一篇《underscore 系列之如何写自己的 underscore》 中的
_.mixin
方法进行一定修改:_.value
根据上面的分析过程,我们知道如果我们打印:
其实会打印一个对象
{_chain: true, _wrapped: [2, 3, 4] }
可是我希望获得值是 [2, 3, 4] 呀!
所以,我们还需要提供一个 value 方法,当执行 value 方法的时候,就返回当前 _wrapped 的值。
此时调用方式为:
最终代码
结合上一篇文章,最终的 underscore 代码组织结构如下:
underscore 系列
underscore 系列目录地址:https://github.com/mqyqingfeng/Blog。
underscore 系列预计写八篇左右,重点介绍 underscore 中的代码架构、链式调用、内部函数、模板引擎等内容,旨在帮助大家阅读源码,以及写出自己的 undercore。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: