Hello, I’m a mouse
Big brother big sister come first don’t go ah, rest assured, this article is full of dry goods! Do you know all these things?
Don’t walk ah don’t walk ah, even if the meeting, also might as well glance, in case there are hundreds of millions of small details worth learning? (Ow O)/~
preface
Feeling like a string of articles received pitiful viewership, that was one way to remain “one hundred and twenty” readers.
This article summarizes the most important and common Javascript knowledge points and addresses each point:
- Basic introduction
- Principle and implementation steps
- Handwritten implementation
Hope to be able to through the analysis of knowledge points and realization, and then add hundreds of millions of details, sublimation of its understanding, to ask for a praise πππ crazy hint!
“Be sure to let me know if your articles are” remaining “ones
You can refer to the directory to select their own part of the need to read, the content will be updated from time to time, suggested collection! (Do me a favor.)
1. Write new method by hand
The principle of
First of all, the new method mainly implements the following functions:
- Create an empty object (
prototype
From theObject.prototype
) - Distinguish constructor from other parameters
- Set the object prototype
- Call the constructor on the object
- If the constructor returns an object, it returns that object, otherwise it returns a new object
We can use the __proto__ attribute for the part of the above functionality that involves the prototype chain, but the official recommendation is object.getProtoTypeof and Object.setProtoTypeof.
Each function has a prototype object; constructor is the attribute that the prototype object points to.
implementation
let newObject = function() {
let obj = new Object(),
Constructor = Array.prototype.shift.call(argument);
// obj.__proto__ = Constructor.prototype; / / abandoned
Object.setPrototypeOf(obj, Constructor.prototype);
let ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? ret : obj;
}
Copy the code
If you are familiar with ES6, the above code can actually be optimized with the remaining parameters.
-
Extract constructor and other parameters
function (constructor, ...arguments){} Copy the code
-
Merge the newly created object operation
Object. Create can replace the three lines in this article. See the implementation next
Object.create(constructor.prototype); let ret = constructor.apply(obj, arguments); / / equivalent to the let obj = new Object(), Constructor = Array.prototype.shift.call(argument); // obj.__proto__ = Constructor.prototype; / / abandoned Object.setPrototypeOf(obj, Constructor.prototype); let ret = Constructor.apply(obj, arguments); Copy the code
2. Write the object.create method
In the last example, we used an Object.create, so let’s talk about it now.
The main function is to generate a new object from an existing object as a prototype object, and this new object’s __proto__ attribute points to the prototype object.
If null is used as an argument, since NULL is the end of the stereotype chain, it will return an empty object (no stereotype chain).
The principle of
- First we need a container to incubate new objects,
let F = function(){}
. - Then we configure the container to generate the object we want,
F.prototype = argsObject
. - Generate the objects we need,
return new F()
.
implementation
function ObjectCreate(proto) {
let F = function(){};
F.prototype = obj;
return new F();
}
Copy the code
3. Inheritance of handwriting classes (prototype chain and parasitic combination inheritance)
Object. Create ();
Prototype chains and inheritance are something that every Web front-end developer can’t escape, and now we’re going to implement inheritance using the Object. Create method we just described.
The principle of
There are many ways you can implement inheritance in javascript, but the best use of resources is still parasitic combinatorial inheritance.
Javascript inheritance is realized through the prototype chain, each object (including functions) has a prototype object, the subclass can read the prototype object of the parent class and the ancestor class, so as to access the methods of the parent class and the ancestor class, to achieve the purpose of inheritance.
Specific see the little Red book, here will not elaborate ~
The steps are as follows:
- Building subclass instance properties from the superclass constructor: The subclass borrows the superclass constructor, creates superclass instance properties on the instance, and adds new subclass properties.
- Inherit superclass attributes: Create a stereotype object based on a superclass stereotype (to prevent creating a superclass instance and creating redundant superclass instance attributes), bind the subclass constructor to the stereotype.
- Enhance the newly created stereotype object: Add subclass stereotype properties and methods:.
This is how class extends extends related keywords work in ES6.
implementation
function Super(name) {
this.name = name;
this.colors = ["red"."blue"];
}
Super.prototype.sayName = function() { console.log(this.name); };
function Sub(name, age) {
Super.call(this, name);
this.age = age;
}
(function inheritPrototype(Sub, Sup) {
// Create a subclass prototype object based on the superclass prototype object, avoiding the creation of superclass instances and redundant attributes
let prototype = Object.create(Sup.prototype);
// Bind the relationship between the new prototype and the subclass constructor
prototype.constructor = Sub;
Sub.prototype = prototype;
})(Sub, Super);
// After binding the prototype, bind the new method to the new prototype object
Sub.prototype.sayAge = function() { console.log(this.age); }
Copy the code
4. Handwritten arrow function
The principle of
Arrow functions are one of the most important new features in ES6. Arrow functions have no arguments, prototype, caller, or even their own this. Instead, they bind the most recent this in the lexical execution field.
Therefore, arrow functions cannot be used as constructors, and cannot be used with new. .
The arrow function still has the prototype chain of the function object, but it cannot bind this when calling call and apply and is ignored.
The arrow function is essentially an enhancement of our earlier ES5 binding scope:
function () {
let that = this;
setTimeout(function() {
return that;
}, 1000);
}
Copy the code
Babel escapes es5 implementation
The implementation of the arrow function is essentially the usual way we pass the this pointer, creating a closure to hold the reference.
function A() {
return () = >{console.log(this)}
}
funcion funA() {
return (function(){console.log(this); }).bind(this);
}
Copy the code
5. Write call and apply functions by hand
Function.prototype.call and function.prototype. apply
In ES6, these two methods are included in Reflext as reflect. call and reflect. apply, which work the same way.
Call and apply are both used as secondments (change the this pointer and call it immediately):
Array.prototype.join.call(["1", "2"], " ")
Copy the code
The above representation says that.split(” “) is called when this points to (that is, the array [“1”, “2”])
func.apply(thisArg, [argsArray])
Copy the code
Parameter 1: The function takes a this, binding the function execution field;
The biggest difference between Call and Apply is that Call accepts a list of parameters, while Apply accepts an array of parameters.
The principle of
First, call and apply are both methods on Function prototypes that we need to define in function.prototype
Secondly, the difference between the two is the difference of extraction parameters, which is mainly implemented by call.
- Check whether it is a function or not
- Separate the parameters in the parameters
- Identify secondment targets
- Create this method at the secondment target (link scope)
- Execute and save the result
- Delete the created method
- Returns the result
implementation
Function.prototype.call = function (thisArg) {
// Check whether the current party is a function (this is Product, check whether Product is a function)
if (typeof this! = ='function') {
throw new TypeError('The current call method is not a function! ');
}
// Get argument list if apply [1]
const args = [...arguments].slice(1);
// If thisArg is null or undefined
thisArg = thisArg || window;
// Create a unique value
const fn = Symbol('fn');
// Add this method to the target
thisArg[fn] = this;
// Execute the saved function, in which case the scope is executed within the scope of the target object
constresult = thisArg[fn](... args);// Delete the value of the newly added attribute
delete thisArg[fn];
// Returns the execution result
return result;
}
Copy the code
Const args = arguments.slice(1) if apply;
6. Write bind
Bind is based on apply and call, and I don’t want it to be called directly, I want it to be called when I need it.
The principle of
Bind this to and return a function that calls Call/apply when executed.
- Save the original function reference, i.e
this
- Prevent error reporting and extraction of parameters
- Return a function
- Returns a function that takes a quadratic argument
new
The operation priority is greater thanbind
, in view of thenew
Call extra judgment
implementation
1. Basic implementation ideas
Function.prototype.bind1 = function(context, ... args){
var self = this;
return function(){
returnself.apply( context, args); }};Copy the code
2. Add error reporting and parameter extraction
Arguments are extracted twice: the arguments passed in when bind is called, and the arguments passed in when the return function is called
Function.prototype.bind2 = function (context) {
if (typeof this! = ='function') {
throw new Error('Function.prototype.bind - what is trying to be bound is not callable')}let f = this;
let agrs = [...arguments].slice(1) // Take the argument for the first time
let bindFun = function () {
let agrs2 = [...arguments] // Take the second parameter
let Allagrs = agrs.concat(agrs2) // Merge two groups of parameters (note the order)
f.apply(context, Allagrs) // Use apply to change this to s
}
return bindFun
}
Copy the code
3. Call judgment for new
If called, this points to the newly created object
Function.prototype.bind3 = function (context) {
if (typeof this! = ='function') {
throw new Error('Function.prototype.bind - what is trying to be bound is not callable');
};
let f = this;
let agrs = [...arguments].slice(1); // Take the first argument (the argument passed when calling bind2)
let bindFun = function () {
let agrs2 = [...arguments];
let Allagrs = agrs.concat(agrs2);
// If the function is called, this points to the newly created object
f.apply(this instanceof f ? this : context, Allagrs);
}
bindFun.prototype = f.prototype // Prototype chain inheritance
return bindFun
}
Copy the code
7. Write a more comprehensive Typeof
Typeof is a common type determination operator in javascript, but it has drawbacks.
We can use typeof to determine number, String, object, Boolean, function, undefined, symbol, bigint.
This is based on the fact that when javascript stores variables at the bottom level, it stores their type information in the lowest 1-3 bits of the variable’s machine code, roughly as follows:
- 000: object
- 010: floating point number
- 100: string
- 110: a Boolean value
- 1: the integer
- -2^30: undefined
Where all similar objects (Array, etc.) and NULL (all zeros, ending with 000 as an object) return object
For this reason, we generally use toString to determine objects.
The principle of
Based on the Object. The prototype. ToString secondment to achieve:
According to the type implementation given by MDN, the judgment steps are as follows:
-
Check whether the value is null or undefined
-
To prevent over-interpretation, elements like HTMLDivElement are retrieved and then filtered using regular
deepType.match(/^(array|bigint|date|error|function|generator|regexp|symbol)$/)
-
Normal types are returned directly using Typeof
implementation
There are two inconsistencies in the implementation given by MDN:
- Here,
fullClass
Personally, it feels a little redundant - Why judge one last time
typeof obj === 'function'
Before,match
I think we got a match.
The likelihood knowledge is shallow, if have big guy to see, hope can give a solution.
MDN version
function type(obj, fullClass) {
if (fullClass) {
return (obj === null)?'[object Null]' : Object.prototype.toString.call(obj);
}
if (obj == null) { return (obj + ' ').toLowerCase(); }
var deepType = Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
if (deepType === 'generatorfunction') { return 'function' }
return deepType.match(/^(array|bigint|date|error|function|generator|regexp|symbol)$/)? deepType : (typeof obj === 'object' || typeof obj === 'function')?'object' : typeof obj;
}
Copy the code
8. Instanceof handwriting
This is a way to determine if an instance belongs to a type.
Too often I will not say nonsense, directly on the principle.
The principle of
Judgment basis:
-
Symbol.hasinstance can override the instanceof behavior (by default, referring to the prototype chain), defined in the constructor:
class MyArray { static [Symbol.hasInstance](instance) { return Array.isArray(instance); }}console.log([] instanceof MyArray); // true Copy the code
We can even go a little overboard:
-
The Array implementation of different Windows may be different, you can use array. isArray to determine
-
Prototype chain
- By instance
__proto__
Or useObject.getPrototypeOf
Get the prototype object - Compare types by stereotype object, or stereotype object
constructor
Comparison of type - If not, continue to query the prototype chain, i.e
__proto__.__proto__
Until the end of the prototype chainnull
implementation
function myInstanceof(instance, object) {
if (object[Symbol.hasInstance]) return object[Symbol.hasInstance](instance);
else {
if (instance === null|| instance ! = ='object') return false;
if (object === Array) return Array.isArray(instance);
let proto = Object.getPrototypeOf(instance);
while(proto ! = =null) {
if(proto == object.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false; }}Copy the code
9. Handwritten deep copy
A shallow copy is a copy of a reference, and a deep copy is a copy of a value:
- Shallow copy
let obj = {a:1};
let obj2 = obj;
obj2.a = 2;
console.log(obj); // {a:2}
Copy the code
In fact, obj and obj2 point to the same memory region identifier, so the changes are the same.
Object.assign(A, B) adds all references from B to A;
- Deep copy
All deep copy does is copy the values and break the references.
let obj = {a:1};
let obj2 = {};
obj2.a = obj.a;
obj2.a = 2;
console.log(obj); // {a:1}
Copy the code
The principle of
There are two ways to do this. One is to convert the array to a string and then rebuild the array from the string.
Another way is to loop recursively, copying the structure of the object.
For cyclic recursion, we need to prevent:
- A circular reference
- Multiple copies of the same reference object
The steps are as follows:
- Record objects iterated through arrays (before copy, after copy)
- Check whether it is an object. If it is an object, perform deep copy
implementation
- JSON. Parse and JSON. Stringify
- Properties with values of function, re, and Symbol are not copied
- This can lead to circular references
- The same reference is copied multiple times, that is
a.a = a.b = {}
But copy the resultsa.a
εa.b
for{}
But not equal to
- Cyclic recursive copy
function deepClone(obj) {
let copyed = new Map(a);function _deepClone (obj) {
if (obj && typeof obj === "object") {
let objClone = Array.isArray(obj) ? [] : {};
if(! copyed.has(obj)) copyed.set(obj, objClone);for ( key in obj ) {
if( obj.hasOwnProperty(key) ) {
if ( obj[key] && typeof obj[key] === "object" ) {
if (copyed.has(obj[key])) objClone[key] = copyed.get(obj[key]);
else objClone[key] = _deepClone(obj[key]);
}else{ objClone[key] = obj[key]; }}}return objClone;
} else return obj;
}
return _deepClone(obj);
}
Copy the code
10. Handwritten array deduplication
The principle of
Based on the Object. The prototype. ToString. Call.
We need to judge:
-
Symbol(“”= symbol (1)), but does not participate in the operation, so “”= symbol (1) implicit conversion error
To evaluate the same value Symbol, call Symbol(1).tostring ()
-
object
Content is not judged heavy, judge whether there has been the same reference
Content reweighting is only available for objects json.stringify
-
NaN is judged separately and uniquely by number. isNaN and hasNaN tokens
-
Other numerical
Including null and undefined
Use temp to store Typeof + String(obj) as key values, judge the weight
implementation
Array.prototype.uniq = function () {
if (!this.length || this.length == 0) return this;
var res = [], key, hasNaN = false, temp = {};
for (var i = 0 ; i < this.length; i++) {
if (typeof this[i] === 'symbol') {
res.push(this[i]);
} else if (Object.prototype.toString.call(this[i]) === '[object Object]') {
key = typeof(this[i]) + JSON.stringify(this[i]);
if(! temp[key]) { res.push(this[i]);
temp[key] = true; }}else if (Number.isNaN(this[i])) { // If the current traversal element is NaN
if(! hasNaN) { res.push(NaN);
hasNaN = true; }}else {
key = typeof(this[i]) + this[i];
if(! temp[key]) { res.push(this[i]);
temp[key] = true; }}}return res;
}
Copy the code
11. Handwriting array flat
Flattening an array is expanding a nested array:
The principle of
We need to pass in a parameter n to control the number of layers to unfold,
Here I just use the method is recursive, of course I believe you must have a better ~
- Use n to control the number of recursive levels, native
flat
The default is 1 - Determines if the maximum level of recursion has been reached, and if so, returns the element directly
- Determine whether the current item is an array or other data
- Use recursive
reduce
δΈconcat
Concatenate and return the result
implementation
function flatArray(arr, n = 1) {
function deep(item, i) {
if (i === n) {
return item;
}
else {
if (Array.isArray(item)) {
return item.reduce((prev, cur) = > {
return prev.concat(deep(cur, i+1)); } []); }else returnitem; }}return deep(arr, 0);
}
Copy the code
11. Handwriting anti-shock function debounce
The so-called anti-shake function, as the name suggests, prevents wobbly legs from shaking!
This function is mainly used to prevent events from being triggered frequently. No matter how many times events are triggered, the task can be executed only after the interval of the last time.
Commonly used for onMousemove, onresize, search input box and other high-frequency trigger events:
The principle of
- Set a timer
- Trigger time for the first time, activate a timer
- The timer is reset when events are triggered for as long as the timer is valid.
- The last trigger and interval of a certain time, the timer ends, trigger the event;
- Clear the timer.
This should require the use of closures to store timers.
implementation
function debounce(fn, delay) {
let timer;
return function (. args) {
var _this = this; Save the scope of the created functionif (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(_this, args);
clearTimeout(timer);
}, delay);
};
}
Copy the code
12. Handwritten throttle function throttle
The throttling function is similar to the anti – shake function, but different.
The throttling function is a bit like a game character’s skill and can only be used once at a given time.
To put it another way, cool the CD.
As the name suggests, throttling is often used to control traffic by controlling how often network requests are sent, such as login authentication and searches.
The principle of
There are two implementations, both time-based in nature.
- Timer β² timing
- Timestamp timing
Similarly, closures are needed to determine if the cooling is complete and can be executed.
After each execution event, set a timer. During the timing period, the execution time cannot be set.
implementation
- The timer
function throttle(func, delay = 100) {
let canRun = true;
return function (. args) {
let _this = this;
if(! canRun) {return;
}
func.apply(_this, args);
canRun = false;
setTimeout(function () {
canRun = true;
}, delay)
}
}
Copy the code
- The time stamp
function throttle(func, delay = 100) {
let previous;
return function (. args) {
let _this = this;
let now = +new Date(a)if (now - previous <= delay ) {
return;
}
func.apply(_this, args);
previous = +new Date();
}
}
Copy the code
13. Write the sleep function
What is the sleep function? The short answer is __ Varudo
That is stop time!
We need to have our tasks executed at certain intervals, or stopped for n seconds.
To be clear, JavaScript is single-threaded, but setTimeout is implemented based on a browser module that starts a timer, each of which has no effect on the other.
So the above code will eventually pop up together.
A compatible solution is to use an increment timer:
setTimeout(func(), 100); setTimeout(func(), 200);
Or the callback function call:
setTimeout(()=>{func(); setTimeout(func(), 100); }, 100);
But that’s not really elegant,
It’s time to get back to the sun and call async/await (generator and promise syntax candy).
The principle of
We need to implement a sleep function for real time and return a promise
The external async execution function waits for the sleep with await
In order to achieve the purpose of time stop.
implementation
function sleep(delay) {
return new Promise((resolve) = >{
setTimeout(() = >resolve(), delay);
});
}
Copy the code
Of course, it is important to note that the sleep is not on time, and it can be delayed by looking at the event loop queue.
conclusion
Thank you so much for watching, your likes are the best support for me!
Keep learning! I’ll be back ~ what do you think in the comments section ~
I will update this article from time to time in the future, if you feel good, remember to bookmark, something is ok, review the old to know something new ~
Refer to the article
- MDN
- Introduction to the Object. The prototype. ToString. Call () method
- JS deep copy summary
- Thoroughly understand function tremble and function throttling