Handwritten path navigation

  • Implement a new operator
  • Implement a json.stringify
  • Implement a json.parse
  • Implement a call or apply
  • Implement a function.bind
  • Implementing an inheritance
  • Implement a JS function currying
  • Write a Promise by hand.
  • Debouncing and Throttling
  • Write a deep copy of JS
  • Implement an instanceOf

1. Implement onenewThe operator

Source: “javascript you Don’t Know” English version

The new operator does these things:

  • It creates an entirely new object.
  • It will be executed[[Prototype]](i.e.__proto__Links).
  • It makesthisPoints to the newly created object.
  • throughnewEach object created will eventually be[[Prototype]]Link to this functionprototypeOn the object.
  • If the function does not return an object typeObject(including,Functoin, Array, Date, RegExg, Error), thennewA function call in an expression returns the object reference.
function New(func) {
    var res = {};
    if(func.prototype ! == null) { res.__proto__ = func.prototype; } var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));if ((typeof ret === "object" || typeof ret === "function") && ret ! == null) {return ret;
    }
    return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
Copy the code

2. Implement oneJSON.stringify

Json.stringify (value[, replacer [, space]]) :

  • Boolean | Number| StringThe type is automatically converted to its original value.
  • undefined, any function andsymbolIs ignored (when present in a property value of a non-array object), or is converted tonull(when appearing in an array).
  • Properties that cannot be enumerated are ignored
  • Properties are also ignored if the value of an object’s property is referred back to the object itself in some indirect way, a circular reference.
function jsonStringify(obj) {
    let type = typeof obj;
    if (type! = ="object") {
        if (/string|undefined|function/.test(type)) {
            obj = '"' + obj + '"';
        }
        return String(obj);
    } else {
        let json = []
        let arr = Array.isArray(obj)
        for (let k in obj) {
            let v = obj[k];
            let type = typeof v;
            if (/string|undefined|function/.test(type)) {
                v = '"' + v + '"';
            } else if (type= = ="object") {
                v = jsonStringify(v);
            }
            json.push((arr ? "" : '"' + k + '" :) + String(v));
        }
        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
    }
}
jsonStringify({x : 5}) // "{"x": 5}"
jsonStringify([1, "false".false/ /])"[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"
Copy the code

3. Implement oneJSON.parse

JSON.parse(text[, reviver])

Used to parse JSON strings and construct JavaScript values or objects described by the string. Provides an optional reviver function to transform the obtained object before returning it.

3.1 First: Call eval directly

function jsonParse(opt) {
    return eval('(' + opt + ') ');
}
jsonParse(jsonStringify({x : 5}))
// Object { x: 5}
jsonParse(jsonStringify([1, "false".false[1])) / /,"false", falsr]
jsonParse(jsonStringify({b: undefined}))
// Object { b: "undefined"}
Copy the code

Avoid using eval unnecessarily. Eval () is a dangerous function that executes code that has the rights of the implementer. If the string code you run with eval() is manipulated by malicious parties (people with bad intentions), you may end up running malicious code on the user’s computer under the authority of your web page/extension.

It executes JS code and has XSS vulnerabilities.

If you just want to remember this method, you have to check the json parameter.

var rx_one = /^[\],:{}\s]*$/; var rx_two = /\\(? : ["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"\\\n\r]*"|true|false|null|-? \d+(? :\.\d*)? (? :[eE][+\-]? \d+)? /g; var rx_four = /(? (: : ^ | |,)? :\s*\[)+/g;if (
    rx_one.test(
        json
            .replace(rx_two, "@")
            .replace(rx_three, "]")
            .replace(rx_four, "")
    )
) {
    var obj = eval("(" +json + ")");
}
Copy the code

3.2 The second type: Function

Source magical eval() and new Function()

Core: Function has the same string parameter properties as eval.

var func = new Function(arg1, arg2, ... , functionBody);

In a real-world application of converting JSON, this is all you need to do.

var jsonStr = '{ "age": 20, "name": "jack" }'
var json = (new Function('return ' + jsonStr))();
Copy the code

Both eval and Function can be used to dynamically compile JS code, but they are not recommended in actual programming.

This is interview oriented programming. These two are enough. As for the third and fourth, which involve tedious recursion and state machine-related principles, see:

Three Ways to implement Json.Parse

4. Implement onecallorapply

Call and Apply’s mock implementation #11

Call syntax:

fun.call(thisArg, arg1, arg2, …) , calls a function with a specified value of this and a list of arguments, respectively.

Apply the grammar:

Func.apply (thisArg, [argsArray]), a call to a function, and the arguments provided as an array (or array-like object).

4.1 Function.callRealization by routine

Call the core:

  • Sets a function as a property of an object
  • Execute & to remove the function
  • The specifiedthisTo the function and pass in the given arguments to execute the function
  • If no argument is passed, the default point is window

Why is it routine implementation? In a real interview, your interviewer likes you to think slowly, so you can start by writing a simple version:

4.4.1 simple version

var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
}
foo.bar() // 1
Copy the code

4.1.2 perfect version

When the interviewer has a further question, or you can pretend to think about it at this point. Then write the following version:

Function.prototype.call2 = function(content = window) {
    content.fn = this;
    let args = [...arguments].slice(1);
    letresult = content.fn(... args); delete content.fn;return result;
}
let foo = {
    value: 1
}
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}
bar.call2(foo, 'black'.'18') // black 18 1
Copy the code

4.2 Function.applySimulation implementation of

The apply() implementation is similar to call(), except that the arguments are different. Just post the code:

Function.prototype.apply2 = function(context = window) {
    context.fn = this
    letresult; // Determine if there is a second argumentif(arguments[1]) { result = context.fn(... arguments[1]) }else {
        result = context.fn()
    }
    delete context.fn
    return result
}
Copy the code

5. Implement oneFunction.bind()

The bind () method:

Will create a new function. When the new function is called, the first argument to bind() will be as this when it runs, and the following sequence of arguments will be passed as its arguments before the arguments passed. (from MDN)

In addition, the bind implementation needs to consider the impact of instantiation on the prototype chain.

Function.prototype.bind2 = function(content) {
    if(typeof this ! ="function") {
        throw Error("not a function"} // If the parameter type is not asked, write from herelet fn = this;
    let args = [...arguments].slice(1);
    
    let resFn = function() {
        returnfn.apply(this instanceof resFn ? this : content,args.concat(... arguments) ) }function tmp() {}
    tmp.prototype = this.prototype;
    resFn.prototype = new tmp();
    
    return resFn;
}
Copy the code

6. Implement an inheritance

Parasitic combinatorial inheritance

Writing this is generally only recommended, because other methods of inheritance involve calling the constructor of the parent class twice in one instance or have other disadvantages.

The core implementation is to replace the Parent constructor with an F null constructor.

function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log('parent name:', this.name);
}
function Child(name, parentName) {
    Parent.call(this, parentName);  
    this.name = name;    
}
function create(proto) {
    function F(){}
    F.prototype = proto;
    return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
    console.log('child name:', this.name);
}
Child.prototype.constructor = Child;

var parent = new Parent('father');
parent.sayName();    // parent name: father

var child = new Child('son'.'father');
Copy the code

7. Implement a JS function currying

What is currying?

In computer science, Currying is the technique of transforming a function that takes multiple arguments into one that takes a single argument (the first argument of the original function) and returns a new function that takes the rest of the arguments and returns a result.

The main functions and characteristics of function currying are parameter multiplexing, early return and delayed execution.

General version 7.1

function curry(fn, args) {
    var length = fn.length;
    var args = args || [];
    return function(){
        newArgs = args.concat(Array.prototype.slice.call(arguments));
        if (newArgs.length < length) {
            return curry.call(this,fn,newArgs);
        }else{
            returnfn.apply(this,newArgs); }}}function multiFn(a, b, c) {
    returna * b * c; } var multi = curry(multiFn); multi(2)(3)(4); Multi (2 and 4); Multi (2) (3, 4); Multi (2, 3) (4);Copy the code

7.2 ES6SAO writing

const curry = (fn, arr = []) => (... args) => ( arg => arg.length === fn.length ? fn(... arg) : curry(fn, arg) )([...arr, ...args])letCurryTest = curry ((a, b, c, d) = > a + b + c + d) curryTest (1, 2, 3) (4) / / return 10 curryTest (1, 2) and (4) and (3) / / return 10 curryTest (1, 2) (3, 4) / / return 10Copy the code

8. Write it by handPromise(Intermediate and advanced compulsory examination)

Let’s go over the Promise/A+ specification:

  • Three statespending| fulfilled(resolved) | rejected
  • When inpendingState, you can transition tofulfilled(resolved)orrejectedstate
  • When infulfilled(resolved)The state orrejectedState, it’s immutable.
  1. There has to be athenThe asynchronous execution method,thenTakes two arguments and must return a promise:
// this will be fulfilled fulfilled. // this will be fulfilled. // this will be fulfilled.Copy the code

8.1 PromiseFlow chart analysis based on

Promise

var promise = new Promise((resolve,reject) => {
    if{resolve(value)}else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})
Copy the code

8.2 Interview is sufficient

Source: Implement A Promise that perfectly conforms to the Promise/A+ specification

function myPromise(constructor){
    let self=this;
    self.status="pending"Self. value=undefined; self.value=undefined; self.value=undefined; Self. reason=undefined; self.reason=undefined; self.reason=undefined; // Define the state when the state is rejectedfunctionResolve (value){// two === ="pending", ensuring that the change in state is irreversibleif(self.status==="pending"){
          self.value=value;
          self.status="resolved"; }}functionReject (reason){// two === ="pending", ensuring that the change in state is irreversibleif(self.status==="pending"){
          self.reason=reason;
          self.status="rejected"; }} // catch a constructor exception try{constructor(constructor,reject); }catch(e){ reject(e); }}Copy the code

Also, we need to define the chained call THEN method on the prototype of myPromise:

myPromise.prototype.then=function(onFullfilled,onRejected){
   let self=this;
   switch(self.status){
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:       
   }
}
Copy the code

Test it out:

var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)}) // Output 1Copy the code

8.3 DMC version

Just post it, this version is easy to understand

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function Promise(excutor) {
    letthat = this; // Cache the current promise instance object that. Status = PENDING; // The initial state that. Value = undefined; That. Reason = undefined; // this is very depressing. / / rejected state refused to the reason that onFulfilledCallbacks = []; This is fulfilled. OnRejectedCallbacks = []; // Store the onRejected function corresponding to the rejected statefunctionResolve (value) {// value Indicates the final value to be received in a successful stateif(value instanceof Promise) {
            returnvalue.then(resolve, reject); } // In practice, make sure that the onFulfilled and onRejected methods are executed asynchronouslythenThe new execution stack after the round of the event loop in which the method is called.setTimeout(() => {// Call the resolve callback corresponding to onFulfilledif(that. Status === PENDING) {// this will be fulfilled only if the PENDING state => resolve reject. that.value = value; that.onFulfilledCallbacks.forEach(cb => cb(that.value)); }}); }functionReject (reason) {// reason Indicates the reject received in the failed statesetTimeout(() => {// Call the reject callback corresponding to the onRejected functionif(that. Status === PENDING) {// Only the PENDING status => rejected (resolve reject) that. that.reason = reason; that.onRejectedCallbacks.forEach(cb => cb(that.reason)); }}); } // New Promise((resolve, reject) => {// throw new Error();'error in excutor')
    // })
    try {
        excutor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
    const that = this;
    let newPromise;
    // 处理参数默认值 保证参数后续能够继续执行
    onFulfilled =
        typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected =
        typeof onRejected === "function" ? onRejected : reason => {
            throw reason;
        };
    ifThis is very depressing. (this is very depressing.) {// this is very depressingreturn newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                try{
                    letx = onFulfilled(that.value); resolvePromise(newPromise, x, resolve, reject); } catch(e) {reject(e); // Catch the exception thrown earlier in onFulfilledthen(onFulfilled, onRejected); }}); })}if(that. Status === REJECTED) {//return newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    letx = onRejected(that.reason); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); }}); }); }ifThis is a pity pity. (that. Status === PENDING) {// this is a pity pityreturn newPromise = new Promise((resolve, reject) => {
            that.onFulfilledCallbacks.push((value) => {
                try {
                    letx = onFulfilled(value); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); }}); that.onRejectedCallbacks.push((reason) => { try {letx = onRejected(reason); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); }}); }); }};Copy the code

9. Hand-written stabilization (Debouncing) and throttling (Throttling)

The Scroll event itself will trigger re-rendering of the page, and the Handler of the Scroll event will be triggered frequently. Therefore, the handler of the event should not have complex operations, such as DOM operations, in the event processing. There are two common solutions to this high frequency event triggering problem (such as page scroll, screen resize, listening for user input, etc.), which are damping and throttling.

9.1 image stabilization (Debouncing) implementation

Typical example: Limiting mouse combo firing.

A better explanation is:

When an event occurs, the event handler waits for a certain threshold of time. If no event occurs after this period of time, the event handler processes the last event. Let’s say we are 0.01 seconds away from the specified time and another event comes along, then the previous wait is invalid and we need to wait for the specified time again.

// Anti-jitter functionfunction debounce(fn,wait=50,immediate) {
    let timer;
    return function() {
        if(immediate) {
            fn.apply(this,arguments)
        }
        if(timer) clearTimeout(timer)
        timer = setTimeout(()=> {
            fn.apply(this,arguments)
        },wait)}}Copy the code

Combined example: rolling anti – shaking

// We want to bind the handler to the scroll eventfunction realFunc(){
    console.log("Success"); } // Use the anti-jitter window.addeventListener ()'scroll',debounce(realFunc,500)); Window.addeventlistener (window.addeventListener)'scroll',realFunc);
Copy the code

9.2 the throttling (Throttling) implementation

The idea is that events travel in a pipe, and by adding this throttle, the velocity of events slows down. In fact, that’s what this function does. It limits the frequency of a function call to a certain threshold, like 1s, so that the function is not called twice

Simple throttling function:

function throttle(fn, wait) {
	let prev = new Date();
	return function() { 
	    const args = arguments;
		const now = new Date();
		if (now - prev > wait) { fn.apply(this, args); prev = new Date(); }}Copy the code

9.3 Combining practice

The third parameter is used to switch modes.

const throttle = function(fn, delay, isDebounce) {
  let timer
  let lastCall = 0
  return function(... args) {if (isDebounce) {
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => { fn(... args) }, delay) }else {
      const now = new Date().getTime()
      if (now - lastCall < delay) returnlastCall = now fn(... args) } } }Copy the code

Write a deep copy of JS by hand

One of the most famous implementations of the beggar version is also mentioned in JavaScript you Don’t Know:

10.1 the beggar version

 var newObj = JSON.parse( JSON.stringify( someObj ) );
Copy the code

10.2 Interview is sufficient

functionDeepCopy (obj){// determine if it is a simple data type,if(typeof obj == "object"Var result = constructor == Array? [] : {};for(let i in obj){
            result[i] = typeof obj[i] == "object"? deepCopy(obj[i]) : obj[i]; }}elseVar result = obj; }return result;
}
Copy the code

There’s a discussion about deep copying every day, so here are two, after all, I…

11. Implement oneinstanceOf

function instanceOf(left,right) {

    let proto = left.__proto__;
    let prototype = right.prototype
    while(true) {
        if(proto === null) return false
        if(proto === prototype) return trueproto = proto.__proto__; }}Copy the code

❤️ see three things

If you found this post inspiring, I’d like to invite you to do three small favors for me:

  1. Like, let more people can also see this content (collection does not like, is playing rogue – -)
  2. Pay attention to the public number “front-end dispeller”, irregularly share original knowledge.
  3. Check out other articles as well
  • Design Patterns you Inadvertently Use (part 1) – Creation patterns
  • “King of data visualization library” D3.js fast start to Vue application
  • “True ® Path to Full Stack” a back-end guide to Web front-end development
  • “Vue Practice” 5 minutes for a Vue CLI plug-in
  • “Vue practices” arm your front-end projects
  • “Advanced front end interview” JavaScript handwriting unbeatable secrets
  • “Learn from source code” answers to Vue questions that interviewers don’t know
  • “Learn from the source code” Vue source code in JS SAO operation
  • “Vue Practice” project to upgrade vue-CLI3 correct posture
  • Why do you never understand JavaScript scope chains?

You can also go to my GitHub blog and get the source files for all the posts:

Front end exit guide: github.com/roger-hiro/…