The chain pattern is a way of calling methods in a chained manner. It does not belong to the category of the generally defined 23
design patterns, but is usually considered as a broadly categorized technique design pattern.
Chained calls are very common in the JavaScript
language, such as in jQuery
, Promise
, etc., all of which use chained calls. When we need to access the properties or methods of the same object multiple times, we often have to write the object multiple times for the .
or ()
operations. Chained calls are a way to simplify this process, making the code concise and readable.
There are usually several ways to implement chained calls, but fundamentally they are similar, all by returning objects for later calls.
this
, which is the implementation method of jQuery
, is usually the method used for chained calls.this
, explicitly returns the chained object.When it comes to chained calls, it is necessary to mention the optional chaining operator in JavaScript
, which is part of the new ES2020
features, including operators such as ?.
, ??
, and ??=
. The optional chaining operator ?.
allows reading the value of a property deep within an object chain without having to explicitly validate each reference in the chain. The ?.
operator functions similarly to the .
chaining operator, but the difference is that it does not cause an error when the reference is nullish
, i.e., null
or undefined
, and the expression short-circuits to return the value of undefined
. When used with function calls, if the given function does not exist, it returns undefined
. When attempting to access a property of an object that may not exist, the optional chaining operator makes the expression shorter and more concise. It is also very helpful when exploring the content of an object if it is uncertain which properties must exist.
jQuery
is a high-end but luxurious framework with many wonderful methods and logic. Although it is not as popular now as MVVM
-mode frameworks like Vue
and React
, the design of jQuery
is great and definitely worth learning. Let's take the most basic instantiation of jQuery
as an example here to explore how jQuery
achieves chained calls using this
.
First, let's define a basic class and use the prototype chain to inherit methods.
By defining a class and instantiating it, the instances can share methods on the prototype, but calling methods directly on the _jQuery
class obviously won't work. The first type of exception thrown is because there are no static methods on the _jQuery
class, and the second type of exception is because _jQuery
as a function call didn't return a value. Here we can see that when calling jQuery
via $()
, it returns an object containing multiple methods, which we cannot access directly. We have to use another variable to access it.
Actually, in order to reduce variable creation, jQuery
directly treats _fn
as a property of _jQuery
.
So far, it can indeed achieve the invocation of methods on the prototype via _jQuery()
, but in jQuery
, the main purpose of $()
is to be used as a selector to select elements, and what is currently returned is an _jQuery.fn
object, which is obviously not what we want. In order to obtain the returned elements, we need to define an init
method on the prototype to obtain the elements. Here, for simplicity, we directly use document.querySelector
, while in reality, the construction of the jQuery
selector is much more complex.
But it seems like this leaves out the chained call of this
, so we need to use the pointing of this
. Since this
always points to the object that calls it, we just need to mount the selected element on the this
object here.
But then a problem emerged. The elements selected by our selector are directly attached to _jQuery.fn
. In this way, since the prototype is shared, subsequently defined selectors will overwrite the ones defined earlier. This obviously won't work. So, we use the new
operator to create a new object.
This introduced another issue. When we use new
to instantiate _jQuery.fn.init
, the this
returned points to the instance of _jQuery.fn.init
, which prevents us from making chained calls. jQuery
used a very clever method to solve this problem by directly pointing the prototype of _jQuery.fn.init
to _jQuery.prototype
. Although there might be a problem of circular reference, the performance overhead is not significant. Thus, we have completed the implementation of the jQuery
selector and chained calls.
https://zhuanlan.zhihu.com/p/110512501 https://juejin.cn/post/6844904030221631495 https://segmentfault.com/a/1190000011863232 https://github.com/songjinzhong/JQuerySource https://leohxj.gitbooks.io/front-end-database/content/jQuery/jQuery-source-code/index.html https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/%E5%8F%AF%E9%80%89%E9%93%BE