Heim > Schlagzeilen > Hauptteil

Eine Zusammenfassung verschiedener wichtiger Handschriften, die oft in Front-End-Interviews getestet werden!

藏色散人
Freigeben: 2021-01-12 14:29:38
nach vorne
2749 Leute haben es durchsucht

Dieser Artikel behandelt verschiedene wichtige Handschrifttests, die häufig in Front-End-Interviews getestet werden.

RECORMENDE:
2021 PHP-Interviewfragen Zusammenfassung (Sammlung) 》 《《
2021 Front-End-Interview Fragen Zusammenfassung (Sammlung)

    it wird empfohlen,:
  • instance of (Inspektion Verständnis der Prototypenkette)
  • neu (Verständnis des Prozesses der Erstellung von Objektinstanzen)
  • call&apply&bind (Verständnis dieses Zeigers)
  • Handschriftliches Versprechen (Verständnis von Asynchronität)

Handschriftliches natives Ajax (Verständnis der Ajax-Prinzipien und http-Anfragen Das Verständnis der Methode konzentriert sich auf die Implementierung von Get- und Post-Anfragen.

Instanz von

Während des Suchvorgangs wird die Prototypenkette der Variablen auf der linken Seite durchlaufen, bis der Prototyp der Variablen auf der rechten Seite gefunden wird. Die Suche schlägt fehl und es wird „false“ zurückgegeben. Implementieren Sie die Map-Methode des Arrays

map des Arrays. Die Methode ()

gibt ein neues Array zurück. Jedes Element in diesem neuen Array entspricht dem Rückgabewert des entsprechenden Elements im ursprünglichen Array, nachdem die bereitgestellte Funktion einmal aufgerufen wurde . Verwendung:

 let myInstanceof = (target,origin) => {
     while(target) {
         if(target.__proto__===origin.prototype) {
            return true
         }
         target = target.__proto__
     }
     return false
 }
 let a = [1,2,3]
 console.log(myInstanceof(a,Array));  // true
 console.log(myInstanceof(a,Object));  // true
Nach dem Login kopieren

Native Implementierung:

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

3. Reduce implementiert die Map-Methode des Arrays

Verwenden Sie die integrierte Reduce-Methode des Arrays, um die Map-Methode zu implementieren und die Beherrschung zu untersuchen des Reduzierprinzips

 Array.prototype.myMap = function(fn, thisValue) {
     let res = []
     thisValue = thisValue||[]
     let arr = this
     for(let i in arr) {
        res.push(fn(arr[i]))
     }
     return res
 }
Nach dem Login kopieren

4 . Die Reduziermethode des handgeschriebenen Arrays

reduce() empfängt eine Funktion als Akkumulator (von links nach rechts) und wird schließlich zu einem Wert . Es ist ein weiteres neues Array in ES5.

Parameter:

callback (eine Funktion, die für jedes Element im Array aufgerufen wird und vier Funktionen akzeptiert :)

previousValue (der Rückgabewert des letzten Zeitpunkt, zu dem die Callback-Funktion aufgerufen wurde, oder der Anfangswert)

currentValue (das Array-Element, das gerade verarbeitet wird)

currentIndex (der Index des Array-Elements, das gerade verarbeitet wird)

array (das Array, das die Methode Reduce() aufruft)
  • initialValue (optionaler Anfangswert. Als Wert, der an previousValue übergeben wird, wenn die Rückruffunktion zum ersten Mal aufgerufen wird)
    • Array.prototype.myMap = function(fn,thisValue){
           var res = [];
           thisValue = thisValue||[];
           this.reduce(function(pre,cur,index,arr){
               return res.push(fn.call(thisValue,cur,index,arr));
           },[]);
           return res;
      }
      
      var arr = [2,3,1,5];
      arr.myMap(function(item,index,arr){
       console.log(item,index,arr);
      })
      Nach dem Login kopieren
    • 5. Array-Flattening
    • Array-Flattening dient der Konvertierung eines mehrdimensionalen Arrays in ein eindimensionales Array
    1. Die neue Methode flat( bereitgestellt von es6 Depth)
  •  function reduce(arr, cb, initialValue){
         var num = initValue == undefined? num = arr[0]: initValue;
         var i = initValue == undefined? 1: 0
         for (i; i
    Nach dem Login kopieren
  • Tatsächlich gibt es eine einfachere Möglichkeit, das Zielarray direkt in ein 1-dimensionales Array umzuwandeln, ohne die Dimensionen des Arrays zu kennen dimensionales Array. Der Tiefenwert ist auf Unendlich eingestellt. 2. Verwenden von cancat Funktion.

Wenn die Curry-Funktion genügend Parameter erhält, führt sie die ursprüngliche Funktion aus. Wie kann festgestellt werden, wann genügend Parameter erreicht sind? Es gibt zwei Ideen:

Ermitteln Sie die Anzahl der formalen Parameter der Funktion über das Längenattribut der Funktion. Die Anzahl der formalen Parameter ist die Anzahl der erforderlichen Parameter.

Wenn Sie die Currying-Tool-Funktion aufrufen, Geben Sie die erforderliche Anzahl von Parametern manuell an.

Kombinieren Sie diese beiden Punkte, um eine einfache Curry-Funktion zu implementieren:

let a = [1,[2,3]]; 
a.flat(); // [1,2,3] 
a.flat(1); //[1,2,3]
Nach dem Login kopieren

Lassen Sie es uns überprüfen:

let a = [1,[2,3,[4,[5]]]]; 
a.flat(Infinity); // [1,2,3,4,5]  a是4维数组
Nach dem Login kopieren
Unsere häufig verwendete Werkzeugbibliothek lodash bietet auch die Curry-Methode und fügt einen sehr interessanten Platzhalter hinzu Die Funktion wird verwendet, um die Reihenfolge eingehender Parameter durch Platzhalter zu ändern.

Wenn wir beispielsweise einen Platzhalter übergeben, überspringen die in diesem Aufruf übergebenen Parameter den Platzhalter und die Position des Platzhalters wird durch die Parameter des nächsten Aufrufs gefüllt, wie zum Beispiel diesen:

Schauen Sie sich direkt an offizielle Website Beispiel:

  • Als nächstes überlegen wir uns, wie wir die Platzhalterfunktion implementieren.

    Für die Curry-Funktion von lodash wird die Curry-Funktion auf dem Lodash-Objekt gemountet, sodass das Lodash-Objekt als Standardplatzhalter verwendet wird.
  • Und unsere selbst implementierte Curry-Funktion selbst ist nicht auf einem Objekt montiert, daher wird die Curry-Funktion als Standardplatzhalter verwendet

    Der Zweck der Verwendung von Platzhaltern besteht darin, die Reihenfolge der Parameterübergabe zu ändern, sodass in der Implementierung der Curry-Funktion Jedes Mal muss aufgezeichnet werden, ob ein Platzhalter verwendet wird, und die durch den Platzhalter dargestellte Parameterposition wird aufgezeichnet.
Gehe direkt zum Code:

function flatten(arr) {
     var res = [];
     for (let i = 0, length = arr.length; i Überprüfen:;<p></p><pre class="brush:php;toolbar:false">/**
 * 将函数柯里化
 * @param fn    待柯里化的原函数
 * @param len   所需的参数个数,默认为原函数的形参个数
 */
function curry(fn,len = fn.length) {
 return _curry.call(this,fn,len)
}

/**
 * 中转函数
 * @param fn    待柯里化的原函数
 * @param len   所需的参数个数
 * @param args  已接收的参数列表
 */
function _curry(fn,len,...args) {
    return function (...params) {
         let _args = [...args,...params];
         if(_args.length >= len){
             return fn.apply(this,_args);
         }else{
          return _curry.call(this,fn,len,..._args)
         }
    }
}
Nach dem Login kopieren
Zu diesem Zeitpunkt haben wir eine Curry-Funktion vollständig implementiert~~

7. Deep Copy implementieren

Der Unterschied zwischen flachem Kopieren und tiefem Kopieren :Eine Zusammenfassung verschiedener wichtiger Handschriften, die oft in Front-End-Interviews getestet werden!

Flache Kopie: Kopiert nur eine Ebene und kopiert nur Referenzen auf tieferen Objektebenen.

Tiefe Kopie: Kopiert mehrere Ebenen,

Daten auf jeder Ebene werden kopiert. Auf diese Weise hat die Änderung des Kopierwerts keine Auswirkungen auf andere Objekte

ES6-Methode für flaches Kopieren: Object.assign(target,...sources)

let _fn = curry(function(a,b,c,d,e){
 console.log(a,b,c,d,e)
});

_fn(1,2,3,4,5);     // print: 1,2,3,4,5
_fn(1)(2)(3,4,5);   // print: 1,2,3,4,5
_fn(1,2)(3,4)(5);   // print: 1,2,3,4,5
_fn(1)(2)(3)(4)(5); // print: 1,2,3,4,5
Nach dem Login kopieren

8 Handschriftlicher Aufruf, Anwenden, Binden

手写call

Function.prototype.myCall=function(context=window){  // 函数的方法,所以写在Fuction原型对象上
 if(typeof this !=="function"){   // 这里if其实没必要,会自动抛出错误
    throw new Error("不是函数")
 }
 const obj=context||window   //这里可用ES6方法,为参数添加默认值,js严格模式全局作用域this为undefined
 obj.fn=this      //this为调用的上下文,this此处为函数,将这个函数作为obj的方法
 const arg=[...arguments].slice(1)   //第一个为obj所以删除,伪数组转为数组
 res=obj.fn(...arg)
 delete obj.fn   // 不删除会导致context属性越来越多
 return res
}
Nach dem Login kopieren
//用法:f.call(obj,arg1)
function f(a,b){
 console.log(a+b)
 console.log(this.name)
}
let obj={
 name:1
}
f.myCall(obj,1,2) //否则this指向window

obj.greet.call({name: 'Spike'}) //打出来的是 Spike
Nach dem Login kopieren

手写apply(arguments[this, [参数1,参数2.....] ])

Function.prototype.myApply=function(context){  // 箭头函数从不具有参数对象!!!!!这里不能写成箭头函数
 let obj=context||window
 obj.fn=this
 const arg=arguments[1]||[]    //若有参数,得到的是数组
 let res=obj.fn(...arg)
 delete obj.fn
 return res
} 
function f(a,b){
 console.log(a,b)
 console.log(this.name)
}
let obj={
 name:'张三'
}
f.myApply(obj,[1,2])  //arguments[1]
Nach dem Login kopieren

手写bind

this.value = 2
var foo = {
 value: 1
};
var bar = function(name, age, school){
 console.log(name) // 'An'
 console.log(age) // 22
 console.log(school) // '家里蹲大学'
}
var result = bar.bind(foo, 'An') //预置了部分参数'An'
result(22, '家里蹲大学') //这个参数会和预置的参数合并到一起放入bar中
Nach dem Login kopieren

简单版本

Function.prototype.bind = function(context, ...outerArgs) {
 var fn = this;
 return function(...innerArgs) {   //返回了一个函数,...rest为实际调用时传入的参数
 return fn.apply(context,[...outerArgs, ...innerArgs]);  //返回改变了this的函数,
 //参数合并
 }
}
Nach dem Login kopieren

new失败的原因:

例:

// 声明一个上下文
let thovino = {
 name: 'thovino'
}

// 声明一个构造函数
let eat = function (food) {
 this.food = food
 console.log(`${this.name} eat ${this.food}`)
}
eat.prototype.sayFuncName = function () {
 console.log('func name : eat')
}

// bind一下
let thovinoEat = eat.bind(thovino)
let instance = new thovinoEat('orange')  //实际上orange放到了thovino里面
console.log('instance:', instance) // {}
Nach dem Login kopieren

生成的实例是个空对象

new操作符执行时,我们的thovinoEat函数可以看作是这样:

function thovinoEat (...innerArgs) {
 eat.call(thovino, ...outerArgs, ...innerArgs)
}
Nach dem Login kopieren

在new操作符进行到第三步的操作thovinoEat.call(obj, ...args)时,这里的obj是new操作符自己创建的那个简单空对象{},但它其实并没有替换掉thovinoEat函数内部的那个上下文对象thovino。这已经超出了call的能力范围,因为这个时候要替换的已经不是thovinoEat函数内部的this指向,而应该是thovino对象。

换句话说,我们希望的是new操作符将eat内的this指向操作符自己创建的那个空对象。但是实际上指向了thovinonew操作符的第三步动作并没有成功

可new可继承版本

Function.prototype.bind = function (context, ...outerArgs) {
 let that = this;

function res (...innerArgs) {
     if (this instanceof res) {
         // new操作符执行时
         // 这里的this在new操作符第三步操作时,会指向new自身创建的那个简单空对象{}
         that.call(this, ...outerArgs, ...innerArgs)
     } else {
         // 普通bind
         that.call(context, ...outerArgs, ...innerArgs)
     }
     }
     res.prototype = this.prototype //!!!
     return res
}
Nach dem Login kopieren

9. 手动实现new

new的过程文字描述:

  • 创建一个空对象 obj;

  • 将空对象的隐式原型(proto)指向构造函数的prototype。

  • 使用 call 改变 this 的指向

  • 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。

function Person(name,age){
 this.name=name
 this.age=age
}
Person.prototype.sayHi=function(){
 console.log('Hi!我是'+this.name)
}
let p1=new Person('张三',18)

////手动实现new
function create(){
 let obj={}
 //获取构造函数
 let fn=[].shift.call(arguments)  //将arguments对象提出来转化为数组,arguments并不是数组而是对象    !!!这种方法删除了arguments数组的第一个元素,!!这里的空数组里面填不填元素都没关系,不影响arguments的结果      或者let arg = [].slice.call(arguments,1)
 obj.__proto__=fn.prototype
 let res=fn.apply(obj,arguments)    //改变this指向,为实例添加方法和属性
 //确保返回的是一个对象(万一fn不是构造函数)
 return typeof res==='object'?res:obj
}

let p2=create(Person,'李四',19)
p2.sayHi()
Nach dem Login kopieren

细节:

[].shift.call(arguments)  也可写成:
 let arg=[...arguments]
 let fn=arg.shift()  //使得arguments能调用数组方法,第一个参数为构造函数
 obj.__proto__=fn.prototype
 //改变this指向,为实例添加方法和属性
 let res=fn.apply(obj,arg)
Nach dem Login kopieren

10. 手写promise(常见promise.all, promise.race)

// Promise/A+ 规范规定的三种状态
const STATUS = {
 PENDING: 'pending',
 FULFILLED: 'fulfilled',
 REJECTED: 'rejected'
}

class MyPromise {
 // 构造函数接收一个执行回调
 constructor(executor) {
     this._status = STATUS.PENDING // Promise初始状态
     this._value = undefined // then回调的值
     this._resolveQueue = [] // resolve时触发的成功队列
     this._rejectQueue = [] // reject时触发的失败队列
    
 // 使用箭头函数固定this(resolve函数在executor中触发,不然找不到this)
 const resolve = value => {
     const run = () => {
         // Promise/A+ 规范规定的Promise状态只能从pending触发,变成fulfilled
         if (this._status === STATUS.PENDING) {
             this._status = STATUS.FULFILLED // 更改状态
             this._value = value // 储存当前值,用于then回调
            
             // 执行resolve回调
             while (this._resolveQueue.length) {
                 const callback = this._resolveQueue.shift()
                 callback(value)
             }
         }
     }
     //把resolve执行回调的操作封装成一个函数,放进setTimeout里,以实现promise异步调用的特性(规范上是微任务,这里是宏任务)
     setTimeout(run)
 }

 // 同 resolve
 const reject = value => {
     const run = () => {
         if (this._status === STATUS.PENDING) {
         this._status = STATUS.REJECTED
         this._value = value
        
         while (this._rejectQueue.length) {
             const callback = this._rejectQueue.shift()
             callback(value)
         }
     }
 }
     setTimeout(run)
 }

     // new Promise()时立即执行executor,并传入resolve和reject
     executor(resolve, reject)
 }

 // then方法,接收一个成功的回调和一个失败的回调
 function then(onFulfilled, onRejected) {
  // 根据规范,如果then的参数不是function,则忽略它, 让值继续往下传递,链式调用继续往下执行
  typeof onFulfilled !== 'function' ? onFulfilled = value => value : null
  typeof onRejected !== 'function' ? onRejected = error => error : null

  // then 返回一个新的promise
  return new MyPromise((resolve, reject) => {
    const resolveFn = value => {
      try {
        const x = onFulfilled(value)
        // 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
        x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
      } catch (error) {
        reject(error)
      }
    }
  }
}

  const rejectFn = error => {
      try {
        const x = onRejected(error)
        x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
      } catch (error) {
        reject(error)
      }
    }

    switch (this._status) {
      case STATUS.PENDING:
        this._resolveQueue.push(resolveFn)
        this._rejectQueue.push(rejectFn)
        break;
      case STATUS.FULFILLED:
        resolveFn(this._value)
        break;
      case STATUS.REJECTED:
        rejectFn(this._value)
        break;
    }
 })
 }
 catch (rejectFn) {
  return this.then(undefined, rejectFn)
}
// promise.finally方法
finally(callback) {
  return this.then(value => MyPromise.resolve(callback()).then(() => value), error => {
    MyPromise.resolve(callback()).then(() => error)
  })
}

 // 静态resolve方法
 static resolve(value) {
      return value instanceof MyPromise ? value : new MyPromise(resolve => resolve(value))
  }

 // 静态reject方法
 static reject(error) {
      return new MyPromise((resolve, reject) => reject(error))
    }

 // 静态all方法
 static all(promiseArr) {
      let count = 0
      let result = []
      return new MyPromise((resolve, reject) =>       {
        if (!promiseArr.length) {
          return resolve(result)
        }
        promiseArr.forEach((p, i) => {
          MyPromise.resolve(p).then(value => {
            count++
            result[i] = value
            if (count === promiseArr.length) {
              resolve(result)
            }
          }, error => {
            reject(error)
          })
        })
      })
    }

 // 静态race方法
 static race(promiseArr) {
      return new MyPromise((resolve, reject) => {
        promiseArr.forEach(p => {
          MyPromise.resolve(p).then(value => {
            resolve(value)
          }, error => {
            reject(error)
          })
        })
      })
    }
}
Nach dem Login kopieren

11. 手写原生AJAX

步骤

  • 创建 XMLHttpRequest 实例

  • 发出 HTTP 请求

  • 服务器返回 XML 格式的字符串

  • JS 解析 XML,并更新局部页面

不过随着历史进程的推进,XML 已经被淘汰,取而代之的是 JSON。

了解了属性和方法之后,根据 AJAX 的步骤,手写最简单的 GET 请求。

version 1.0:

myButton.addEventListener('click', function () {
  ajax()
})

function ajax() {
  let xhr = new XMLHttpRequest() //实例化,以调用方法
  xhr.open('get', 'https://www.google.com')  //参数2,url。参数三:异步
  xhr.onreadystatechange = () => {  //每当 readyState 属性改变时,就会调用该函数。
    if (xhr.readyState === 4) {  //XMLHttpRequest 代理当前所处状态。
      if (xhr.status >= 200 && xhr.status <p>promise实现</p><pre class="brush:php;toolbar:false">function ajax(url) {
  const p = new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onreadystatechange = () => {
      if (xhr.readyState == 4) {
        if (xhr.status >= 200 && xhr.status  console.log(res))
  .catch(reason => console.log(reason))
Nach dem Login kopieren

12. 手写节流防抖函数

函数节流与函数防抖都是为了限制函数的执行频次,是一种性能优化的方案,比如应用于window对象的resize、scroll事件,拖拽时的mousemove事件,文字输入、自动完成的keyup事件。
Nach dem Login kopieren

节流:连续触发事件但是在 n 秒中只执行一次函数

例:(连续不断动都需要调用时用,设一时间间隔),像dom的拖拽,如果用消抖的话,就会出现卡顿的感觉,因为只在停止的时候执行了一次,这个时候就应该用节流,在一定时间内多次执行,会流畅很多。

防抖:指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

例:(连续不断触发时不调用,触发完后过一段时间调用),像仿百度搜索,就应该用防抖,当我连续不断输入时,不会发送请求;当我一段时间内不输入了,才会发送一次请求;如果小于这段时间继续输入的话,时间会重新计算,也不会发送请求。

防抖的实现:

function debounce(fn, delay) {
     if(typeof fn!=='function') {
        throw new TypeError('fn不是函数')
     }
     let timer; // 维护一个 timer
     return function () {
         var _this = this; // 取debounce执行作用域的this(原函数挂载到的对象)
         var args = arguments;
         if (timer) {
            clearTimeout(timer);
         }
         timer = setTimeout(function () {
            fn.apply(_this, args); // 用apply指向调用debounce的对象,相当于_this.fn(args);
         }, delay);
     };
}

// 调用
input1.addEventListener('keyup', debounce(() => {
 console.log(input1.value)
}), 600)
Nach dem Login kopieren

节流的实现:

function throttle(fn, delay) {
  let timer;
  return function () {
    var _this = this;
    var args = arguments;
    if (timer) {
      return;
    }
    timer = setTimeout(function () {
      fn.apply(_this, args); // 这里args接收的是外边返回的函数的参数,不能用arguments
      // fn.apply(_this, arguments); 需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。
      timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
    }, delay)
  }
}

p1.addEventListener('drag', throttle((e) => {
  console.log(e.offsetX, e.offsetY)
}, 100))
Nach dem Login kopieren

13. 手写Promise加载图片

function getData(url) {
  return new Promise((resolve, reject) => {
    $.ajax({
      url,
      success(data) {
        resolve(data)
      },
      error(err) {
        reject(err)
      }
    })
  })
}
const url1 = './data1.json'
const url2 = './data2.json'
const url3 = './data3.json'
getData(url1).then(data1 => {
  console.log(data1)
  return getData(url2)
}).then(data2 => {
  console.log(data2)
  return getData(url3)
}).then(data3 =>
  console.log(data3)
).catch(err =>
  console.error(err)
)
Nach dem Login kopieren

14. 函数实现一秒钟输出一个数

for(let i=0;i{
    console.log(i);
 },1000*i)
}
Nach dem Login kopieren

15. 创建10个标签,点击的时候弹出来对应的序号?

var a
for(let i=0;i'
 a.addEventListener('click',function(e){
     console.log(this)  //this为当前点击的<a>
     e.preventDefault()  //如果调用这个方法,默认事件行为将不再触发。
     //例如,在执行这个方法后,如果点击一个链接(a标签),浏览器不会跳转到新的 URL 去了。我们可以用 event.isDefaultPrevented() 来确定这个方法是否(在那个事件对象上)被调用过了。
     alert(i)
 })
 const d=document.querySelector('p')
 d.appendChild(a)  //append向一个已存在的元素追加该元素。
}</a>
Nach dem Login kopieren
Verwandte Etiketten:
Quelle:segmentfault.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!