“This is the first day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021”
preface
Why am I writing this article? It all started when EDG won the title and I posted a message on moments after the third game… The diagram below.
I didn’t expect EDG to be so tough and so passionate about China. Anyway, congratulations TO EDG for setting the flag and crying to make it come true. (Actually pretty good, enrich yourself.) Now every day and weekends free time to read a few, take notes, put together a paper on the weekend, so go to the nuggets.
This, call, and apply
this
In JavaScript, the object this points to is dynamically bound at runtime based on the execution environment of the function, not the environment in which the function was declared
This points to the
With the exception of with and eval, which can “destroy” our understanding of scope, the directions for this fall into the following categories.
- Methods as objects
- As a general function
- Constructor call
Function.prototype.call
或Function.prototype.apply
call
When called as a method on an object, this refers to that object
var obj = {
name:'moe'.getName:function() {
console.log(this === obj) // true
console.log(this.name) // moe
}
}
obj.getName()
Copy the code
When the function is called as a normal function rather than as an object property, this points to the global object, which in browser JavaScript is window
window.name = 'moe'
var getName = function() {
return this.name
}
var person = {
name:'kid'.getName:function() {
return this.name
}
}
var globalGetName = person.getName
console.log( getName() ) // moe
console.log( globalGetName() ) // moe
Copy the code
Called as a constructor
- When called with the new operator, the function returns an object, and normally this in the constructor refers to the object returned.
var MyClass = function() {
this.name = 'moe'
}
var obj = new MyClass()
console.log(obj.name) // moe
Copy the code
But if the constructor explicitly returns an object of type Object, that object is returned instead of this.
var MyClass = function() {
this.name = 'moe'
return {
name: 'kid'}}var obj = new MyClass()
console.log(obj.name) // kid
Copy the code
This problem does not occur if the constructor does not explicitly return data or non-object type data.
Function.prototype.call or function.prototype. apply calls that can dynamically change the this of the passed Function
var obj1 = {
name:'a'.getName:function() {
return this.name
}
}
var obj2 = {
name:'b'
}
console.log(obj1.getName()) // a
console.log(obj1.getName.call(obj2)) // b
Copy the code
Call and apply
Call and apply are used to modify this Pointers and execute functions, the only difference being the form of their input arguments
- Apply takes two arguments. The first argument specifies what this object in the function body points to, and the second argument is a collection (an array or an array of classes). The apply method passes elements of this collection as arguments to the called function.
- Call is essentially the syntactic sugar of apply. It takes a variable number of arguments. The first argument, like apply, points to this in the function, and from the second argument, each argument is passed to the function.
var foo = function(a, b, c) {
console.log([a, b, c]) / / [1, 2, 3]
}
foo.apply(null[1.2.3])
foo.call(null.1.2.3)
Copy the code
In the code above, the first argument we pass in is null, and this in the function body points to the default host object.
Use of call and apply
- Change the direction of this
- Borrow other object methods
With apply or Call, you can achieve something similar to inheritance.
var A = function (name) {
this.name = name
}
var B = function() {
A.apply(this.arguments)
}
B.prototype.getName = function() {
return this.name
}
var b = new B('newBee')
console.log( b.getName() ) // newBee
Copy the code
Function arguments is an array-like object. Since it is not a true array, it cannot sort or add or remove elements to a collection like an array. In this case we can borrow methods on the array. prototype object, such as push.
(function() {
Array.prototype.push.call(arguments.3);
console.log(arguments); / / [1, 2, 3]}) (1.2)
Copy the code
Closures and higher-order functions
closure
Variable scope
If a variable is declared in a function without the keyword var, it becomes a global variable. If a variable is declared in a function with the keyword var, it is a local variable of the function and can be accessed only in the function.
var foo = function() {
a = 1
var b = 2
console.log('in foo:' , b) // in foo: 2
}
foo()
console.log( a ) / / 1
console.log( b ) // Uncaught ReferenceError: b is not defined
Copy the code
Functions can be used to create function scopes that are like a layer of translucent glass, with variables visible inside the function but not inside the function. This is because when a variable is searched in a function, if it is not declared in the function, the search is searched layer by layer down the chain of scopes created by the code execution environment, up to the global object.
var a = 1
var bar = function() {
var b = 2
var foo = function() {
var c = 3
console.log( b ) / / 2
console.log( a ) / / 1
}
foo()
console.log( c ) // Uncaught ReferenceError: c is not defined
}
bar()
Copy the code
The lifetime of a variable
- Global variables live forever unless they are actively destroyed.
- Local variables within a function are destroyed at the end of the function call.
The existence of closures allows us to continue the life of local variables within a function
var func = function() {
var a = 1
return function() {
a++
console.log(a)
}
}
var f = func()
f() / / 2
f() / / 3
f() / / 4
Copy the code
Similarly, we must have encountered problems like this
/* The following print result is 5 5, why, how to print 0 to 4? * /
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log( i );
}, 1000);
}
console.log(new Date, i);
Copy the code
I in this function actually refers to the last I value, why not 0,1,2,3,4… ? Because when you are a for loop, you did not perform this function, this function is a second you, that is executed when performing this function, it finds itself without the variable I, so he sends it look in the scope of the chain for the variable I, because this time has finished the for loop, so the stored within the scope chain I value is 5, And then you get a 5.
Using closure solution, by self-executing the function, the variable I is saved in the parameters of the function, extending its life cycle.
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 1000);
})(i);
}
console.log(i);
Copy the code
More on closures
- Encapsulation variable
If there are small chunks of code that can stand on their own, we usually wrap them in small functions that can be reused. If they don’t need to be used elsewhere in the program, it’s best to close them with closures.
var mult = (function() {
var cache = {}
var calculate = function() {
var a = 1
for(var i = 0, l = arguments.length; i < l; i++ ) {
a = a * arguments[i]
}
return a
}
return function() {
var args = Array.prototype.join.call(arguments.', ');
if(args in cache) {
return cache[args]
}
return cache[args] = calculate.apply(null.arguments)}})Copy the code
- Perpetuate local variable life
var report = (fucntion() {
var imgs = []
return function(src) {
var img = new Image()
imgs.push(img)
img.src = src
}
})
Copy the code
Closures and object-oriented design
// Closure
var extent = function() {
var value = 0
return {
call:function() {
value++
console.log(value)
}
}
}
var extent = extent();
extent.call(); // Output: 1
extent.call(); // Output: 2
extent.call(); // Output: 3
// Object oriented
var extent2 = {
value:0.call:function() {
this.value++
console.log(this.value)
}
}
extent2.call(); // Output: 1
extent2.call(); // Output: 2
extent2.call(); // Output: 3
Copy the code
Implement command patterns with closures
<html>
<body>
<button id="undo">Click I execute command</button>
<button id="execute">Click I execute command</button>
<script>
var Tv = {
open: function(){
console.log( 'Turn on the TV' );
},
close: function(){
console.log( 'Turn off the TV'); }};var OpenTvCommand = function( receiver ){
this.receiver = receiver;
};
OpenTvCommand.prototype.execute = function(){
this.receiver.open(); // Execute the command to turn on the TV
};
OpenTvCommand.prototype.undo = function(){
this.receiver.close(); // Undo the command and turn off the TV
};
var setCommand = function( command ){
document.getElementById( 'execute' ).onclick = function(){
command.execute(); // Output: turn on the TV
}
document.getElementById( 'undo' ).onclick = function(){
command.undo(); // Output: turn off the TV}}; setCommand(new OpenTvCommand( Tv ) );
</script>
</body>
</html>
Copy the code
Higher-order functions
The function corrification refers to the function output as an argument or function as a return value.
Functions are passed as arguments
-
The callback function
- An asynchronous callback
var getUserInfo = function(userId, cb) { $.ajax('http://xxx.com/getUserInfo?' + userId , function(data) { if(typeof cb === 'function') { cb(data) } }) } getUserInfo(10086.(data) = > { console.log(data) }) Copy the code
- Event delegation
var appendDiv = function( callback ){ for ( var i = 0; i < 100; i++ ){ var div = document.createElement( 'div' ); div.innerHTML = i; document.body.appendChild( div ); if ( typeof callback === 'function'){ callback( div ); }}}; appendDiv(function( node ){ node.style.display = 'none'; }); Copy the code
-
Array.prototype.sort
Array.prototype.sort takes a function as an argument that encapsulates the order of the elements in the Array. We just need to care what sort is used. Encapsulate variable parts in function parameters and dynamically pass array.prototype.sort to make it more flexible.
// From small to big
[1.4.3].sort((a, b) = > {
return a - b
})
// From large to small
[1.4.3].sort((a, b) = > {
return b - a
})
Copy the code
Function is output as the return value
- Determine the data type
var isType = function( type ){
return function( obj ){
return Object.prototype.toString.call( obj ) === '[object '+ type +'] '; }};var isString = isType( 'String' );
var isArray = isType( 'Array' );
var isNumber = isType( 'Number' );
console.log( isArray( [ 1.2.3]));// Output: true
Copy the code
- getSingle
The singleton pattern, which will be covered in more detail in the subsequent design Patterns section
var getSingle = function ( fn ) {
var ret;
return function () {
return ret || ( ret = fn.apply( this.arguments)); }; };Copy the code
Here getSingle is a higher-order function that passes the function as an argument and returns another function to see what happens.
var getScript = getSingle(function(){
return document.createElement( 'script' );
});
var script1 = getScript();
var script2 = getScript();
console.log( script1 === script2 ); // true
Copy the code
Higher-order functions implement AOP
AOP is aspect oriented programming, the main role is to extract some code irrelevant to the core business logic, usually including log statistics, security control, exception handling and so on. After these functions are removed, the business logic module is incorporated in the way of “dynamic weaving”. The advantage of this method is that the purity and high cohesion of the business logic can be maintained, and the removed function module can be easily reused.
Function.prototype.before = function(beforeFn) {
var _self = this // Save a reference to the original function
return function() { // Returns a proxy function that contains both the original and the new function
beforeFn.apply(this , arguments) // Execute a new function to fix this
return _self.apply(this , arguments) // Execute the original function}}Function.prototype.after = function(afterFn) {
var _self = this
return function() {
var ret = _self.apply(this , arguments)
afterFn.apply(this , arguments)
return ret
}
}
var foo = function() {
console.log('Function execution')
}
foo = foo.before(function() {
console.log('before')
}).after(function() {
console.log('after')
})
foo()
Copy the code
Results:
Other applications of higher order functions
- The use of a function called currying
var currying = function( fn ){
var args = [];
return function(){
if ( arguments.length === 0) {return fn.apply( this, args );
}else{
[].push.apply( args, arguments );
return arguments.callee; }}};var cost = (function(){
var money = 0;
return function(){
for ( var i = 0, l = arguments.length; i < l; i++ ){
money += arguments[ i ];
}
return money;
}
})();
var cost = currying( cost ); // Convert to a currying function
cost( 100 ); // Not really evaluated
cost( 200 ); // Not really evaluated
cost( 300 ); // Not really evaluated
alert ( cost() ); // Evaluate and print: 600
Copy the code
- uncurrying
Simply put, uncurrying is a method that implements an assignment from another object. For example, we often use array-like objects that borrow array. prototype methods. This is one of the most common uses of call and apply.
(function(){
Array.prototype.push.call( arguments.4 ); // arguments borrow array.prototype.push methods
console.log( arguments ); // Output: [1, 2, 3, 4]}) (1.2.3 );
Copy the code
Uncurrying is used to solve the problem of extracting the process of generalization this. The following is one of the ways that uncurrying can be implemented.
Function.prototype.uncurrying = function() {
var self = this
return function() {
var obj = Array.prototype.shift.call(arguments);
return self.apply(obj , arguments)}}Copy the code
Let’s look at what is the function of it, in a class Array object the arguments to borrow Array. The prototype method, before the Array. The first prototype. Push. The call is converted to a common push function.
var push = Array.prototype.push.uncurrying();
(function(){
push( arguments.4 );
console.log( arguments ); // Output: [1, 2, 3, 4]}) (1.2.3 );
Copy the code
Through uncurrying, Array. Prototype. Push. Call into a generic function, and function of such a push Array. Prototype. Push the same, not just confined to the smart Array operation, and users of method is more concise and intentions clear. Now take a look at what happens when uncurrying is called using push.
Function.prototype.uncurrying = function () {
var self = this; // self is array.prototype.push
return function() {
var obj = Array.prototype.shift.call( arguments );
/ / obj is {
// "length": 1,
/ / "0" : 1
// }
The first element of the // arguments object is truncated, leaving [2]
return self.apply( obj, arguments );
/ / equivalent to the Array. The prototype. Push. Apply (obj, 2)
};
};
var push = Array.prototype.push.uncurrying();
var obj = {
"length": 1."0": 1
};
push( obj, 2 );
console.log( obj ); // Output: {0: 1, 1: 2, length: 2}
Copy the code
- Function throttling