Eine vollständige Liste häufig gestellter JavaScript-Fragen für Front-End-Interviews

Eine vollständige Liste häufig gestellter JavaScript-Fragen für Front-End-Interviews

Bei Front-End-Interviews ist das manuelle Zerlegen des Codes offensichtlich unvermeidlich und macht einen großen Teil davon aus.
Generell gilt: Wenn der Code gut geschrieben ist, besteht eine hohe Wahrscheinlichkeit, das Vorstellungsgespräch zu bestehen, auch wenn die theoretischen Kenntnisse nicht klar genug beantwortet werden. Tatsächlich stellt viel Handschrift oft Ihr Verständnis relevanter Theorien auf die Probe.

Programmierfragen werden hauptsächlich in folgende Typen unterteilt:

  • Fragen zum Algorithmus
  • Fragen zu JS-Prinzipien und Ajax-Anfragen
  • Frage zum Geschäftsszenario: Implementieren einer Komponente mit einer bestimmten Funktion
  • Andere (fortgeschritten, Prüfung umfassender Computerkenntnisse, relativ weniger getestet): Implementieren Sie das Abonnement-Publisher-Muster; verwenden Sie objektorientierte Programmierung, prozedurale Programmierung, funktionale Programmierung, um den Elefanten in den Kühlschrank zu legen usw.

Den größten Anteil machen dabei die ersten beiden Typen aus.
Bei Algorithmusfragen empfiehlt es sich, sich anzugewöhnen, jeden Tag eine leetcode zu üben, wobei der Schwerpunkt auf Datenstrukturen (Stapel, verknüpfte Liste, Warteschlange, Baum), dynamischer Programmierung, DFS , BFS liegt.

Dieser Artikel befasst sich hauptsächlich mit verschiedenen fokussierten Handschriften des zweiten Typs.

Empfohlene Prioritäten:

  • instanceof (um Ihr Verständnis der Prototypenkette zu testen)
  • new (Verständnis für den Prozess der Objektinstanzerstellung)
  • call&apply&bind (Verständnis, worauf dies hinweist)
  • Handschriftliches promise (Verständnis von Asynchronität)
  • Handgeschriebenes natives ajax (Verständnis der Ajax-Prinzipien und HTTP-Anforderungsmethoden, mit Schwerpunkt auf der Implementierung von Get- und Post-Anforderungen)
  • Ereignisabonnement und -veröffentlichung (Hochfrequenz-Testpunkt)
  • Sonstiges: Die Implementierung von Array- und String-APIs ist relativ einfach. Solange Sie die Verwendung gängiger Methoden von Arrays und Zeichenfolgen verstehen, können Sie sofort eine grobe Gliederung schreiben. (PS: Ich denke, die reduce Methode von Arrays ist schwieriger. Wenn Sie die Zeit haben, können Sie sie separat lesen. Auch wenn Sie im Vorstellungsgespräch nicht aufgefordert werden, reduce zu implementieren, ist es ein Pluspunkt, sie bei der Beantwortung anderer Fragen zu verwenden.)

1. Handschriftliche Instanz von

Instanceof-Funktion:

Bestimmt, ob eine Instanz eine Instanz ihres übergeordneten oder Vorgängertyps ist.

Während des Suchvorgangs durchläuft instanceof die Prototypenkette der Variablen auf der linken Seite, bis es den Prototyp der Variablen auf der rechten Seite findet. Wenn die Suche fehlschlägt, wird false zurückgegeben.

 let myInstanceof = (Ziel,Ursprung) => {
     während(Ziel) {
         wenn (ziel.__proto__===origin.prototype) {
            returniere wahr
         }
         Ziel = Ziel.__proto__
     }
     return false
 }
 sei a = [1,2,3]
 console.log(meineInstanzvon(a,Array)); // wahr
 console.log(meineInstanzvon(a,Objekt)); // wahr


2. Implementieren Sie die Map-Methode des Arrays

map() -Methode eines Arrays gibt ein neues Array zurück, in dem jedes Element dem Rückgabewert entspricht, der entsteht, wenn die bereitgestellte Funktion einmal für das Element an der entsprechenden Position im ursprünglichen Array aufgerufen wird.

Verwendung:

Konstante a = [1, 2, 3, 4];
const b = array1.map(x => x * 2);
console.log(b); // Array [2, 4, 6, 8]


Schauen wir uns vor der Implementierung die Parameter der Map-Methode an.

map Methode hat zwei Parameter, einer ist die Methode fn, die die Array-Elemente bedient, und der andere ist dieser Zeiger (optional). Wenn Sie fn verwenden, können Sie drei Parameter erhalten. Denken Sie daran, sie bei der Implementierung nicht auszulassen, damit sie als vollständige Implementierung betrachtet werden kann.

Native Implementierung:

    // Implementiere Array.prototype.myMap = function(fn, thisValue) {
            lass res = []
            dieserWert = dieserWert||[]
            lass arr = dies
            für(lass i=0; i<arr.length; i++) {
                res.push(fn.call(thisValue, arr[i],i,arr)) // Die Parameter sind dieser Zeiger, aktuelles Array-Element, aktueller Index, aktuelles Array}
            Rückgabewert
        }
        // Verwenden von const a = [1,2,3];
        const b = a.meineMap((a,index)=> {
                gib a+1 zurück; 
            }
        )
        console.log(b) // Gibt [2, 3, 4] aus


3. Reduce implementiert die Map-Methode des Arrays

Verwenden Sie die integrierte reduce Methode des Arrays, um die Map-Methode zu implementieren und das Verständnis reduce Prinzips zu untersuchen

Array.prototype.myMap = Funktion(fn,dieserWert){
     var res = [];
     dieserWert = dieserWert||[];
     dies.reduzieren(Funktion(vor,aktuell,index,arr){
         Führt eine Antwort auf die Frage aus, ob der Wert korrekt ist.
     },[]);
     Rückgabewert;
}
​
var arr = [2,3,1,5];
arr.myMap(Funktion(Element,Index,arr){
 Konsole.log(Element, Index, arr);
})


4. Handgeschriebene Array-Reduzierungsmethode

Die Methode reduce() erhält eine Funktion als Akkumulator und jeder Wert im Array (von links nach rechts) wird auf einen einzelnen Wert reduziert. Dies ist eine weitere Methode zur Array-Element-für-Element-Verarbeitung, die in ES5 hinzugefügt wurde.

Parameter:

  • Rückruf (eine Funktion, die für jedes Element im Array aufgerufen werden soll, akzeptiert vier Funktionen:)
  1. previousValue (der Rückgabewert des letzten Callback-Funktionsaufrufs oder der Anfangswert)
  2. currentValue (das aktuell verarbeitete Array-Element)
  3. currentIndex (der Index des Array-Elements, das aktuell verarbeitet wird)
  4. Array (das Array, für das die Methode „reduce()“ aufgerufen wird)
  • initialValue (optionaler Anfangswert. Der Wert, der an previousValue übergeben wird, wenn die Rückruffunktion zum ersten Mal aufgerufen wird)
 Funktion reduzieren(arr, cb, Anfangswert){
     var num = initValue == undefiniert? num = arr[0]: initValue;
     var i = initValue == undefiniert? 1: 0
     für (i; i< arr.length; i++){
        Zahl = cb(Zahl,arr[i],i)
     }
     Rückgabenummer
 }
 
 Funktion fn(Ergebnis, aktuellerWert, Index){
     Ergebnis + aktueller Wert zurückgeben
 }
 
 var arr = [2,3,4,5]
 var b = reduzieren(arr, fn,10) 
 var c = reduzieren(arr, fn)
 console.log(b) // 24


5. Array-Abflachung

Array-Flattening dient zur Konvertierung eines mehrdimensionalen Arrays in ein eindimensionales Array

5. 1 Neue Methode flat(depth) von es6

sei a = [1,[2,3]];
a.flach(); // [1,2,3]
a.flach(1); //[1,2,3]

Tatsächlich gibt es einen einfacheren Weg. Sie müssen die Dimension des Arrays nicht kennen und können das Zielarray direkt in ein eindimensionales Array konvertieren. Der Tiefenwert ist auf Unendlich eingestellt.

sei a = [1,[2,3,[4,[5]]]];
a.flat(Infinity); // [1,2,3,4,5] a ist ein 4-dimensionales Array

5.2 Verwenden von cancat

Funktion flatten(arr) {
     var res = [];
     für (sei i = 0, Länge = arr.Länge; i < Länge; i++) {
     wenn (Array.isArray(arr[i])) {
     res = res.concat(flatten(arr[i])); //concat ändert das ursprüngliche Array nicht //res.push(...flatten(arr[i])); //oder verwende den Spread-Operator} else {
         res.push(arr[i]);
       }
     }
     Rückgabewert;
 }
 sei arr1 = [1, 2,[3,1],[2,3,4,[2,3,4]]]
abflachen(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]


Ergänzung: Tiefflach angeben

Addieren Sie bei jeder Rekursion einfach den aktuellen deep-1 . Wenn dieser größer als 0 ist, können Sie mit der Erweiterung fortfahren.

     Funktion flach(arr, tief) {
        lass res = []
        für(lass i in arr) {
            wenn(Array.isArray(arr[i])&&deep) {
                res = res.concat(flach(arr[i],tief-1))
            } anders {
                res.push(arr[i])
            }
        }
        Rückgabewert
    }
    Konsole.log(flach([12,[1,2,3],3,[2,4,[4,[3,4],2]]],1));


6. Funktions-Currying

Sie können sich im vorherigen Artikel Front-End-JavaScript gründlich mit dem Funktionscurrying vertraut machen und die gleiche Methode verwenden, die hier verwendet wird.

Die Definition von Currying lautet: Akzeptieren Sie einen Teil der Parameter, geben Sie eine Funktion zurück, um die verbleibenden Parameter zu akzeptieren, und führen Sie die ursprüngliche Funktion aus, nachdem genügend Parameter empfangen wurden.

Wenn die Curry-Funktion genügend Parameter erhält, führt sie die ursprüngliche Funktion aus. Wie kann man feststellen, wann genügend Parameter erreicht sind?

Es gibt zwei Ansätze:

  • Ermitteln Sie die Anzahl der formalen Parameter der Funktion über die Längeneigenschaft der Funktion. Die Anzahl der formalen Parameter ist die Anzahl der erforderlichen Parameter.
  • Geben Sie die Anzahl der erforderlichen Argumente beim Aufruf der Curry-Hilfsfunktion manuell an

Durch die Kombination dieser beiden Punkte können wir eine einfache Curry-Funktion implementieren:

/**
 * Curry die Funktion * @param fn die ursprüngliche Funktion, die curryed werden soll * @param len die Anzahl der benötigten Parameter, standardmäßig die Anzahl der formalen Parameter der ursprünglichen Funktion */
Funktion Curry (fn, Länge = fn. Länge) {
 returniere _curry.call(diese,fn,länge)
}
​
/**
 * Übertragungsfunktion * @param fn ursprüngliche Funktion, die ausgeführt werden soll * @param len benötigte Anzahl Parameter * @param args empfangene Parameterliste */
Funktion _curry(fn,Länge,...Argumente) {
    Rückgabefunktion (...params) {
         let _args = [...args,...params];
         if(_args.length >= len){
             gibt fn.apply(diese,_args) zurück;
         }anders{
          returniere _curry.call(diese,fn,länge,..._args)
         }
    }
}


Lassen Sie uns dies überprüfen:

lass _fn = curry(Funktion(a,b,c,d,e){
 konsole.log(a,b,c,d,e)
});
​
_fn(1,2,3,4,5); // drucken: 1,2,3,4,5
_fn(1)(2)(3,4,5); // drucken: 1,2,3,4,5
_fn(1,2)(3,4)(5); // drucken: 1,2,3,4,5
_fn(1)(2)(3)(4)(5); // drucken: 1,2,3,4,5


Unsere häufig verwendete Toolbibliothek lodash bietet auch eine curry -Methode und fügt eine sehr interessante placeholder hinzu, die die Reihenfolge der eingehenden Parameter mithilfe von Platzhaltern ändert.

Wenn wir beispielsweise einen Platzhalter übergeben, überspringen die in diesem Aufruf übergebenen Parameter den Platzhalter und der Platzhalter wird mit den Parametern des nächsten Aufrufs gefüllt, wie folgt:

Schauen Sie sich das Beispiel der offiziellen Website an:

Als nächstes überlegen wir, wie die Platzhalterfunktion implementiert wird.

Für die curry -Funktion von lodash wird die Curry-Funktion auf dem lodash Objekt bereitgestellt, sodass das Lodash-Objekt als Standardplatzhalter verwendet wird.

Da die von uns selbst implementierte Curry-Funktion auf keinem Objekt gemountet ist, verwenden wir die Curry-Funktion als Standardplatzhalter.

Der Zweck der Verwendung von Platzhaltern besteht darin, die Reihenfolge zu ändern, in der Parameter übergeben werden. Daher muss bei der Implementierung der Curry-Funktion jedes Mal aufgezeichnet werden, ob ein Platzhalter verwendet wird und welche Parameterposition durch den Platzhalter dargestellt wird.

Direkt zum Code:

/**
 * @param fn die Funktion, die curryiert werden soll* @param length die Anzahl der erforderlichen Parameter, standardmäßig die Anzahl der formalen Parameter der Funktion* @param holder Platzhalter, standardmäßig die aktuelle curryierte Funktion* @return {Function} die Funktion nach dem Curry*/
Funktion Curry (fn, Länge = fn. Länge, Halter = Curry) {
 returniere _curry.call(diese,fn,Länge,Halter,[],[])
}
/**
 * Übertragungsfunktion* @param fn ursprüngliche Funktion des Curryings* @param length Anzahl der von der ursprünglichen Funktion benötigten Parameter* @param holder empfangener Platzhalter* @param args empfangene Parameterliste* @param holders empfangene Platzhalterpositionsliste* @return {Function} Funktion zum Fortsetzen des Curryings oder Endergebnis*/
Funktion _curry(fn,Länge,Inhaber,Argumente,Inhaber){
 Rückgabefunktion (..._args) {
 //Kopieren Sie die Parameter, um Verwirrung durch mehrere Operationen an derselben Funktion zu vermeiden. let params = args.slice();
 //Kopieren Sie die Platzhalterpositionsliste und fügen Sie die neu hinzugefügten Platzhalter hier ein let _holders = holders.slice();
 //Parameter durchlaufen, Parameter anhängen oder placeholders_args.forEach((arg,i)=>{ ersetzen
 //Vor dem eigentlichen Parameter steht ein Platzhalter. Ersetzen Sie den Platzhalter durch den eigentlichen Parameter if (arg !== holder && holders.length) {
     let index = Inhaber.Shift();
     _holders.splice(_holders.indexOf(index),1);
     Parameter[Index] = Argument;
 }
 //Vor dem eigentlichen Parameter steht kein Platzhalter. Füge den Parameter an die Parameterliste an, sonst wenn (arg !== holder && !holders.length) {
     Parameter.push(arg);
 }
 //Der Platzhalter wird übergeben. Wenn vorher kein Platzhalter vorhanden ist, notieren Sie die Position des Platzhalters, sonst wenn (arg === holder && !holders.length) {
     Parameter.push(arg);
     _holders.push(Parameterlänge - 1);
 }
 //Der übergebene Platzhalter. Davor steht ein Platzhalter. Lösche die ursprüngliche Position des Platzhalters. Sonst wenn (arg === holder && holders.length) {
    Inhaber.Shift();
 }
 });
 // Die ersten Längendatensätze in Parametern enthalten keine Platzhalter, führen Sie die Funktion aus, wenn (params.length >= length && params.slice(0,length).every(i=>i!==holder)){
 gibt fn.apply(diese, Parameter) zurück;
 }anders{
 returniere _curry.call(dies,fn,Länge,Inhaber,Parameter,_Inhaber)
 }
 }
}


Überprüfen Sie es:

sei fn = Funktion(a, b, c, d, e) {
 Konsole.log([a, b, c, d, e]);
}
​
let _ = {}; // Platzhalter definieren let _fn = curry(fn,5,_); // Curry die Funktion, spezifiziere die benötigte Anzahl an Parametern und spezifiziere die benötigten Platzhalter​
_fn(1, 2, 3, 4, 5); // drucken: 1,2,3,4,5
_fn(_, 2, 3, 4, 5)(1); // drucken: 1,2,3,4,5
_fn(1, _, 3, 4, 5)(2); // drucken: 1,2,3,4,5
_fn(1, _, 3)(_, 4,_)(2)(5); // drucken: 1,2,3,4,5
_fn(1, _, _, 4)(_, 3)(2)(5); // drucken: 1,2,3,4,5
_fn(_, 2)(_, _, 4)(1)(3)(5); // drucken: 1,2,3,4,5


Bisher haben wir eine curry -Funktion vollständig implementiert ~~

7. Implementierung von Shallow Copy und Deep Copy

Deep Copy und Shallow Copy sind nur für Referenzdatentypen wie Object und Array bestimmt.

7.1 Der Unterschied zwischen Shallow Copy und Deep Copy

Oberflächliche Kopie: Erstellen Sie ein neues Objekt, das eine exakte Kopie der Eigenschaftswerte des Originalobjekts aufweist. Wenn die Eigenschaft von einem primitiven Typ ist, wird der Wert des primitiven Typs kopiert. Wenn die Eigenschaft von einem Referenztyp ist, wird die Speicheradresse kopiert. Wenn eines der Objekte die Eigenschaft des Referenztyps ändert, wirkt sich dies auf das andere Objekt aus.

Tiefes Kopieren: Kopiert ein Objekt vollständig aus dem Speicher und öffnet einen neuen Bereich im Heap-Speicher, um es zu speichern. Auf diese Weise wirkt sich eine Änderung des Kopierwerts nicht auf das alte Objekt aus.

Flache Kopierimplementierung:

Methode 1:

Funktion flachKopie(Ziel, Ursprung){
    für (Element im Ursprung lassen) Ziel[Element] = Ursprung[Element];
    Rücklaufziel;
}


Andere Methoden (integrierte API):

(1) Objekt.zuweisen

var obj={a:1,b:[1,2,3],c:function(){console.log('ich bin c')}}
var tar={};
Objekt.assign(tar,obj);


Natürlich ist diese Methode nur für Objekttypen geeignet. Wenn es sich um ein Array handelt, können Sie slice und concat verwenden

(2) Array.Prototyp.Scheibe

var arr=[1,2,[3,4]];
var newArr = arr.slice(0);
Array.prototype.concat
var arr=[1,2,[3,4]];
var newArr=arr.concat();


(3) Array.prototype.concat

var arr=[1,2,[3,4]];
var newArr=arr.concat();

Der Test ist derselbe wie oben (Assign wird mit Objekten getestet, Slice Concat wird mit Arrays getestet). Es ist besser zu verstehen, wenn man die Konzepte Shallow Copy und Deep Copy kombiniert.

Deep Copy-Implementierung:

Methode 1:

In das JSON-Format konvertieren und dann analysieren

const a = JSON.parse(JSON.stringify(b))

Methode 2:

// Implementierung der Deep-Copy-Rekursionsfunktion deepCopy(newObj,oldObj){
     für(var k in altesObj){
         let item = altesObj[k]
         // Bestimmen, ob es ein Array, ein Objekt oder ein einfacher Typ ist?
         wenn(Elementinstanz des Arrays){
             neuesObjekt[k]=[]
             deepCopy(neuesObjekt[k],Element)
         }sonst wenn(Elementinstanz des Objekts){
             neuesObj[k]={}
             deepCopy(neuesObjekt[k],Element)
         }else{ //Einfacher Datentyp, weise direkt newObj[k]=item zu
         }
     }
}


8. Handschriftlich anrufen, beantragen, binden

8.1 Handschriftlicher Anruf

Function.prototype.myCall=function(context=window){ // Funktionsmethode, wird also auf das Function-Prototypobjekt geschrieben if(typeof this !=="function"){ // Wenn hier tatsächlich nichts erforderlich ist, wird automatisch ein Fehler geworfen throw new Error("keine Funktion")
 }
 const obj = Kontext || Fenster // Hier können Sie die ES6-Methode verwenden, um Standardwerte für Parameter hinzuzufügen. Der globale Bereich des strikten Modus von js ist undefiniert
 obj.fn=this //das ist der aufrufende Kontext, dies ist eine Funktion, verwenden Sie diese Funktion als Methode von obj const arg=[...arguments].slice(1) //Das erste ist obj, also löschen Sie es und konvertieren Sie das Pseudo-Array in ein Array res=obj.fn(...arg)
 delete obj.fn // Wenn das Löschen fehlschlägt, werden immer mehr Kontextattribute zurückgegeben.
}
// Verwendung: f.call(obj,arg1)
Funktion f(a,b){
 konsole.log(a+b)
 console.log(dieser.Name)
}
lass obj = {
 Name:1
}
f.myCall(obj,1,2) //Andernfalls zeigt dies auf das Fenster

 
obj.greet.call({name: 'Spike'}) //Die Ausgabe ist Spike

8.2 Handschriftliches Anwenden (Argumente [dies, [Parameter 1, Parameter 2 ...]])

Function.prototype.myApply = function(context) { // Pfeilfunktionen haben niemals ein Argumentobjekt! ! ! ! ! Sie können hier keine Pfeilfunktion schreiben let obj=context||window
 obj.fn=dies
 const arg=arguments[1]||[] //Wenn Parameter vorhanden sind, ist das Ergebnis ein Array let res=obj.fn(...arg)
 obj.fn löschen
 Rückgabewert
} 
Funktion f(a,b){
 konsole.log(a,b)
 console.log(dieser.Name)
}
lass obj = {
 Name: „Zhang San“
}
f.myApply(obj,[1,2]) //Argumente[1]


8.3 Handschriftliche Bindung

dieser.Wert = 2
var foo = {
 Wert: 1
};
var bar = function(name, alter, schule){
 console.log(name) // 'Ein'
 console.log(Alter) // 22
 console.log(Schule) // 'Homeschooling-Universität'
}
var result = bar.bind(foo, 'An') //Setzt einige Parameter 'An' vorab
result(22, 'Home University') //Dieser Parameter wird mit den voreingestellten Parametern zusammengeführt und in bar eingefügt.

Einfache Version

Function.prototype.bind = Funktion(Kontext, ...äußereArgs) {
 var fn = dies;
 return function(...innerArgs) { //Gibt eine Funktion zurück, ...rest ist der Parameter, der beim eigentlichen Aufruf übergeben wird return fn.apply(context,[...outerArgs, ...innerArgs]); //Gibt die Funktion zurück, die dies geändert hat,
 //Parameter zusammenführen}
}


Gründe, warum „Neu“ fehlgeschlagen ist:

Beispiel:

// einen Kontext deklarieren let thovino = {
 Name: 'thovino'
}
​
// Deklariere einen Konstruktor let eat = function (food) {
 dieses.Essen = Essen
 console.log(`${this.name} isst ${this.food}`)
}
eat.prototype.sayFuncName = Funktion () {
 console.log('Funktionsname: essen')
}
​
// binden let thovinoEat = eat.bind(thovino)
let instance = new thovinoEat('orange') //Eigentlich wird Orange in Thovino eingefügt console.log('instance:', instance) // {}


Die generierte Instanz ist ein leeres Objekt

Wenn der new Operator ausgeführt wird, kann unsere thovinoEat Funktion folgendermaßen angezeigt werden:

Funktion thovinoEat (...innerArgs) {
 eat.call(thovino, ...äußereArgs, ...innereArgs)
}


Wenn der neue Operator den dritten Schritt thovinoEat.call(obj, ...args ) erreicht, ist das Objekt hier das einfache leere Objekt {}, das vom neuen Operator selbst erstellt wurde, aber es ersetzt nicht wirklich das Kontextobjekt thovino innerhalb der Funktion thovinoEat . Dies geht über die Möglichkeiten des Aufrufs hinaus, da nicht mehr der this-Zeiger innerhalb der thovinoEat -Funktion, sondern thovino Objekt ersetzt werden muss.

Mit anderen Worten: Wir möchten, dass der neue Operator in eat auf das leere Objekt verweist, das vom Operator selbst erstellt wurde. Aber es zeigt tatsächlich auf thovino , und der dritte Schritt des new Operators ist nicht erfolgreich!

Neue und vererbbare Versionen

Function.prototype.bind = Funktion (Kontext, ...äußereArgumente) {
 lass das = dies;
​
Funktion res (...innerArgs) {
     wenn (diese Instanz von res) {
         // Wenn der neue Operator ausgeführt wird // Hier zeigt dies auf das einfache leere Objekt, das von new selbst im dritten Schritt des neuen Operators erstellt wurde {}
         das.aufrufen(dies, ...äußereArgs, ...innereArgs)
     } anders {
         // Normales Binden
         that.call(Kontext, ...äußereArgs, ...innereArgs)
     }
     }
     res.prototype = dieser.prototype //! ! !
     Rückgabewert
}


9. Manuelle Implementierung neuer

Neue Prozesstextbeschreibung:

  1. Erstellen Sie ein leeres Objekt obj;
  2. Setzen Sie den impliziten Prototyp des leeren Objekts auf den Prototyp des Konstruktors.
  3. Verwenden Sie call, um die Referenz dieses
  4. Wenn kein Rückgabewert vorhanden ist oder ein Nicht-Objektwert zurückgegeben wird, wird „obj“ als neues Objekt zurückgegeben; wenn der Rückgabewert ein neues Objekt ist, wird das Objekt direkt zurückgegeben.
Funktion Person(Name,Alter){
 dieser.name=Name
 this.age=Alter
}
Person.prototype.sayHi=function(){
 console.log('Hallo! Ich bin '+this.name)
}
let p1=neue Person('neue Person',18)
​
////Manuell neue implementieren
Funktion erstellen(){
 let obj={}
 //Konstruktor abrufen let fn=[].shift.call(arguments) //Argumenteobjekt in ein Array umwandeln. Argumente sind kein Array, sondern ein Objekt! ! ! Diese Methode entfernt das erste Element des Argument-Arrays, ! ! Dabei spielt es keine Rolle, ob das leere Array mit Elementen gefüllt ist oder nicht, es hat keinen Einfluss auf das Ergebnis von Argumenten oder let arg = [].slice.call(arguments,1)
 obj.__proto__ = fn.prototype
 let res = fn.apply(obj, arguments) //Ändern Sie dies, um der Instanz Methoden und Eigenschaften hinzuzufügen //Stellen Sie sicher, dass ein Objekt zurückgegeben wird (falls fn kein Konstruktor ist)
 Rückgabetyp von res==='Objekt'?res:obj
}
​
let p2=erstellen(Person,'Person',19)
p2.sagHallo()


Detail:

[].shift.call(arguments) kann auch wie folgt geschrieben werden:
 let arg=[...Argumente]
 let fn=arg.shift() //Aktiviert Argumente zum Aufrufen von Array-Methoden, der erste Parameter ist der Konstruktor obj.__proto__=fn.prototype
 //Ändern Sie diesen Zeiger, um der Instanz Methoden und Attribute hinzuzufügen let res=fn.apply(obj,arg)


10. Handgeschriebenes Versprechen (oft getestet mit promise.all, promise.race)

// Drei Zustände, die in der Promise/A+ Spezifikation spezifiziert sind const STATUS = {
 PENDING: 'ausstehend',
 ERFÜLLT: 'erfüllt',
 ABGELEHNT: „abgelehnt“
}
​
Klasse MeinVersprechen {
 // Der Konstruktor erhält einen Ausführungs-Callback constructor(executor) {
     this._status = STATUS.PENDING // Versprechen Sie den anfänglichen Status this._value = undefined // dann den Rückrufwert this._resolveQueue = [] // Erfolgswarteschlange, ausgelöst durch „Resolve“ this._rejectQueue = [] // Fehlerwarteschlange, ausgelöst durch „Reject“​
 //Verwende die Pfeilfunktion um dies zu beheben (die Auflösungsfunktion wird im Executor ausgelöst, sonst kann sie nicht gefunden werden)
 const resolve = Wert => {
     const laufen = () => {
         // Die Promise/A+ Spezifikation legt fest, dass der Promise-Status nur von ausstehend bis erfüllt ausgelöst werden kann
         wenn (this._status === STATUS.PENDING) {
             this._status = STATUS.FULFILLED // Status ändern this._value = value // Aktuellen Wert für anschließenden Rückruf speichern​
             // Rückruf auflösen, während (this._resolveQueue.length) {
                 const Rückruf = this._resolveQueue.shift()
                 Rückruf(Wert)
             }
         }
     }
     // Kapseln Sie den Resolve-Callback-Vorgang in eine Funktion ein und setzen Sie diese in „setTimeout“, um die Funktion für asynchrone Promise-Aufrufe zu implementieren (Mikrotask in der Spezifikation, Makrotask hier)
     setTimeout(ausführen)
 }
​
 // Das Gleiche wie „Resolve“
 const reject = Wert => {
     const laufen = () => {
         wenn (this._status === STATUS.PENDING) {
         this._status = STATUS.ABGELEHNT
         this._value = Wert
        ​
         während (this._rejectQueue.length) {
             const Rückruf = this._rejectQueue.shift()
             Rückruf(Wert)
         }
     }
 }
     setTimeout(ausführen)
 }

     // Wenn new Promise() aufgerufen wird, wird der Executor sofort ausgeführt und resolve und reject werden übergeben
     Vollstrecker (lösen, ablehnen)
 }
​
 // then-Methode, die einen erfolgreichen Rückruf und einen fehlgeschlagenen Rückruf empfängt function then(onFulfilled, onRejected) {
  // Gemäß der Spezifikation wird der Parameter von then, wenn es sich nicht um eine Funktion handelt, ignoriert, der Wert weitergegeben und der Kettenaufruf weiter ausgeführt typeof onFulfilled !== 'function' ? onFulfilled = value => value : null
  typeof onRejected !== 'Funktion' ? onRejected = Fehler => Fehler : null

  // gibt dann ein neues Versprechen zurück
  returniere neues MyPromise((auflösen, ablehnen) => {
    const resolveFn = Wert => {
      versuchen {
        const x = beiErfüllt(Wert)
        // Klassifiziere den Rückgabewert. Wenn es ein Promise ist, warte auf die Änderung des Promise-Status, andernfalls löse direkt auf
        x Instanz von MyPromise? x.then(auflösen, ablehnen) : auflösen(x)
      } Fehler abfangen {
        ablehnen(Fehler)
      }
    }
  }
}
​
  const rejectFn = Fehler => {
      versuchen {
        const x = beiAbgelehnt(Fehler)
        x Instanz von MyPromise? x.then(auflösen, ablehnen) : auflösen(x)
      } Fehler abfangen {
        ablehnen(Fehler)
      }
    }

    Schalter (this._status) {
      Fall STATUS.PENDING:
        this._resolveQueue.push(resolveFn)
        dies._rejectQueue.push(rejectFn)
        brechen;
      Fall STATUS.ERFÜLLT:
        auflösenFn(diesen._Wert)
        brechen;
      Fall STATUS.ABGELEHNT:
        rejectFn(dieser._Wert)
        brechen;
    }
 })
 }
 fangen (ablehnenFn) {
  gib dies zurück.dann(undefiniert, rejectFn)
}
// promise.finally-Methode finally(callback) {
  gib dies zurück.dann(Wert => MyPromise.resolve(callback()).dann(() => Wert), Fehler => {
    MyPromise.resolve(callback()).then(() => Fehler)
  })
}

 // statische Auflösungsmethode statische Auflösung(Wert) {
      Rückgabewert: Instanz von MyPromise? Wert: neues MyPromise (auflösen => auflösen(Wert))
  }

 // statische Ablehnungsmethode statische Ablehnung (Fehler) {
      returniere neues MyPromise((auflösen, ablehnen) => ablehnen(Fehler))
    }

 // statische alle Methoden static all(promiseArr) {
      lass count = 0
      let ergebnis = []
      returniere neues MyPromise((auflösen, ablehnen) => {
        wenn (!promiseArr.length) {
          Rückgabewert (Ergebnis)
        }
        promiseArr.fürEach((p, i) => {
          MyPromise.resolve(p).then(Wert => {
            zählen++
            Ergebnis[i] = Wert
            wenn (Anzahl === promiseArr.Länge) {
              Lösung (Ergebnis)
            }
          }, Fehler => {
            ablehnen(Fehler)
          })
        })
      })
    }

 // Statische Race-Methode static race(promiseArr) {
      returniere neues MyPromise((auflösen, ablehnen) => {
        promiseArr.fürJeden(p => {
          MyPromise.resolve(p).then(Wert => {
            Auflösung (Wert)
          }, Fehler => {
            ablehnen(Fehler)
          })
        })
      })
    }
}

11. Handgeschriebenes natives AJAX

Schritt:

  • Erstellen einer XMLHttpRequest-Instanz
  • HTTP-Anfragen stellen
  • Der Server gibt eine Zeichenfolge im XML-Format zurück
  • JS analysiert XML und aktualisiert Teilseiten

Im Laufe der Geschichte wurde XML jedoch eliminiert und durch JSON ersetzt.

Nachdem Sie die Eigenschaften und Methoden verstanden haben, schreiben Sie die einfachste GET-Anfrage gemäß den AJAX-Schritten.

Version 1.0:

myButton.addEventListener('klicken', Funktion () {
  ajax()
})

Funktion ajax() {
  let xhr = new XMLHttpRequest() //Instanziieren, um die Methode xhr.open('get', 'https://www.google.com') aufzurufen //Parameter 2, URL. Parameter drei: asynchron xhr.onreadystatechange = () => { //Diese Funktion wird aufgerufen, wenn sich die Eigenschaft readyState ändert.
    if (xhr.readyState === 4) { //Der aktuelle Status des XMLHttpRequest-Proxys.
      if (xhr.status >= 200 && xhr.status < 300) { //200-300 Anfrage erfolgreich let string = request.responseText
        //Die Methode JSON.parse() wird verwendet, um die JSON-Zeichenfolge zu analysieren und einen JavaScript-Wert oder ein Objekt zu konstruieren, das durch die Zeichenfolge let object = JSON.parse(string) beschrieben wird.
      }
    }
  }
  request.send() //Wird verwendet, um tatsächlich eine HTTP-Anfrage zu stellen. GET-Anfrage ohne Parameter}

Versprechenserfüllung

Funktion Ajax (URL) {
  const p = neues Versprechen((lösen, ablehnen) => {
    let xhr = neue XMLHttpRequest()
    xhr.open('erhalten', URL)
    xhr.onreadystatechange = () => {
      wenn (xhr.readyState == 4) {
        wenn (xhr.status >= 200 und xhr.status <= 300) {
          auflösen(JSON.parse(xhr.responseText))
        } anders {
          reject('Anforderungsfehler')
        }
      }
    }
    xhr.send() //HPPT-Anfrage senden})
  Rückkehr p
}
let url = "/data.json"
ajax(url).dann(res => console.log(res))
  .catch(Grund => console.log(Grund))


12. Handschriftliche Drosselung und Anti-Shake-Funktion

Sowohl die Funktionsdrosselung als auch die Funktion „Anti-Shake“ zielen darauf ab, die Ausführungshäufigkeit von Funktionen zu begrenzen. Sie sind eine Lösung zur Leistungsoptimierung, wie z. B. resize und scroll von window , die mousemove beim Ziehen und keyup bei der Texteingabe und der automatischen Vervollständigung.
Drosselung: Ereignisse kontinuierlich auslösen, Funktionen jedoch nur alle n Sekunden ausführen

Beispiel : (Verwenden Sie es, wenn eine kontinuierliche Bewegung aufgerufen werden muss, legen Sie ein Zeitintervall fest), wie beim Ziehen von DOM. Wenn Sie Debounce verwenden, entsteht ein Gefühl der Blockierung, da es nur einmal ausgeführt wird, wenn es stoppt. Zu diesem Zeitpunkt sollten Sie Throttling verwenden und es innerhalb eines bestimmten Zeitraums mehrmals ausführen, was viel reibungsloser läuft.

Anti-Shake: bedeutet, dass eine Funktion nur einmal innerhalb von n Sekunden nach Auslösen eines Ereignisses ausgeführt werden kann. Wenn das Ereignis innerhalb von n Sekunden erneut ausgelöst wird, wird die Ausführungszeit der Funktion neu berechnet.

Beispiel : (Wird nicht aufgerufen, wenn es kontinuierlich ausgelöst wird, sondern nach einer gewissen Zeit nach der Auslösung aufgerufen), wie bei der Nachahmung der Baidu-Suche sollte Anti-Shake verwendet werden. Wenn ich kontinuierlich eingebe, wird keine Anforderung gesendet; wenn ich für eine gewisse Zeit keine Eingabe mache, wird einmal eine Anforderung gesendet; wenn ich weniger als diese Zeitspanne weiter eingebe, wird die Zeit neu berechnet und es wird keine Anforderung gesendet.

12.1 Anti-Shake-Implementierung

Funktion Entprellung(fn, Verzögerung) {
     wenn(Typ von fn!=='Funktion') {
        throw new TypeError('fn ist keine Funktion')
     }
     let timer; // Einen Timer verwalten
     Rückgabefunktion () {
         var _this = this; // Holen Sie sich das this des Debounce-Ausführungsbereichs (das Objekt, an das die ursprüngliche Funktion gemountet ist)
         var args = Argumente;
         wenn (Zeitgeber) {
            Zeitüberschreitung löschen(Timer);
         }
         Timer = setzeTimeout(Funktion () {
            fn.apply(_this, args); // Verwenden Sie „apply“, um auf das Objekt zu zeigen, das „debounce“ aufruft, was gleichbedeutend ist mit „_this.fn(args);“.
         }, Verzögerung);
     };
}

// Anruf
input1.addEventListener('keyup', entprellung(() => {
 konsole.log(eingabe1.wert)
}), 600)


12.2 Implementierung der Drosselung

Funktion Drosselklappe(fn, Verzögerung) {
  lass den Timer;
  Rückgabefunktion () {
    var _this = dies;
    var args = Argumente;
    wenn (Zeitgeber) {
      zurückkehren;
    }
    Timer = setzeTimeout(Funktion () {
      fn.apply(_this, args); // Hier empfängt args die Parameter der von außen zurückgegebenen Funktion und Argumente können nicht verwendet werden
      // fn.apply(_this, arguments); Hinweis: Chrome 14 und Internet Explorer 9 akzeptieren immer noch keine arrayähnlichen Objekte. Wenn ihnen ein arrayähnliches Objekt übergeben wird, wird eine Ausnahme ausgelöst.
      timer = null; // Lösche den Timer nach der Ausführung von fn nach der Verzögerung. Zu diesem Zeitpunkt ist der Timer falsch und der Gashebel kann den Timer eingeben}, Verzögerung)
  }
}

div1.addEventListener('ziehen', Drosselklappe((e) => {
  Konsole.log(e.offsetX, e.offsetY)
}, 100))

13. Handschriftliches Versprechen, Bilder hochzuladen

Funktion getData(URL) {
  returniere neues Promise((lösen, ablehnen) => {
    $.ajax({
      URL (URL = URL = URL),
      Erfolg(Daten) {
        auflösen (Daten)
      },
      Fehler(Fehler) {
        ablehnen (Fehler)
      }
    })
  })
}
const url1 = "./data1.json"
const url2 = "./data2.json"
const url3 = "./data3.json"
getData(url1).then(data1 => {
  konsole.log(daten1)
  returniere getData(url2)
}).dann(data2 => {
  konsole.log(data2)
  returniere getData(url3)
}).dann(data3 =>
  konsole.log(data3)
).catch(err =>
  console.error(fehler)
)


14. Die Funktion gibt eine Zahl pro Sekunde aus

(!!! Diese Frage wurde dieser Tage im Campus-Rekrutierungsinterview von ByteDance gestellt. Es wurde gefragt, welche Var ausgegeben wird. Warum kann sie in „let“ geändert werden?
Gibt es eine andere Möglichkeit, dies zu erreichen? Ich habe in meinem Blog über die zweite Methode des Schreibens ohne Let geschrieben, aber ich habe sie vergessen ~~~ Ich habe sie vergeblich gelernt)

ES6: Implementierung mit dem Prinzip des Let-Block-Bereichs

for(let i=0;i<=10;i++){ //Mit var drucken ist 11
 setzeTimeout(()=>{
    konsole.log(i);
 },1000*i)
}


Schreiben ohne let: Das Prinzip besteht darin, einen Blockebenenbereich mit einer sofort ausgeführten Funktion zu erstellen

für(var i = 1; i <= 10; i++){
    (Funktion (i) {
        setzeTimeout(Funktion () {
            konsole.log(i);
        }, 1000 * ich)
    })(ich);
}


15. 10 Tags erstellen und die entsprechenden Seriennummern beim Anklicken einblenden?

var ein
für (lass i = 0; i < 10; i++) {
 a=Dokument.Element erstellen('a')
 a.innerHTML=i+'<br>'
 a.addEventListener('klicken',Funktion(e){
     console.log(this) //das ist das aktuell angeklickte <a>
     e.preventDefault() //Wenn diese Methode aufgerufen wird, wird das Standardereignisverhalten nicht mehr ausgelöst.
     //Wenn Sie beispielsweise nach der Ausführung dieser Methode auf einen Link (ein Tag) klicken, springt der Browser nicht zur neuen URL. Wir können event.isDefaultPrevented() verwenden, um zu bestimmen, ob diese Methode (für dieses Ereignisobjekt) aufgerufen wurde.
     Warnung(i)
 })
 const d = Dokument.QuerySelector('div')
 d.appendChild(a) //append hängt das Element an ein bestehendes Element an.
}


16. Implementieren Sie das Abonnement und die Veröffentlichung von Ereignissen (eventBus).

Implementieren Sie die EventBus-Klasse mit on off once trigger , die dem Binden und Aufheben von Ereignis-Listenern, dem Aufheben von Ereignissen nach einer Ausführung und dem Auslösen von Ereignis-Listenern entsprechen. Diese Frage wurde sowohl von ByteDance als auch von Kuaishou gestellt. Ich bin in letzter Zeit beschäftigt und die Antwort wird später aktualisiert.

Klasse EventBus {
    ein(Ereignisname, Listener) {}
    aus(Ereignisname, Listener) {}
    einmal (Ereignisname, Listener) {}
    Auslöser(Ereignisname) {}
}

const e = neuer EventBus();
// fn1 fn2
e.on('e1', fn1)
e.einmal('e1', fn2)
e.trigger('e1') // fn1() fn2()
e.trigger('e1') // fn1()
e.aus('e1', fn1)
e.trigger('e1') // null

erreichen:

      //Klasse class EventBus deklarieren {
        Konstruktor() {
          this.eventList = {} //Erstellen Sie ein Objekt zum Sammeln von Ereignissen}
        //Ereignis veröffentlichen $on(eventName, fn) {
          //Feststellen, ob der Ereignisname veröffentlicht wurde? Veröffentlichung hinzufügen: Veröffentlichung erstellen und hinzufügen this.eventList[eventName]
            ? this.eventList[Ereignisname].push(fn)
            : (diese.eventList[eventName] = [fn])
        }
        //Ereignis abonnieren $emit(eventName) {
          if (!eventName) throw new Error('Bitte geben Sie den Ereignisnamen ein')
          //Abonnementparameter abrufen const data = [...arguments].slice(1)
          wenn (diese.eventList[eventName]) {
            diese.eventList[eventName].forEach((i) => {
              versuchen {
                i(...data) //Abfrageereignisse} catch (e) {
                console.error(e + 'eventName:' + eventName) //Fehler während der Ausführung sammeln}
            })
          }
        }
        //Einmal ausführen$once(eventName, fn) {
          const _this = dies
          Funktion onceHandle() {
            fn.apply(null, Argumente)
            _this.$off(eventName, onceHandle) //Überwachung nach erfolgreicher Ausführung abbrechen}
          dies.$on(Ereignisname, onceHandle)
        }
        //Abmelden $off(eventName, fn) {
          //Alle Abonnements stornieren, wenn keine Parameter übergeben werden if (!arguments.length) {
            Rückgabe (diese.Ereignisliste = {})
          }
          //Wenn eventName als Array übergeben wird, mehrere Abonnements stornieren, if (Array.isArray(eventName)) {
            returniere Ereignisname.fürJedes((Ereignis) => {
              dies.$aus(Ereignis, fn)
            })
          }
          //Alle Warteschlangen unter dem Ereignisnamen abbrechen, wenn fn nicht übergeben wird, if (arguments.length === 1 || !fn) {
            this.eventList[Ereignisname] = []
          }
          //Brechen Sie die Funktion unter dem Ereignisnamen ab
          this.eventList[Ereignisname] = this.eventList[Ereignisname].filter(
            (f) => f !== fn
          )
        }
      }
      const event = neuer EventBus()

      sei b = Funktion (v1, v2, v3) {
        console.log('b', v1, v2, v3)
      }
      sei a = Funktion () {
        Konsole.log('a')
      }
      Ereignis.$einmal('Test', a)
      Ereignis.$auf('test', b)
      Ereignis.$emit('test', 1, 2, 3, 45, 123)

      Ereignis.$aus(['test'], b)

      Ereignis.$emit('test', 1, 2, 3, 45, 123)

Dies ist das Ende des Artikels über die hochfrequente Handschrift von js für Front-End-Interviews. Weitere verwandte Inhalte zur hochfrequenten Handschrift von js finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder durchsuchen Sie die verwandten Artikel unten weiter. Ich hoffe, Sie werden 123WORDPRESS.COM in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • JS implementiert eine Methode zum Verhindern von hochfrequenten Dauerklicks [basierend auf ES6-Syntax]
  • So verhindern Sie das häufige Auslösen und Aufrufen von Ereignisfunktionen in JavaScript
  • Kennen Sie alle 24 Methoden zur JavaScript-Schleifendurchquerung?
  • Detaillierte Erklärung der JavaScript-Array-Deduplizierung
  • js, um einen einfachen Karusselleffekt zu erzielen
  • js zur Realisierung einer einfachen Scheibenuhr
  • JavaScript zur Implementierung des Flugzeugkriegsspiels
  • Sogar ein Anfänger kann den Unterschied zwischen typeof und instanceof in js verstehen

<<:  Zusammenfassung verschiedener Replikationsmethoden für die MySQL Master-Slave-Replikation

>>:  So lösen Sie das domänenübergreifende Front-End-Problem mithilfe des Nginx-Proxys

Artikel empfehlen

So erstellen Sie Ihren eigenen nativen JavaScript-Router

Inhaltsverzeichnis Vorwort Einführung JavaScript ...

SVG+CSS3 zum Erzielen eines dynamischen Welleneffekts

Eine Vektorwelle <svg viewBox="0 0 560 20...

Details zum Prototypmodus des Javascript-Entwurfsmusters

Inhaltsverzeichnis 1. Prototyp-Modus Beispiel 1 B...

Analyse des kumulativen Aggregationsprinzips von MySQL und Anwendungsbeispiele

Dieser Artikel veranschaulicht anhand von Beispie...

Mehrere Möglichkeiten, Python-Programme im Linux-Hintergrund auszuführen

1. Die erste Methode besteht darin, den Befehl un...

Bringen Sie Ihnen bei, wie Sie die Zeigerposition in Javascript erhalten

Die Methode zum Abrufen der Zeigerposition in Jav...

Tutorial-Diagramm zur Installation von Zabbix2.4 unter Centos6.5

Die feste IP-Adresse des Centos-DVD1-Versionssyst...