Object
模拟实现call/apply
apply
共两个参数,第二个参数是一个数组;call
有n
个参数,从第二个参数开始是一个参数列表- 都是立即执行
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使用和基本实现
发表评论