本文目录
[[toc]]
range()
测试用例:
for (let num of range(1, 4)) {
console.log(num)
}
// 1
// 2
// 3
// 4普通循环
/**
* @param {number} from
* @param {number} to
*/
function range(from, to) {
const list = []
for (let i = from; i <= to; ++i) {
list.push(i)
}
return list
}迭代器 interator
/**
* 手动实现 iterator
*
* @param {number} from
* @param {number} to
*/
function range(from, to) {
let i = from
return {
[Symbol.interator]() {
const isDone = i > to
return {
done: isDone,
value: isDone ? undefined : i,
}
}
}
}生成器 generator
/**
* 生成器的结果能够被 for of 迭代出来,其本身就实现了 iterator
*
* @param {number} from
* @param {number} to
*/
function range(from, to) {
return (function * () {
while (from <= to) {
yield from++
}
})()
}迭代器 + 生成器 interator + generator
/**
* 多包一层,纯凑方案数,好处是不需要 IIFE 了
*
* @param {number} from
* @param {number} to
*/
function range(from, to) {
return {
[Symbol.iterator]: function * () {
while (from <= to) {
yield from++
}
},
}
}new
function _new(fn, ...args) {
const target = Object.create(fn.prototype)
const result = fn.call(target, ...args)
const isObjectOrFunction = (result !== null && typeof result === 'object') || typeof result === 'function'
return isObjectOrFunction ? result : target
}instanceof
function myInstanceof(left, right) {
// 这里先用typeof来判断基础数据类型,如果是,直接返回false
if (typeof left !== 'object' || left === null) {
return false
}
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left)
while (true) {
if (proto === null) {
return false
}
if (proto === right.prototype) {
// 找到相同原型对象,返回true
return true
}
proto = Object.getPrototypeof(proto)
}
}call
function myCall(thisArg, ...args) {
if (thisArg == null) {
return fn(...args)
}
// 使用 Symbol 防止属性冲突
const prop = Symbol('temporary property for fn')
thisArg[prop] = fn
const result = thisArg[prop](...args)
// 移除临时属性
Reflect.deleteProperty(thisArg, prop)
return result
}
function printName() {
console.log(this.name)
}
console.log(printName.myCall({ name: 'ly' })) // => lyapply
function myApply(thisArg, args) {
// 这里的 this 其实就是 func.myCall(thisArg, ...args) 中的 func,因为 myCall 是通过 func 调用的嘛
const func = this
if (thisArg === undefined || thisArg === null) {
// 如果 thisArg 是 undefined 或则 null,this 指向全局对象,直接调用就可以达到指向全局对象的目的了
return func(args)
}
const tempFunc = Symbol('Temp property')
// 在 thisArg 上临时绑定 func
thisArg[tempFunc] = func
// 通过 thisArg 调用 func 来达到改变 this 指向的作用
const result = thisArg[tempFunc](args)
// 删除临时属性
delete thisArg[tempFunc]
return result
}
function printName() {
console.log(this.name)
}
console.log(printName.myCall({ name: 'ly' })) // => lybind
普通实现,不支持 new 创建对象
function myBind(func, thisArg, ...args) {
// bind 返回的是一个新函数
return function (...otherArgs) {
// 执行函数时 this 始终为外层函数中的 thisArg,前面的调用参数也被绑定为 args
return func.call(thisArg, ...args, ...otherArgs)
}
}
function printThisAndAndArgs(...args) {
console.log(`This is ${JSON.stringify(this)}, arguments is ${[...args].join(', ')}`)
}
const boundFunc = myBind(printThisAndAndArgs, { name: 'Lily' }, 1, 2, 3)
boundFunc(4, 5, 6) // => This is {"name":"Lily"}, arguments is 1, 2, 3, 4, 5, 6支持 new 创建的 bind 版本
function myBind(func, thisArg, ...args) {
// bind 返回的是一个新函数,如果使用 new 调用了被绑定后的函数,其中的 this 即是 new 最后返回的实例对象,也就是 target
return function (...otherArgs) {
// 当 new.target 为 func,不为空时,绑定 this,而不是 thisArg
return func.call(new.target ? this : thisArg, ...args, ...otherArgs)
}
}
function Student(name, age) {
this.name = name
this.age = age
}
const BoundStudent1 = Student.bind({ name: 'Taylor' }, 'ly')
console.log(new BoundStudent1(22)) // => Student { name: 'ly', age: 22 }
const BoundStudent2 = myBind(Student, { name: 'Taylor' }, 'ly')
console.log(new BoundStudent2(22)) // => { name: 'ly', age: 22 }最终完美版
/**
* 模拟实现 Function.prototype.bind
*
* @param {Function} fn 被绑定 this 的函数
* @param {*} thisArg
* @param {...any} boundArgs 被绑定的参数
* @returns {Function} 绑定 this 后的新函数
*/
function myBind(fn, thisArg, ...boundArgs) {
function boundFn(...otherArgs) {
// 被使用 new 调用时 this 应该就是被指定的 this
const ctx = new.target ? this : thisArg
return fn.call(ctx, ...boundArgs, ...otherArgs)
}
// 这一步是为了 boundFn 被当作构造函数使用时,其实例能正常访问 fn 原型链上的属性
boundFn.prototype = Object.create(fn.prototype)
boundFn.prototype.constructor = boundFn
// 默认就是 writeable: false,所以 name 和 length 都被设置为不可写了
Object.defineProperties(boundFn, {
name: {
// 被绑定 this 的新函数的 name 都是原函数前面加 bound
value: `bound ${fn.name}`,
},
length: {
// boundFn 的 length 是剩余需要传递的参数
value: Math.max(fn.length - boundArgs.length, 0),
},
})
return boundFn
}
function Student(name, age) {
this.name = name
this.age = age
}
Student.prototype.type = 'student'
const BoundStudent2 = Student.myBind({ name: 'Taylor' }, 'ly')
console.log(new BoundStudent2(22).type) // => student
console.log(BoundStudent2.name) // => bound Student
console.log(BoundStudent2.length) // => 2