这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
柯里化
重要程度:⭐⭐
手写频率:⭐⭐⭐
函数柯里化是一个不怎么常用,但是面试的时候经常考察的知识点,分为两种,一种是确定参数数量的版本,一种是不确定参数的版本
确定参数
function curry(fn, ...args) {
return args.length < fn.length ? curry.bind(null, fn, ...args) : fn(...args)
}
实现的核心就是判断args.length
和fn.length
的大小,如果args.length
小于fn.length
,说明参数还未传入完,那么继续返回带有之前args
的函数,否则直接执行fn(...args)
还有一个关键点就是...args
和bind
,我们用bind
将之前的参数传入给返回的函数,不定参数接收传入的所有参数,这样就实现了参数的保存,我们来看看args
变化的过程
function curry(fn, ...args) {
console.log(args)
return args.length < fn.length ? curry.bind(null, fn, ...args) : fn(...args)
}
function add(a, b, c) {
return a + b + c
}
const curryAdd = curry(add)
curryAdd(1)(2)(3)
// []
// [ 1 ]
// [ 1, 2 ]
// [ 1, 2, 3 ]
一目了然吧
不定参数
我们以实现add(1, 2)(1).toSum()
的效果为例
function add(...args) {
const sum = args.reduce((acc, cur) => acc + cur)
function fn(...args2) {
return add(args2.reduce((acc, cur) => acc + cur) + sum)
}
fn.toSum = () => sum
return fn
}
这里用到了reduce
,不熟悉的同学可以先去看看用法 reduce() - MDN
简单解析一下
args.reduce((acc, cur) => acc + cur)
这段代码是用来求传入的args
的总和
那么接下来的代码就很容易看懂啦,函数fn
就是一个接收新的args2
,并加上之前算出的sum
作为参数递归调用add
函数,这里也用到了闭包
最后,我们需要一个返回结果的函数,直接在fn
上定义一个toSum
函数,返回计算的结果
各种 API
重要程度:⭐⭐⭐⭐
手写频率:⭐⭐⭐⭐
JS 中的各种 API 不仅非常重要,也是面试常常会考察的
map
Array.prototype.iMap = function iMap(fn, context) {
return this.reduce(
(acc, cur, index) => [...acc, fn.call(context, cur, index, this)],
[]
)
}
reduce
Array.prototype.iReduce = function iReduce(fn, initial) {
let result = initial || this[0]
let start = initial ? 0 : 1
for (let i = start; i < this.length; i++) {
result = fn(result, this[i], i, this)
}
return result
}
filter
Array.prototype.iFilter = function iFilter(fn, context) {
return this.reduce(
(acc, cur, index) =>
fn.call(context, cur, index, this) ? [...acc, cur] : acc,
[]
)
}
flat
Array.prototype.iFlat = function iFlat(depth = 1) {
let arr = this
while (depth && arr.some(Array.isArray)) {
arr = [].concat(...arr)
depth -= 1
}
return arr
}
call
Function.prototype.iCall = function iCall(context = window, ...args) {
const tmpKey = Symbol('tmp')
context[tmpKey] = this
const result = context[tmpKey](...args)
delete context[tmpKey]
return result
}
apply
Function.prototype.iApply = function iApply(context = window, args) {
const tmpKey = Symbol('tmp')
context[tmpKey] = this
const result = context[tmpKey](...args)
delete context[tmpKey]
return result
}
bind
Function.prototype.iBind = function iBind(context, ...args) {
return (...rest) => this.apply(context, [...args, ...rest])
}
Object.create
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的、__proto__。
Object.iCreate = function iCreate(proto) {
function Noop() {}
Noop.prototype = proto
Noop.prototype.constructor = Noop
return new Noop()
}
new
new 运算符用来创建用户自定义的对象类型的实例或者具有构造函数的内置对象的实例。
实现要点:
- 如果构造函数返回的结果是引用数据类型就返回该引用类型 不是引用类型就返回实例
function iNew(Constructor, ...args) {
const instance = Object.create(Constructor.prototype) // 创建 + 绑定原型
const result = Constructor.call(instance, ...args)
// 如果构造函数返回的结果是引用数据类型就返回该引用类型 不是引用类型就返回实例
return result instanceof Object ? result : instance
}
instanceof
用迭代的方法实现
function iInstanceof(left, right) {
let proto = Object.getPrototypeOf(left)
while (true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = Object.getPrototypeOf(proto)
}
}