Detailed explanation of chain call in JavaScript
- 2021-09-24 21:31:51
- OfStack
Chain pattern
Chain mode is a form of chained invocation, which does not exactly belong to the commonly defined design pattern category, but it is a very useful code building technique.
Describe
Chain call is very common in JavaScript language, such as jQuery, Promise, etc., all of which use chain call. When we call the same object for many times its attributes or methods, we need to write the object for many times. Or () operation. Chain call is a coding way to simplify this process, so that the code is concise and easy to read.
Chain invocation is usually implemented in the following ways, but they are essentially similar, by returning objects for later invocation.
var Person = function() {};
Person.prototype.setAge = function(age){
this.age = age;
return this;
}
Person.prototype.setWeight = function(weight){
this.weight = weight;
return this;
}
Person.prototype.get = function(){
return `{age: ${this.age}, weight: ${this.weight}}`;
}
var person = new Person();
var des = person.setAge(10).setWeight(30).get();
console.log(des); // {age: 10, weight: 30}
var person = {
age: null,
weight: null,
setAge: function(age){
this.age = age;
return this;
},
setWeight: function(weight){
this.weight = weight;
return this;
},
get: function(){
return `{age: ${this.age}, weight: ${this.weight}}`;
}
};
var des = person.setAge(10).setWeight(30).get();
console.log(des); // {age: 10, weight: 30}
function numsChain(num){
var nums = num;
function chain(num){
nums = `${nums} -> ${num}`;
return chain;
}
chain.get = () => nums;
return chain;
}
var des = numsChain(1)(2)(3).get();
console.log(des); // 1 -> 2 -> 3
Optional chain operator
When it comes to chain calls, it is necessary to say that the optional chain operator of JavaScript under 1 belongs to the new feature operator of ES2020? .,? ? ,? ? =, optional chain operator? Allows the value of a property located deep in the chain of connected objects to be read without explicitly verifying that each reference in the chain is valid. ? The. operator functions similar to the. chain operator except that it does not cause an error if the reference is null nullish, i.e. null or undefined, and the short-circuit return value of the expression is undefined. When used with function call 1, undefined is returned if the given function does not exist. When trying to access an object property that may not exist, the optional chain operator will make the expression shorter and more concise. When exploring the contents of an object, the optional chain operator is also helpful if you are not sure which attributes must exist.
Grammar
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
Example
const obj = {a: {}};
console.log(obj.a); // {}
console.log(obj.a.b); // undefined
// console.log(obj.a.b.c); // Uncaught TypeError: Cannot read property 'c' of undefined
console.log(obj && obj.a); // {}
console.log(obj && obj.a && obj.a.b && obj.a.b.c); // undefined
console.log(obj?.a?.b?.c); // undefined
const test = void 0;
const prop = "a";
console.log(test); // undefined
console.log(test?.a); // undefined
console.log(test?.[prop]); // undefined
console.log(test?.[0]); // undefined
console.log(test?.()); // undefined
Chain Call in jQuery
jQuery is a high-end and luxurious framework, in which there are many wonderful methods and logic. Although it is very popular in the framework of MVVM mode similar to Vue and React, the design of jQuery is really great and worth learning. Here, taking the most basic instantiation of jQuery as an example, we explore how jQuery can be called chain through this.
Firstly, a basic class is defined, and methods are inherited through prototype chain.
function _jQuery(){}
_jQuery.prototype = {
constructor: _jQuery,
length: 2,
size: function(){
return this.length;
}
}
var instance = new _jQuery();
console.log(instance.size()); // 2
// _jQuery.size() // Uncaught TypeError: _jQuery.size is not a function
// _jQuery().size() / /Uncaught TypeError: Cannot read property 'size' of undefined
By defining a class and instantiating it, Methods on prototypes can be shared between instances, It is obviously impossible to call directly through the _jQuery class. The first exception is thrown because there is no static method on the _ jQuery class, and the second exception is because _ jQuery does not return a value after being executed as a function. It can be seen from this that jQuery returns an object containing multiple methods when it is called by $(), but it is only accessible by itself, so we use another variable to access it.
function _jQuery(){
return _fn;
}
var _fn = _jQuery.prototype = {
constructor: _jQuery,
length: 2,
size: function(){
return this.length;
}
}
console.log(_jQuery().size()); // 2
In fact, in order to reduce the creation of variables, jQuery directly regards _fn as one attribute of _jQuery.
function _jQuery(){
return _jQuery.fn;
}
_jQuery.fn = _jQuery.prototype = {
constructor: _jQuery,
length: 2,
size: function(){
return this.length;
}
}
console.log(_jQuery().size()); // 2
At this point, we can really call the methods on the prototype in the way of _ jQuery (). However, in jQuery, the main goal of $() is to select elements as a selector, but now it returns a _ jQuery. fn object, which obviously fails to meet the requirements. In order to obtain the returned elements, it defines an init method on the prototype to obtain elements. Here, document. querySelector is directly used to save trouble. In fact, the selector construction of jQuery is very complicated.
function _jQuery(selector){
return _jQuery.fn.init(selector);
}
_jQuery.fn = _jQuery.prototype = {
constructor: _jQuery,
init: function(selector){
return document.querySelector(selector);
},
length: 3,
size: function(){
return this.length;
}
}
console.log(_jQuery("body")); // <body>...</body>
It seems that the chain call this is omitted again, so we need to use the pointing of this here, because this always points to the object calling him when calling, so we can mount the selected element on the this object here.
function _jQuery(selector){
return _jQuery.fn.init(selector);
}
_jQuery.fn = _jQuery.prototype = {
constructor: _jQuery,
init: function(selector){
this[0] = document.querySelector(selector);
this.length = 1;
return this;
},
length: 3,
size: function(){
return this.length;
}
}
var body = _jQuery("body");
console.log(body); // {0: body, length: 1, constructor: ƒ, init: ƒ, size: ƒ}
console.log(body.size()); // 1
console.log(_jQuery.fn); // {0: body, length: 1, constructor: ƒ, init: ƒ, size: ƒ}
However, there is another problem at this time. The elements selected by our selector are directly mounted on _ jQuery. fn. In this way, because the prototype is shared, the selector defined later will overwrite the selector defined earlier, which is obviously impossible, so we use new operator to create a new object.
var person = {
age: null,
weight: null,
setAge: function(age){
this.age = age;
return this;
},
setWeight: function(weight){
this.weight = weight;
return this;
},
get: function(){
return `{age: ${this.age}, weight: ${this.weight}}`;
}
};
var des = person.setAge(10).setWeight(30).get();
console.log(des); // {age: 10, weight: 30}
0
So there is a problem again, When we instantiate _ jQuery. fn. init with new, the returned this points to an instance of _ jQuery. fn. init, We can't make chain calls. jQuery solves this problem with a very clever method, and directly points the prototype of _ jQuery. fn. init to _ jQuery. prototype. Although there will be the problem of circular reference, this one performance consumption is relatively nothing, so we have completed the implementation of jQuery selector and chain calls.
var person = {
age: null,
weight: null,
setAge: function(age){
this.age = age;
return this;
},
setWeight: function(weight){
this.weight = weight;
return this;
},
get: function(){
return `{age: ${this.age}, weight: ${this.weight}}`;
}
};
var des = person.setAge(10).setWeight(30).get();
console.log(des); // {age: 10, weight: 30}
1
1 question per day
https://github.com/WindrunnerMax/EveryDay
The above is a detailed explanation of JavaScript chain call details, more information about JavaScript chain call please pay attention to other related articles on this site!