手写各种原生方法

Object

模拟实现call/apply

  • apply共两个参数,第二个参数是一个数组;
  • calln个参数,从第二个参数开始是一个参数列表
  • 都是立即执行
  • call/apply使用示例
const obj = { name: 'wmm66 - obj' } // 用var会默认挂到window下 var name = 'wmm66 - window' function sayName(age, job) { console.log(this.name, age, job) } const age = 18 const job = 'fe' sayName(age, job) sayName.apply(obj, [age, job]) sayName.call(obj, age, job)
  • 模拟实现call
// ES5实现 // Function.prototype.myCall = function(context) { // var context = context || window // context.fn = this // var args = [] // for (let i = 1, len = arguments.length; i < len; i++) { // args.push('arguments[' + i + ']') // } // var result = eval('context.fn(' + args + ')') // delete context.fn // return result // } // ES6实现 Function.prototype.myCall = function(context, ...args) { context = context || window const fn = Symbol('fn') context[fn] = this // const result = eval('context[fn](...args)') const result = context[fn](...args) delete context[fn] return result } sayName.myCall(obj, age, job)
  • 模拟实现apply
// 实现和call相同,只是输入参数不同 Function.prototype.myApply = function(context, args) { context = context || window args = Array.isArray(args) ? args : [] context.fn = this const result = context[fn](...args) delete context.fn return result } sayName.myApply(obj, [age, job])

模拟实现bind

  • bind使用示例
const obj = { name: 'wmm66 - obj' } function sayName(age, job) { console.log(this.name, age, job) } const age = 18 const job = 'fe' const objSayName = sayName.bind(obj) objSayName(age, job) // 柯里化测试 const objSayName2 = sayName.bind(obj, age) objSayName2(job)
  • 模拟实现bind
Function.prototype.myBind = function(context, ...args) { if (typeof this !== 'function') { throw new Error("Function.prototype.bind - what is trying to be bound is not callable") } var self = this var fbound = function() { // args.concat(Array.prototype.slice.call(arguments)):将两次传的参数合并,实现柯里化 self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))) } return fbound }
  • 对于构造函数,要保证原函数的原型对象上的属性不能丢失。改造下
const Person = function(name, age) { this.name = name this.age = age this.test = '66666' } Person.prototype.say = function() { console.log(this.name, this.age, this.test) } const p1 = new Person('wmm66', 18); p1.say() // bind使用示例 const obj = { name: 'wmm66', age: 18 } const Wmm = Person.bind(obj, '这是name') const p2 = new Wmm(23) p2.say() // bind实现完善 Function.prototype.myBind = function(context, ...args) { if (typeof this !== 'function') { throw new Error("Function.prototype.bind - what is trying to be bound is not callable") } var self = this var fbound = function() { self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))) } var Fn = function() {} Fn.prototype = this.prototype fbound.prototype = new Fn() return fbound } // myBind使用示例 const obj = { name: 'wmm66', age: 18 } const Wmm = Person.myBind(obj, '这是name') const p3 = new Wmm(23) p3.say()

模拟实现new

  • new使用示例如下
const Person = function(name) { this.name = name || 'wmm' } Person.prototype.sayName = function() { console.log('prototype.sayName: ', this.name) } const p1 = new Person('wmm66') p1.sayName() console.log(p1)

new创建对象实际上会经历以下4个步骤:

  • 创建一个新对象;
  • 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
  • 执行构造函数中的代码(为这个新对象添加属性);
  • 返回新对象。判断构造函数返回值是否为对象,如果为对象就使用构造函数返回的值,否则使用 obj,这样就实现了忽略构造函数返回的原始值。
  • 模拟new示例
const myNew = function(creator, ...args) { // 创建一个对象,因为new操作符会返回一个对象 const obj = {} // 将对象与构造函数原型链接起来 obj.__proto__ = creator.prototype creator.prototype.constructor = creator // 执行构造函数代码 将构造函数中的this指向这个对象,并传递参数 const res = creator.apply(obj, args) // 判断构造函数返回值是否为对象 return res instanceof Object ? res : obj } const p2 = myNew(Person, 'wmm66') p2.sayName() console.log(p2)
  • 使用Object.create()改写下
const myNew = function(creator, ...args) { if (typeof creator !== 'function') { console.error('newOperator function the first param must be a function') } // 1.新建一个空对象,并将 构造函数的原型对象赋给这个空对象 const obj = Object.create(creator.prototype) // 2.执行构造函数,相应参数被传入,并将this的上下文指向新创建的对象obj // const res = creator.apply(obj, args) const res = creator.call(obj, ...args) let isObject = typeof res === 'object' && res !== null let isFunction = typeof res === 'function' // 3.如果构造函数返回了对象,就舍弃之前创建的对象obj,newObj = ret // 4.反之,newObj = obj return isObject || isFunction ? res : obj } const p3 = myNew(Person, 'wmm66') p3.sayName() console.log(p3)

模拟实现Object.create

function createObj(o) { const F = function() {} F.prototype = o F.prototype.constructor = F return new F() }
function createObj(o) { const obj = {} obj.__proto__ = o return obj }

模拟实现Object.freeze

function myFreeze(obj) { if (obj instanceof Object) { Object.seal(obj) for (let key in obj) { if (obj.hasOwnProperty(key)) { Object.defineProperty(obj, key, { writable: false }) } myFreeze(obj[key]) } } return obj }
/** * Object.freeze 冻结的对象只能保证这个对象的属性不变 * 如果对象属性的值还是一个复杂数据类型,那么是可以修改成功的 * 实现一个深度冻结的方法 */ function deepFreeze(obj) { const propNames = Object.getOwnPropertyNames(obj) propNames.forEach(key => { const prop = obj[key] if (typeof prop === 'object' && prop !== null) deepFreeze(prop) }) return Object.freeze(obj) }

模拟实现instanceof

function myInstanceof(left, right) { // ES6 新写法是 let proto = Object.getPrototypeOf(left) let proto = left.__proto__ const prototype = right.prototype while (true) { if (proto === null) return false if (proto === prototype) return true proto = proto.__proto__ } }

Array

模拟实现push

Array.prototype.myPush = function() { for (let i = 0; i < arguments.length; i++) { this[this.length] = arguments[i] } return this.length }

模拟实现unshift

Array.prototype.myUnshift = function() { const len = arguments.length this.length += len for (let i = this.length - 1; i >= len; i--) { this[i] = this[i - len] } for (let i = 0; i < len; i++) { this[i] = arguments[i] } return this.length }

模拟实现reduce

Array.prototype.myReduce = function(fn, prev) { for (let i = 0; i < this.length; i++) { if (typeof prev === 'undefined') { prev = fn(this[i], this[i + 1], i + 1, this) i++ } else { prev = fn(prev, this[i], i, this) } } return prev }

接口请求

Ajax

function ajax(url, methods = 'GET', data = null) { methods = methods.toUpperCase() return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open(methods, url, true) methods === 'POST' && xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.onreadystatechange = function() { if (this.readyState === 4) { if (this.status === 200) { resolve(this.responseText) } else { const resJson = { code: this.status, response: this.response } reject(resJson) } } } data ? xhr.send(JSON.stringify(data)) : xhr.send() }) }

jsonp

/** * callback参数是全局方法的方法名 * 返回后自动调用那个对应的全局方法 */ function jsonp(url, data, callback) { data.callback = callback const arr = [] for (let key in data) { if (data.hasOwnProperty(key)) { arr.push(`${key}=${data[key]}`) } } const script = document.createElement('script') script.src = url + '?' + arr.join('&') document.body.appendChild(script) return new Promise((resolve, reject) => { try { resolve(data) } catch(e) { reject(e) } finally { document.body.removeChild(script) } }) }

Proxy

function MyProxy(target, handler) { const _target = deepClone(target) Object.keys(_target).forEach(key => { Object.defineProperty(_target, key, { get() { return handler.get && handler.get(target, key) }, set(value) { handler.set && handler.set(target, key, value) } }) }) return _target } function deepClone(obj) { if (obj === null) return null if (typeof obj !== 'object') return obj if (obj instanceof Date) return new Date(obj) if (obj instanceof RegExp) return new RegExp(obj) const newObj = new obj.constructor for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = deepClone(obj[key]) } } return newObj }

Promise

参考:Promise模拟实现

简易版Vue实现

参考:Vue双向数据绑定原理

实现redux、react-redux

参考:redux使用和基本实现


创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: