Maison > titres > le corps du texte

Un résumé de diverses écritures clés qui sont souvent testées lors des entretiens frontaux !

藏色散人
Libérer: 2021-01-12 14:29:38
avant
2813 Les gens l'ont consulté

Cet article couvre divers tests d'écriture manuscrite clés qui sont couramment testés lors des entretiens front-end.

Recommandé :
Résumé des questions d'entretien PHP 2021 (collection)
Résumé des questions d'entretien frontal 2021 (collection)

Il est recommandé de privilégier :

  • instanceof (pour examiner la compréhension de la chaîne de prototypes)
  • nouveau (pour créer une compréhension du processus d'instance d'objet)
  • call&apply&bind (compréhension de ce pointeur)
  • Promesse manuscrite (compréhension de l'asynchrone)
  • Ajax natif manuscrit (compréhension du principe ajax et requête http La compréhension de la méthode, en se concentrant sur la mise en œuvre des requêtes get et post)

1 instance manuscrite de

instance de fonction :

.

Déterminez si une instance est une instance de sa classe parent ou de son type ancêtre.

instanceof Pendant le processus de recherche, la chaîne prototype de la variable de gauche sera parcourue jusqu'à ce que le prototype de la variable de droite soit trouvé La recherche échoue et false

 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
Copier après la connexion
est renvoyé

2. Implémentez la méthode map du tableau

La méthode map() du tableau le fera. renvoie un nouveau tableau, et chaque élément de ce nouveau tableau correspond à la valeur de retour après avoir appelé une fois la fonction fournie sur l'élément de position correspondant dans le tableau d'origine.

Utilisation :

const a = [1, 2, 3, 4];
const b = array1.map(x => x * 2);
console.log(b);   // Array [2, 4, 6, 8]
Copier après la connexion

Implémentation native :

 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
 }
Copier après la connexion

3 réduire implémente la méthode map du tableau<.>

Utilisez la méthode de réduction intégrée du tableau pour implémenter la méthode map et tester la maîtrise du principe de réduction

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);
})
Copier après la connexion

4 La méthode de réduction du tableau manuscrit<.>reduce La méthode () reçoit une fonction comme accumulateur. Chaque valeur du tableau (de gauche à droite) commence à se réduire et devient finalement une autre nouvelle valeur du tableau. méthode de traitement des éléments dans ES5

Paramètres :

    rappel (une fonction appelée sur chaque élément du tableau, acceptant quatre fonctions :)
  • previousValue (La valeur de retour lors du dernier appel de la fonction de rappel, ou la valeur initiale)
    • currentValue (l'élément du tableau en cours de traitement)
    • currentIndex (l'index de l'élément du tableau en cours de traitement)
    • array (tableau pour appeler la méthode réduire())
    initialValue (valeur initiale facultative. Comme valeur passée à previousValue lorsque la fonction de rappel est appelée pour la première fois)
  •  function reduce(arr, cb, initialValue){
         var num = initValue == undefined? num = arr[0]: initValue;
         var i = initValue == undefined? 1: 0
         for (i; i
    Copier après la connexion
5. Aplatissement de tableau

L'aplatissement de tableau consiste à convertir des tableaux multidimensionnels en tableaux unidimensionnels.

1. Nouvelles méthodes fournies par es6 flat(profondeur)

let a = [1,[2,3]]; 
a.flat(); // [1,2,3] 
a.flat(1); //[1,2,3]
Copier après la connexion
En fait, il existe un moyen plus simple, sans connaître les dimensions du tableau, convertir directement le tableau cible en un tableau unidimensionnel. La valeur de la profondeur est définie sur Infini.

let a = [1,[2,3,[4,[5]]]]; 
a.flat(Infinity); // [1,2,3,4,5]  a是4维数组
Copier après la connexion

2. Utiliser cancat

function flatten(arr) {
     var res = [];
     for (let i = 0, length = arr.length; i 
Copier après la connexion

6. Fonction curry

La définition du curry : recevoir une partie Paramètres, renvoie une fonction pour recevoir les paramètres restants, et après avoir reçu suffisamment de paramètres, exécute la fonction d'origine.

Lorsque la fonction curry reçoit suffisamment de paramètres, elle exécutera la fonction d'origine. Comment déterminer quand suffisamment de paramètres sont atteints ?

Il y a deux idées :

    Obtenir le nombre de paramètres formels de la fonction via l'attribut length de la fonction. Le nombre de paramètres formels est le nombre requis. paramètres
  • Lors de l'appel de la fonction outil de curry, spécifiez manuellement le nombre de paramètres requis
  • Combiner ces deux points pour obtenir une fonction Curry simple :
/**
 * 将函数柯里化
 * @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)
         }
    }
}
Copier après la connexion

Vérifions-le :

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
Copier après la connexion

Notre bibliothèque d'outils couramment utilisée, lodash, fournit également la méthode curry et ajoute une fonction d'espace réservé très intéressante via des espaces réservés. Modifiez l'ordre des paramètres transmis.

Par exemple, si nous passons un espace réservé, les paramètres passés dans cet appel ignoreront l'espace réservé, et la position de l'espace réservé sera remplie par les paramètres du prochain appel, comme ceci :

Regardez directement l'exemple sur le site officiel :

Un résumé de diverses écritures clés qui sont souvent testées lors des entretiens frontaux !Réfléchissons ensuite à la façon d'implémenter la fonction d'espace réservé.

Pour la fonction curry de lodash, la fonction curry est montée sur l'objet lodash, donc l'objet lodash est utilisé comme espace réservé par défaut.

Notre fonction curry auto-implémentée elle-même n'est montée sur aucun objet, donc la fonction curry est utilisée comme espace réservé par défaut

L'utilisation d'espaces réservés consiste à modifier l'ordre de livraison des paramètres, donc dans le curry implémentation de la fonction, il est nécessaire d'enregistrer si un espace réservé est utilisé à chaque fois et d'enregistrer la position du paramètre représentée par l'espace réservé.

Allez directement au code :

/**
 * @param  fn           待柯里化的函数
 * @param  length       需要的参数个数,默认为函数的形参个数
 * @param  holder       占位符,默认当前柯里化函数
 * @return {Function}   柯里化后的函数
 */
function curry(fn,length = fn.length,holder = curry){
 return _curry.call(this,fn,length,holder,[],[])
}
/**
 * 中转函数
 * @param fn            柯里化的原函数
 * @param length        原函数需要的参数个数
 * @param holder        接收的占位符
 * @param args          已接收的参数列表
 * @param holders       已接收的占位符位置列表
 * @return {Function}   继续柯里化的函数 或 最终结果
 */
function _curry(fn,length,holder,args,holders){
 return function(..._args){
 //将参数复制一份,避免多次操作同一函数导致参数混乱
 let params = args.slice();
 //将占位符位置列表复制一份,新增加的占位符增加至此
 let _holders = holders.slice();
 //循环入参,追加参数 或 替换占位符
 _args.forEach((arg,i)=>{
 //真实参数 之前存在占位符 将占位符替换为真实参数
 if (arg !== holder && holders.length) {
     let index = holders.shift();
     _holders.splice(_holders.indexOf(index),1);
     params[index] = arg;
 }
 //真实参数 之前不存在占位符 将参数追加到参数列表中
 else if(arg !== holder && !holders.length){
     params.push(arg);
 }
 //传入的是占位符,之前不存在占位符 记录占位符的位置
 else if(arg === holder && !holders.length){
     params.push(arg);
     _holders.push(params.length - 1);
 }
 //传入的是占位符,之前存在占位符 删除原占位符位置
 else if(arg === holder && holders.length){
    holders.shift();
 }
 });
 // params 中前 length 条记录中不包含占位符,执行函数
 if(params.length >= length && params.slice(0,length).every(i=>i!==holder)){
 return fn.apply(this,params);
 }else{
 return _curry.call(this,fn,length,holder,params,_holders)
 }
 }
}
Copier après la connexion

Vérifiez-le : ;

let fn = function(a, b, c, d, e) {
 console.log([a, b, c, d, e]);
}

let _ = {}; // 定义占位符
let _fn = curry(fn,5,_);  // 将函数柯里化,指定所需的参数个数,指定所需的占位符

_fn(1, 2, 3, 4, 5);                 // print: 1,2,3,4,5
_fn(_, 2, 3, 4, 5)(1);              // print: 1,2,3,4,5
_fn(1, _, 3, 4, 5)(2);              // print: 1,2,3,4,5
_fn(1, _, 3)(_, 4,_)(2)(5);         // print: 1,2,3,4,5
_fn(1, _, _, 4)(_, 3)(2)(5);        // print: 1,2,3,4,5
_fn(_, 2)(_, _, 4)(1)(3)(5);        // print: 1,2,3,4,5
Copier après la connexion

À ce stade, nous avons complètement implémenté une fonction curry~~

7 . Implémenter la copie profonde

La différence entre la copie superficielle et la copie profonde :

Copie superficielle : un seul calque est copié et un objet plus profond- les références de niveau sont uniquement copiées

Copie approfondie : copiez plusieurs couches,

les données de chaque niveau seront copiées

. De cette façon, la modification de la valeur de copie n'affectera pas les autres objetsMéthode de copie superficielle ES6 : Object.assign(target,...sources)

let obj={
 id:1,
 name:'Tom',
 msg:{
 age:18
 }
}
let o={}
//实现深拷贝  递归    可以用于生命游戏那个题对二维数组的拷贝,
//但比较麻烦,因为已知元素都是值,直接复制就行,无需判断
function deepCopy(newObj,oldObj){
     for(var k in oldObj){
         let item=oldObj[k]
         //判断是数组?对象?简单类型?
         if(item instanceof Array){
             newObj[k]=[]
             deepCopy(newObj[k],item)
         }else if(item instanceof Object){
             newObj[k]={}
             deepCopy(newObj[k],item)
         }else{  //简单数据类型,直接赋值
             newObj[k]=item
         }
     }
}
Copier après la connexion

Appel manuscrit, postuler, lier

手写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
}
Copier après la connexion
//用法: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
Copier après la connexion

手写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]
Copier après la connexion

手写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中
Copier après la connexion

简单版本

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

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) // {}
Copier après la connexion

生成的实例是个空对象

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

function thovinoEat (...innerArgs) {
 eat.call(thovino, ...outerArgs, ...innerArgs)
}
Copier après la connexion

在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
}
Copier après la connexion

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()
Copier après la connexion

细节:

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

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)
          })
        })
      })
    }
}
Copier après la connexion

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))
Copier après la connexion

12. 手写节流防抖函数

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

节流:连续触发事件但是在 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)
Copier après la connexion

节流的实现:

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))
Copier après la connexion

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)
)
Copier après la connexion

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

for(let i=0;i{
    console.log(i);
 },1000*i)
}
Copier après la connexion

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>
Copier après la connexion

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal