思路:将传入的对象作为原型
function create(obj) { function F() {} F.prototype = obj return new F()}复制代码
instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
实现步骤:
首先获取类型的原型然后获得对象的原型然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 `null`,因为原型链最终为 `null`function myInstanceof(left, right) { let proto = Object.getPrototypeOf(left), // 获取对象的原型 prototype = right.prototype; // 获取构造函数的 prototype 对象 // 判断构造函数的 prototype 对象是否在对象的原型链上 while (true) { if (!proto) return false; if (proto === prototype) return true; proto = Object.getPrototypeOf(proto); }}复制代码
(1)首先创建了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
function myNew(fn, ...args) { // 判断参数是否是一个函数 if (typeof fn !== "function") { return console.error("type error"); } // 创建一个对象,并将对象的原型绑定到构造函数的原型上 const obj = Object.create(fn.prototype); const value = fn.apply(obj, args); // 调用构造函数,并且this绑定到obj上 // 如果构造函数有返回值,并且返回的是对象,就返回value ;否则返回obj return value instanceof Object ? value : obj; }复制代码
class MyPromise { constructor(fn){ // 存储 reslove 回调函数列表 this.callbacks = [] const resolve = (value) => { this.data = value // 返回值给后面的 .then while(this.callbacks.length) { let cb = this.callbacks.shift() cb(value) } } fn(resolve) } then(onResolvedCallback) { return new MyPromise((resolve) => { this.callbacks.push(() => { const res = onResolvedCallback(this.data) if (res instanceof MyPromise) { res.then(resolve) } else { resolve(res) } }) }) }}// 这是测试案例new MyPromise((resolve) => { setTimeout(() => { resolve(1) }, 1000)}).then((res) => { console.log(res) return new MyPromise((resolve) => { setTimeout(() => { resolve(2) }, 1000) })}).then(res =>{console.log(res)})复制代码
函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
// //防抖function debounce(fn, date) { let timer //声明接收定时器的变量 return function (...arg) { // 获取参数 timer && clearTimeout(timer) // 清空定时器 timer = setTimeout(() => { // 生成新的定时器 //因为箭头函数里的this指向上层作用域的this,所以这里可以直接用this,不需要声明其他的变量来接收 fn.apply(this, arg) // fn() }, date) }}//--------------------------------// 节流function debounce(fn, data) { let timer = +new Date() // 声明初始时间 return function (...arg) { // 获取参数 let newTimer = +new Date() // 获取触发事件的时间 if (newTimer - timer >= data) { // 时间判断,是否满足条件 fn.apply(this, arg) // 调用需要执行的函数,修改this值,并且传入参数 timer = +new Date() // 重置初始时间 } }}复制代码
call 函数的实现步骤:
判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
判断传入上下文对象是否存在,如果不存在,则设置为 window 。
处理传入的参数,截取第一个参数后的所有参数。
将函数作为上下文对象的一个属性。
使用上下文对象来调用这个方法,并保存返回结果。
删除刚才新增的属性。
返回结果。 // call函数实现Function.prototype.myCall = function(context) { // 判断调用对象 if (typeof this !== "function") { console.error("type error"); } // 获取参数 let args = [...arguments].slice(1), result = null; // 判断 context 是否传入,如果未传入则设置为 window context = context || window; // 将调用函数设为对象的方法 context.fn = this; // 调用函数 result = context.fn(...args); // 将属性删除 delete context.fn; return result;};复制代码
apply 函数的实现步骤:
判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
判断传入上下文对象是否存在,如果不存在,则设置为 window 。
将函数作为上下文对象的一个属性。
判断参数值是否传入
使用上下文对象来调用这个方法,并保存返回结果。
删除刚才新增的属性
返回结果 // apply 函数实现Function.prototype.myApply = function(context) { // 判断调用对象是否为函数 if (typeof this !== "function") { throw new TypeError("Error"); } let result = null; // 判断 context 是否存在,如果未传入则为 window context = context || window; // 将函数设为对象的方法 context.fn = this; // 调用方法 if (arguments[1]) { result = context.fn(...arguments[1]); } else { result = context.fn(); } // 将属性删除 delete context.fn; return result;};复制代码
bind 函数的实现步骤:
判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
保存当前函数的引用,获取其余传入参数值。
创建一个函数返回
函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 this
// bind 函数实现Function.prototype.myBind = function(context) { // 判断调用对象是否为函数 if (typeof this !== "function") { throw new TypeError("Error"); } // 获取参数 var args = [...arguments].slice(1), fn = this; return function Fn() { // 根据调用方式,传入不同绑定值 return fn.apply( this instanceof Fn ? this : context, args.concat(...arguments) ); };};复制代码
函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
function curry(fn, ...args) { return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);}复制代码
创建AJAX请求的步骤:
const SERVER_URL = "/server";let xhr = new XMLHttpRequest();// 创建 Http 请求xhr.open("GET", SERVER_URL, true);// 设置状态监听函数xhr.onreadystatechange = function() { if (this.readyState !== 4) return; // 当请求成功时 if (this.status === 200) { handle(this.response); } else { console.error(this.statusText); }};// 设置请求失败时的监听函数xhr.onerror = function() { console.error(this.statusText);};// 设置请求头信息xhr.responseType = "json";xhr.setRequestHeader("Accept", "application/json");// 发送 Http 请求xhr.send(null);复制代码