前端手写篇

Object.getPrototypeOf是一个JavaScript内置函数,用于获取指定对象的原型

Object.create() 是 JavaScript 中用来创建一个新对象,并且可以将其设置为继承自另一个对象的原型对象

1.实现防抖函数(debounce)

防抖函数原理:把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算。

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

手写简化版:

// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
  // 缓存一个定时器id
  let timer = 0
  // 这里返回的函数是每次用户实际调用的防抖函数
  // 如果已经设定过定时器了就清空上一次的定时器
  // 开始一个新的定时器,延迟执行用户传入的方法
  return function(...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      func.apply(this, args)
    }, wait)
  }
}

 

 

适用场景:

  • 文本输入的验证,连续输入文字后发送 AJAX 请求进行验证,验证一次就好。
  • 按钮提交场景:防止多次提交按钮,只执行最后提交的一次。
  • 服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似。

2.实现节流函数(throttle)

节流函数原理:指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的时间才会执行回调函数。总结起来就是:事件,按照一段时间的间隔来进行触发

像dom的拖拽,如果用防抖的话,就会出现卡顿的感觉,因为只在停止的时候执行了一次,这个时候就应该用节流,在一定时间内多次执行,会流畅很多

手写简版

使用时间戳的节流函数会在第一次触发事件时立即执行,以后每过 wait 秒之后才执行一次,并且最后一次触发事件不会被执行。

时间戳方式:

    const throttle = (func, wait = 50) => {
            let lastTime = 0;
            return function (...args) {
                let now = new Date();
                if (now - lastTime > wait) {
               
                    lastTime = now;
                    func.apply(this,args)
                }
            }
        }
        setInterval(
            throttle(()=>{
                console.log('1')
            },1000)
        ,1)

定时器方式:

使用定时器的节流函数在第一次触发时不会执行,而是在 delay 秒之后才执行,当最后一次停止触发后,还会再执行一次函数

  const throttle1 =(func, wait = 50) => {
            let timer = null ;
            return function(...args){
                if(!timer){
                    timer = setTimeout(()=>{
                        func.apply(this,args);
                        timer = null 
                    },wait)
                }
            }
        }

        setInterval(
            throttle1(()=>{
                console.log('1')
            },1000)
        ,1)

适用场景:

  • DOM 元素的拖拽功能实现(mousemove
  • 搜索联想(keyup
  • 计算鼠标移动的距离(mousemove
  • Canvas 模拟画板功能(mousemove
  • 监听滚动事件判断是否到页面底部自动加载更多
  • 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
  • 缩放场景:监控浏览器resize
  • 动画场景:避免短时间内多次触发动画引起性能问题

总结

  • 函数防抖:将几次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
  • 函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。

3 实现instanceOf

  • 步骤1:先取得当前类的原型,当前实例对象的原型链
  • ​步骤2:一直循环(执行原型链的查找机制)
    • 取得当前实例对象原型链的原型链(proto = proto.__proto__,沿着原型链一直向上查找)。
    • 如果 当前实例的原型链__proto__上找到了当前类的原型prototype,则返回 true。
    • 如果 一直找到Object.prototype.__proto__ == nullObject的基类(null)上面都没找到,则返回 false。
 // 实例.__proto__ = 类.prototype
        function _instanceof(example, classFunc) {
            // 基本数据类型直接返回false
            if (typeof example !== 'object' || example === null) return false;
            let proto = Object.getPrototypeOf(example);
            while (true) {
                if (proto == null) return false;
                if (proto == classFunc.prototype); return true
                proto = Object.getPrototypeOf(proto)
            }
        }
        console.log('test', _instanceof(null, Array)) // false
        console.log('test', _instanceof([], Array)) // true
        console.log('test', _instanceof('', Array)) // false
        console.log('test', _instanceof({}, Object)) // true

4.实现new的过程

new操作符做了这些事:

  • 创建一个全新的对象
  • 这个对象的__proto__要指向构造函数的原型prototype
  • 执行构造函数,使用 call/apply 改变 this 的指向
  • 返回值为object类型则作为new方法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) {
  // 基于原型链 创建一个新对象
  let newObj = Object.create(fn.prototype);
  // 添加属性到新对象上 并获取obj函数的结果
  let res = fn.apply(newObj, args); // 改变this指向

  // 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
  return typeof res === 'object' ? res: newObj;
}

// 用法
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.say = function() {
  console.log(this.age);
};
let p1 = myNew(Person, "poety", 18);
console.log(p1.name);
console.log(p1);
p1.say();


 

 

5.实现call方法

call做了什么:

  • 将函数设为对象的属性
  • 执行和删除这个函数
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数,默认指向为 window
Function.prototype.myCall = function (context = window, ...args) {
            if (typeof this !== 'function') {
                throw new Error('type error')
            }
            let key = Symbol('key');
            // this就是fn
            // this() fn()
            context[key] = this;
            // 利用context进行调用
            let result = context[key](...args);
            delete context[key];
            return result
        }

        //用法: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

6.实现apply方法

思路: 利用this的上下文特性。apply其实就是改一下参数的问题

Function.prototype.myApply = function (context = window, args) {
            if (typeof this !== 'function') {
                throw new Error('type Error')
            }
            let key = Symbol('key')
            context[key] = this;
            let result = context[key](...args)
            delete context[key]
            return result
        }
        // 使用
        function f(a, b) {
            console.log(a, b)
            console.log(this.name)
        }
        let obj = {
            name: '张三'
        }
        f.myApply(obj, [1, 2]) //arguments[1]

7.实现bind方法

bind 的实现对比其他两个函数略微地复杂了一点,涉及到参数合并(类似函数柯里化),因为 bind 需要返回一个函数,需要判断一些边界问题,以下是 bind 的实现

Function.prototype.myBind = function (context, ...args1) {
  let fn = this;
  return function (...args2) {
    return fn.apply(context, [...args1, ...args2]);
  };
};

8.实现深拷贝

简单版:

const newObj = JSON.parse(JSON.stringify(oldObj));

局限性:

  • 他无法实现对函数 、RegExp等特殊对象的克隆,因为在JSON中,函数和RegExp等特殊对象的值会被转化为 null
  • 会抛弃对象的constructor,所有的构造函数会指向Object,由于 JSON.stringify() 会把对象转化为 JSON 字符串,再通过 JSON.parse() 解析成对象,这个过程中会丢失对象的 constructor 属性,导致所有对象的 constructor 都指向 Object。
  • 对象有循环引用,会报错,当被克隆的对象中存在循环引用时,JSON.stringify() 会抛出异常,因为 JSON 格式不支持循环引用。例如,如果对象A中引用了对象B,而对象B又引用了对象A,那么 JSON.stringify(A) 会抛出异常。

面试简版

function deepClone(obj) {
    // 如果是 值类型 或 null,则直接return
    if(typeof obj !== 'object' || obj === null) {
      return obj
    }
    
    // 定义结果对象
    let copy = {}
    
    // 如果对象是数组,则定义结果数组
    if(obj.constructor === Array) {
      copy = []
    }
    
    // 遍历对象的key
    for(let key in obj) {
        // 如果key是对象的自有属性
        if(obj.hasOwnProperty(key)) {
          // 递归调用深拷贝方法
          copy[key] = deepClone(obj[key])
        }
    }
    
    return copy
} 
调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历。这就是我们在解这一类题时的核心的方法。

进阶版

  • 解决拷贝循环引用问题
  • 解决拷贝对应原型问题
function deepClone(value, hash = new WeakMap) {
            // 如果是值类型或者null ,则直接return
            if (value == null) return value
            if (value instanceof RegExp) return new RegExp(value);
            if (value instanceof Date) return new Date(value);
            if (typeof value !== 'object') {
                return value
            }
            let obj = new value.constructor();
            // 说明是一个对象类型 
            if (hash.get(value)) {
                return hash.get(value)
            }
            hash.set(value, obj);
            // 遍历对象的key
            for (let key in value) {
                // 如果key是对象自有属性
                if (value.hasOwnProperty(key)) {
                    obj[key] = deepClone(value[key], hash)
                }
            }
            return obj
        }
        var o = {};
        o.x = o;
        var o1 = deepClone(o); // 如果这个对象拷贝过了 就返回那个拷贝的结果就可以了
        console.log(o1);

9.实现类的继承

1. 寄生组合继承

  function Parent(name) {
            this.name = name;
        }
        Parent.prototype.say = function () {
            console.log(this.name + ` say`);
        }
        Parent.prototype.play = function () {
            console.log(this.name + ` play`);
        }

        function Child(name, parent) {
            // 
            Parent.call(this, parent);
            this.name = name;
        }
        Child.prototype = Object.create(Parent.prototype)
        Child.prototype.say = Parent.prototype.play = function () {
            console.log(this.name + ` play`);
        }
        // 注意记得把子类的构造指向子类本身
        Child.prototype.constructor = Child;

        var parent = new Parent('parent');
        parent.say()

        var child = new Child('child');
        child.say()
        child.play(); // 继承父类的方法

10.实现Promise相关方法

1.实现Promise相关方法

实现 resolve 静态方法有三个要点:

  • 传参为一个 Promise, 则直接返回它。
const promise1 = Promise.resolve('resolved');

const resultPromise = Promise.resolve(promise1);

console.log(resultPromise === promise1); // true

在这个例子中,promise1 是一个已经 resolved 的 Promise 对象,调用 Promise.resolve 方法时传入了 promise1 对象,因此返回的 Promise 对象直接就是 promise1 对象本身,而不是新创建的一个 Promise 对象。因此,resultPromisepromise1 引用同一个对象,它们完全相等

  • 传参为一个 thenable 对象,返回的 Promise 会跟随这个对象,采用它的最终状态作为自己的状态。
const thenable = {
  then(resolve, reject) {
    setTimeout(() => {
      resolve('resolved from thenable');
    }, 1000);
  }
};

const resultPromise = Promise.resolve(thenable);

resultPromise.then(value => {
  console.log(value); // resolved from thenable
});

在这个例子中,thenable 对象具有 then 方法,因此可以认为它是一个 thenable 对象。调用 Promise.resolve 方法时传入了 thenable 对象,因此返回的 Promise 对象会跟随 thenable 对象,等待它的状态发生变化。在 thenable 对象的 then 方法中,我们使用了 setTimeout 来模拟异步操作,1 秒后将其状态设置为 resolved,这时候返回的 Promise 对象的状态也会相应地变为 resolved,成功回调函数会被调用,并输出 resolved from thenable

  • 其他情况,直接返回以该值为成功状态的promise对象。
const resultPromise = Promise.resolve('resolved');

resultPromise.then(value => {
  console.log(value); // resolved
});

调用 Promise.resolve 方法时传入了一个普通的字符串 'resolved',因此返回的 Promise 对象的状态会直接被设置为 resolved,成功回调函数会被调用,并输出 'resolved'

Promise.reject = (param) => {
            // 如果 param 是一个 Promise 对象,则直接返回该对象,不需要再创建新的 Promise 对象。
            if (param instanceof Promise) return param;
            // 如果 param 是一个 thenable 对象,则返回一个 Promise 对象,该对象的状态会跟随 param 对象,采用它的最终状态作为自己的状态。
            if(param && param.then && typeof param.then==='function'){
                // param 状态变为成功会调用resolve,将新 Promise 的状态变为成功,反之亦然
                param.then(resolve,reject)
            }else{
                // 如果 param 不是 Promise 对象也不是 thenable 对象,则返回一个以 param 为成功状态的 Promise 对象。 
                resolve(param)
            }
        }

2 实现 Promise.reject

Promise.reject 中传入的参数会作为一个 reason 原封不动地往下传, 实现如下:

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    });
}

3 实现 Promise.prototype.finally

  1. 无论 Promise 的状态是成功还是失败,finally 方法中的回调函数都会被执行。
  2. 如果 finally 回调函数中返回的 Promise 对象被 reject,且前面没有捕获该错误的处理函数,那么该错误会传递到后面的 thenerr 处理函数中。

    如果 finally 回调函数中返回的 Promise 对象被 resolve,那么它将不会影响原来 Promise 对象的状态和值,也不会改变后面的 then 的状态和值。

  3. 如果 finally 方法中有异步操作,如 Promise,它会等待异步操作完成后再将原 Promise 对象的状态传递给下一个 then 方法。也就是说,如果 finally 方法中包含异步操作,后面的 then 方法会等待它们全部执行完毕,再将原 Promise 对象的状态传递给下一个 then 方法。
  Promise.prototype.finally = function (callback) {
            //返回一个新的promise对象
            return this.then((data) => {
                return Promise.resolve(callback()).then(() => data)
            }, err => {
                return Promise.resolve(callback()).then(() => {
                    throw err
                })
            })
        }

4 实现 Promise.all

1.如果传入参数为一个空的可迭代对象,则直接进行 resolve。

如果传入的可迭代对象是空的,即没有任何元素,Promise.all 方法会立即返回一个已完成的 Promise 对象,其值为一个空数组。例如:

Promise.all([]).then((result) => {
  console.log(result); // []
});

2.如果参数中有一个 promise 失败,那么 Promise.all 返回的 promise 对象失败。

Promise.all([
  Promise.resolve(1),
  Promise.reject(new Error("Error")),
  Promise.resolve(3)
]).catch((error) => {
  console.log(error); // Error: Error
});

3.在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组。

Promise.all([
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3)
]).then((result) => {
  console.log(result); // [1, 2, 3]
});
Promise.all = function (promises) {
            return new Promise((resolve, reject) => {
                // 存储每一个 promise 对象的解决结果
                let result = [];
                // 计数器 index 来记录已解决的 promise 数量。
                let index = 0;
                let len = promises.length;
                if (len === 0) {
                    resolve(result)
                    return;
                }
                for (let i = 0; i < len; i++) {
                    Promise.resolve(promises[i]).then(data => {
                        result[i] = data;
                        index++
                        if (index === len) resolve(result)
                    }).catch(err => {
                        reject(err)
                    })
                }

            })
        }

5 实现promise.allsettle

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise`结果

假设有三个异步函数 fetchData1(), fetchData2(), fetchData3() 分别用来从服务器获取数据,这些函数都返回一个 Promise 对象。我们想要同时获取这三个异步操作的结果,并根据结果采取不同的操作,我们可以使用 Promise.allSettled() 方法来实现。

const promises = [fetchData1(), fetchData2(), fetchData3()];

Promise.allSettled(promises)
  .then(results => {
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log(`获取数据成功: ${result.value}`);
      } else {
        console.log(`获取数据失败: ${result.reason}`);
      }
    });
  });

在这个例子中,我们创建了一个 Promise 对象数组 promises,其中包含了三个异步函数的返回值。然后,我们调用 Promise.allSettled(promises) 方法来等待这三个异步操作都完成。当所有的异步操作完成后,Promise.allSettled() 方法返回一个 Promise 对象,该对象带有一个结果数组,其中每个结果表示对应的 Promise 对象的执行结果。

.then() 方法中,我们遍历了结果数组,并根据每个结果的 status 属性来判断对应的 Promise 对象是否成功执行。如果成功执行,则打印出获取数据成功的信息,否则打印出获取数据失败的信息。

function isPromise (val) {
  return typeof val.then === 'function'; // (123).then => undefined
}

Promise.allSettled = function(promises) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let times = 0;
    const setData = (index, data) => {
      arr[index] = data;
      if (++times === promises.length) {
        resolve(arr);
      }
      console.log('times', times)
    }

    for (let i = 0; i < promises.length; i++) {
      let current = promises[i];
      if (isPromise(current)) {
        current.then((data) => {
          setData(i, { status: 'fulfilled', value: data });
        }, err => {
          setData(i, { status: 'rejected', value: err })
        })
      } else {
        setData(i, { status: 'fulfilled', value: current })
      }
    }
  })
}

6 实现 Promise.race

race 的实现相比之下就简单一些,只要有一个 promise 执行完,直接 resolve 并停止执行

Promise.race = function(promises) {
  return new Promise((resolve, reject) => {
    let len = promises.length;
    if(len === 0) return;
    for(let i = 0; i < len; i++) {
      Promise.resolve(promise[i]).then(data => {
        resolve(data);
        return;
      }).catch(err => {
        reject(err);
        return;
      })
    }
  })
}

7.实现一个Promise

11 实现发布订阅模式

发布订阅者模式,一种对象间一对多的依赖关系,但一个对象的状态发生改变时,所依赖它的对象都将得到状态改变的通知。

主要的作用(优点):

  1. 广泛应用于异步编程中(替代了传递回调函数)
  2. 对象之间松散耦合的编写代码

缺点:

  • 创建订阅者本身要消耗一定的时间和内存
  • 多个发布者和订阅者嵌套一起的时候,程序难以跟踪维护

实现的思路:

  • 创建一个对象(缓存列表)
  • on方法用来把回调函数fn都加到缓存列表中
  • emit 根据key值去执行对应缓存列表中的函数
  • off方法可以根据key值取消订阅
   class EventEmiter {
            constructor() {

                this._events = {}
            }
            // 订阅事件的方法
            on(eventName, callback) {
                if (!this._events) {
                    this._events = {}
                }
                // 合并之前订阅的cb
                this._events[eventName] = [...(this._events[eventName] || []), callback]
            }
            // 触发事件的方法
            emit(eventName, ...args) {
                if (!this._events[eventName]) {
                    return
                }
                // 遍历执行所有订阅的事件
                this._events[eventName].forEach(fn => {
                    fn(...args)
                });
            }
            off(eventName, cb) {
                if (!this._events[eventName]) {
                    return
                }
                // 遍历执行所有订阅的事件
                this._events[eventName] = this._events[eventName].filter(fn => fn != cb && fn.l != cb)
            }
            once(eventName, callback) {
                const one = (...args) => {
                    // 等callback执行完毕在删除
                    callback(args)
                    this.off(eventName, one)
                }
                one.l = callback // 自定义属性
                this.on(eventName, one)
            }

        }

        let event = new EventEmiter();
        let login1 = function (...args) {
            console.log('login success1', args)
        }
        let login2 = function (...args) {
            console.log('login success2', args)
        }
        event.on('login', login1)
        // 
        event.once('login', login2)

        event.off('login', login1) // 解除订阅
        event.emit('login', 1, 2, 3, 4, 5)
        event.emit('login', 6, 7, 8, 9)
        event.emit('login', 10, 11, 12)

发布订阅者模式和观察者模式的区别?

  • 发布/订阅模式是观察者模式的一种变形,两者区别在于,发布/订阅模式在观察者模式的基础上,在目标和观察者之间增加一个调度中心。
  • 观察者模式是由具体目标调度,比如当事件触发,Subject 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。
  • 发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。

12.实现观察者模式

观察者模式(基于发布订阅模式) 有观察者,也有被观察者

观察者需要放到被观察者中,被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式,收集观察者,状态变化后要主动通知观察者

 class Subject { // 被观察者 学生
            constructor() {
                this.state = 'happy'
                this.observers = []; // 存储所有的观察者
            }
            // 收集所有的观察者
            attach(o) {
                this.observers.push(o)
            }
            // 更新被观察者 状态的方法
            setState(newState) {
                this.state = newState; // 更新状态
                // this 指被观察者 学生
                this.observers.forEach(o => o.update(this)) // 通知观察者 更新它们的状态
            }
        }
        class Observer { // 观察者 父母和老师
            constructor(name) {
                this.name = name;
            }
            update(student) {
                console.log('当前' + this.name + '被通知了', '当前学生的状态是' + student.state)
            }
        }
        let student = new Subject('学生');

        let parent = new Observer('父母');
        let teacher = new Observer('老师');
        // 被观察者存储观察者的前提,需要先接纳观察者
        student.attach(parent);
        student.attach(teacher);
        student.setState('被欺负了')

13.实现单例模式

核心要点: 用闭包和Proxy属性拦截

使用闭包和 Proxy 属性拦截可以很好地实现单例模式。具体来说,我们可以通过闭包来确保只创建一个实例,然后使用 Proxy 属性拦截来防止对该实例进行不必要的操作或修改

在实现中,我们可以先将类的构造函数定义为私有属性,并通过闭包来创建一个实例。然后,我们可以使用 Proxy 对象来拦截对该实例的属性读取、赋值和删除操作,以确保只有一个实例并且不被修改。最后,我们可以将实例作为单例对象的公共属性,使其可以被全局访问。

   const Singleton = (function () {
            let instance = null;
            const SingletonClass = function () {
                // ...私有属性和方法
            }
            // construct 方法用于拦截 SingletonClass 的构造函数,
            // 它检查是否已经创建了实例,如果没有则创建实例,否则返回已经存在的实例。
            return new Proxy(SingletonClass, {
                construct(target, args) {
                    if (!instance) {
                        instance = new target(...args);
                    }
                    return instance;
                },
                get(target, prop) {
                    return instance[prop];
                },
                set(target, prop, value) {
                    // 防止修改属性
                    throw new Error('Cannot modify singleton instance');
                },
                deleteProperty(target, prop) {
                    // 防止删除属性
                    throw new Error('Cannot delete singleton instance property');
                }
            })
        })()
        // 使用示例
        const s1 = new Singleton(); // 创建单例实例
        const s2 = new Singleton(); // 获取已有单例实例
        console.log(s1 === s2); // true,两个实例是同一个对象
        s1.foo = 'bar'; // 不能修改属性,会抛出错误
        console.log(s1.foo); // undefined
        delete s1.foo; // 不能删除属性,会抛出错误

14 实现Ajax

步骤

  • 创建 XMLHttpRequest 实例
  • 发出 HTTP 请求
  • 服务器返回 XML 格式的字符串
  • JS 解析 XML,并更新局部页面
  • 不过随着历史进程的推进,XML 已经被淘汰,取而代之的是 JSON。
function ajax() {
            let xhr = new XMLHttpRequest();
            xhr.open('get', 'http://127.0.0.1')
            xhr.onreadystatechange = (request, resonese) => {
                console.log(xhr.readyState)
                if (xhr.readyState === 4) {
                    console.log(xhr.status)
                    if (xhr.status >= 200 && xhr.status < 300) {
                        console.log('成功')
                        let string = xhr.responseText
                        //JSON.parse() 方法用来解析JSON字符串,构造由字符串描述的JavaScript值或对象
                        let object = JSON.parse(string)
                    }
                }
            }
      //参数2,url。参数三:异步
            xhr.send() //用于实际发出 HTTP 请求。不带参数为GET请求
        }

Promise实现

基于Promise封装Ajax

  • 返回一个新的Promise实例
  • 创建HMLHttpRequest异步对象
  • 调用open方法,打开url,与服务器建立链接(发送前的一些处理)
  • 监听Ajax状态信息
  • 如果xhr.readyState == 4(表示服务器响应完成,可以获取使用服务器的响应了)
    • xhr.status == 200,返回resolve状态
    • xhr.status == 404,返回reject状态
  • xhr.readyState !== 4,把请求主体的信息基于send发送给服务器
function ajax(url) {
            return new Promise((resolve, reject) => {
                let xhr = new XMLHttpRequest();
                xhr.open('get', url)
                xhr.onreadystatechange = () => {
                    if (xhr.readyState == 4) {
                        if (xhr.status >= 200 && xhr.status <= 300) {
                            resolve(JSON.parse(xhr.responseText))
                        } else {
                            reject('请求出错')
                        }
                    }
                }
                xhr.send() //发送hppt请求
            })
        }
        let url = '/data.json'
        ajax(url).then(res => console.log(res))
            .catch(reason => console.log(reason))

15 实现JSONP方法

利用

你可能感兴趣的:(前端,javascript)