Re:从壹开始的Js淦原理

2020.08.05
评论

flat数组降维

利用递归循环和数组合并

Array.prototype.adFlat = function(deep) {
	return deep > 0 ? this.reduce((total, item, index) => {
		return total.concat(Array.isArray(item) ? item.adFlat(deep - 1) : item)
	}, []) : this.slice()
}

call\apply\bind指向修改

利用this的特性,将要执行的方法放到指定的this上,然后执行

//call\apply
Function.prototype.adCall = function() {
	//如果是apply,将 [pointer, ...args] 改为 [pointer, args]
	let [pointer, ...args] = [...arguments], //将传入参数解析成指针和其他参数
	fn = Symbol(); //创建独立名称
	pointer = Object(pointer) || window; //将传入指针转换成实例对象
	pointer[fn] = this; //向创建对象添加fn独立方法
	let rel = pointer[fn](...args); //将执行结果赋值
	delete pointer[fn]; //删除之前添加进指针对象的方法
	return rel; //返回执行结果
}
//bind
Function.prototype.adBind = function(pointer, ...args) {
	return (...newagrs) => this.call(pointer, ...args, ...newagrs) //方法返回一个方法,传入两次传入的参数
}

new对象实例化

创建一个新对象,这个对象的原型指向构造函数的原型->在新对象上执行这个构造函数->返回新对象;与Object.create的差别在于,new会执行构造函数

function _new(fn, ...args) {
	let obj = {}; // 创建一个空对象
	obj.__proto__ = fn.prototype; // 空对象原型指向构造函数原型
	fn.call(obj, ...args); // 改变指向并执行构造函数
	return obj; // 返回新对象
}

instanceOf实例判断

由于instanceOf的判断是会往原型链顶层走的,所以我们需要使用递归或者循环来往原型顶层走,通过实例对象的__proto__和对象的原型prototype进行比对

function instanceOf(leftVal, rightVal) {
	if (typeof leftVal != 'object' || leftVal === null) return false;
	let leftPro = leftVal.__proto__, // 获取实例化内容的原型
		rightPro = rightVal.prototype; // 获取原型
	while (true) { // 深度向上寻找原型,直到顶层Object.prototype
		if (leftPro === null) return false;
		if (leftPro === rightPro) return true;
		leftPro = leftPro.__proto__
	}
}

JSON.parse解析

利用eval函数

function parse (json) {
    // 此处的括号是为了表明内部执行内容为一个整体,就像箭头函数简写返回一个对象也需要括号包裹一样
    return eval("(" + json + ")"); 
}

await/async异步自动执行队列

await与async实际上就是generator函数的语法糖,所以此处使用generator实现。generator本身拥有中断递进的功能,所以与promise配合,通过next的传参性质,使用递归执行generator内容即可

// 此处伪代码并未考虑抛错,如需了解请看下一个代码块
function generatorToAsync(generatorFn) {
	return function() {
		return new Promise((resolve, reject) => {
			const g = generatorFn()
			const handler = (res) => {
				const cb = g.next(res).value
				if (typeof cb.then === 'function') {
					cb.then(res => handler(res))
				} else {
					resolve(res)
				}
			}
			g.next().value.then(handler)
		})
	}
}
 
function fn(nums) {
	return new Promise(resolve => {
		setTimeout(() => {
			resolve(nums * 2)
		}, 1000)
	})
}
 
const asyncFn = generatorToAsync(function*() {
	const num1 = yield fn(1)
	console.log('1 end');
	const num2 = yield fn(num1)
	console.log('2 end');
	const num3 = yield fn(num2)
	console.log('3 end');
	return num3
})
asyncFn().then(res => console.log(res)) // 3秒后输出 8

较为完善的实现

原文链接

function generatorToAsync(generatorFn) {
	return function() {
		const gen = generatorFn.apply(this, arguments) // gen有可能传参
		// 返回一个Promise
		return new Promise((resolve, reject) => {
			function go(key, arg) {
				let res
				try {
					res = gen[key](arg) // 这里有可能会执行返回reject状态的Promise
				} catch (error) {
					return reject(error) // 报错的话会走catch,直接reject
				}
				// 解构获得value和done
				console.log(res);
				const {
					value,
					done
				} = res
				if (done) {
					// 如果done为true,说明走完了,进行resolve(value)
					return resolve(value)
				} else {
					// 如果done为false,说明没走完,还得继续走
					// value有可能是:常量,Promise,Promise有可能是成功或者失败
					return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))
				}
			}
			go("next") // 第一次执行
		})
	}
}

is值相同对比

利用1/0===Infinity1/-0===-Infinity的特性区分正负0相同判断;利用NaN永不与NaN相等的特性判断NaN

function is(x, y) {
	if (x === y) {
		// 防止 -0 和 +0
		return x !== 0 || 1 / x === 1 / y
	}
	// 防止NaN
	return x !== x && y !== y
}
console.log(is(0, -0)); // false