实现 new 操作符
function myNew(){
// 获取除了Fn之外的参数,由于是模拟的,所以需要这样处理一下,
// 但是如果是new Person(),直接拿参数就行了
var Fn = Array.prototype.shift.call(arguments);
// 创建空对象
var obj = {};
// 空对象继承构造函数的原型对象
obj.__proto__ = Fn.prototype;
// 调用构造函数,将obj作为上下文传入构造函数,并获取结果
var res = Fn.apply(obj,args);
// 根据结果返回对象
return typeof res === "object" && res !== null ? res : obj;
}
实现 instanceof 原型判断
// 如 [] instanceof Array
function myInstanceof(left, right) {
// 如果不是object,没必要判断了
if (typeof left !== 'object' || left === null) return false;
// 获取left上的原型对象,也可以用__proto__
let proto = Object.getPrototypeOf(left);
// 从left上不断往上找,直到找到头
while (true) {
if (proto === null) return false;
if (proto === right.prototype) return true;
proto = Object.getPrototypeOf(left);
}
}
console.log(myInstanceof([], Array));
实现 typeof 类型判断
// 这里有个问题,和原生的typeof不太一样,如array,这里会输出array,而原生typeof会输出object
// 所以如果要完全和typeof保持一致,可以内部判断一下, 不是那6个基本类型,全部返回object
function myTypeOf(target) {
return Object.prototype.toString.call(target).slice(8, -1).toLowerCase()
}
// ---测试----
function test() { }
console.log(myTypeof(1), typeof 1);
console.log(myTypeof(true), typeof true);
console.log(myTypeof('hello'), typeof 'hello');
console.log(myTypeof(Symbol('symbol')), typeof Symbol('symbol'));
console.log(myTypeof(undefined), typeof undefined);
console.log(myTypeof(null), typeof null);
console.log(myTypeof([]), typeof []);
console.log(myTypeof({ a: 123 }), typeof { a: 123 });
console.log(myTypeof(test), typeof test);
console.log(myTypeof(new Date()), typeof new Date());
Array 方法实现
forEach
Array.prototype.forEach2 = function (callbackfn, thisArg) {
let array = Array.prototype.slice.call(this);
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
callbackfn.call(thisArg, array[i], i, this);
}
}
var arr = [1, 2, 4, 5];
arr.forEach2((num, index) => {
return arr[index] = num * 2
})
console.log(arr) // [2 , 4 , 8 , 10]
console.log("===================================");
every
Array.prototype.every2 = function (predicate, thisArg) {
let array = Array.prototype.slice.call(this);
if (array.length <= 0) return false;
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
let res = predicate.call(thisArg, array[i], i, this);
if (!res) {
return false;
}
}
return true;
}
console.log([1, 2, 3].every2((v, i, arr) => {
console.log(`value:${v},index:${i},arr:${arr}`);
return v >= 0;
}));
console.log("===================================");
some
Array.prototype.some2 = function (predicate, thisArg) {
let array = Array.prototype.slice.call(this);
if (array.length <= 0) return false;
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
let res = predicate.call(thisArg, array[i], i, this);
if (res) {
return true;
}
}
return false;
}
console.log([1, 2, 3].some2((v, i, arr) => {
console.log(`value:${v},index:${i},arr:${arr}`);
return v >= 2;
}));
console.log("===================================");
filter
Array.prototype.filter2 = function (predicate, thisArg) {
let array = Array.prototype.slice.call(this);
let filterArray = [];
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
predicate.call(thisArg, array[i], i, this) && filterArray.push(array[i]);
}
return filterArray;
}
console.log([1, 2, 3].filter2((v, i, arr) => {
console.log(`value:${v},index:${i},arr:${arr}`);
return v >= 2;
}));
console.log("===================================");
// 一般使用在存在大量相同数据的表格中,存储了许多没有意义的数据。举个例子:一个数组存储了100个数据,有效数据只有10个,就可以使用稀疏数组存储。
// 稀疏数组处理的方法:
// 记录行和列个数
// 将不同值元素的行列 以及数组存储在一个小的数组中 ,缩小存储数据占用的空间。
map
Array.prototype.map2 = function (callbackfn, thisArg) {
let array = Array.prototype.slice.call(this);
let mapArray = [];
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
mapArray[i] = callbackfn.call(thisArg, array[i], i, this);
}
return mapArray;
}
console.log([1, 2, 3].map2((v, i, arr) => {
console.log(`value:${v},index:${i},arr:${arr}`);
return v * 2;
}));
console.log("===================================");
find
Array.prototype.find2 = function (predicate, thisArg) {
let array = Array.prototype.slice.call(this);
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
let res = predicate.call(thisArg, array[i], i, this);
if (res) {
return array[i]
}
}
return undefined;
}
console.log([{ name: 1 }, { name: 2 }, { name: 3 }].find2((v, i, arr) => v.name == 2));
console.log("===================================");
findIndex
Array.prototype.findIndex2 = function (predicate, thisArg) {
let array = Array.prototype.slice.call(this);
for (let i = 0; i < array.length; i++) {
if (!array.hasOwnProperty(i)) continue;//稀松数组?
let res = predicate.call(thisArg, array[i], i, this);
if (res) {
return i
}
}
return -1;
}
console.log([{ name: 1 }, { name: 2 }, { name: 3 }].findIndex2((v, i, arr) => v.name == 3));
console.log("===================================");
flat
// flat() 方法创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中。
Array.prototype.flat2 = function (depth) {
let array = Array.prototype.slice.call(this);
if (depth <= 0 || typeof depth !== "number") return array;
let res = [];
for (let i = 0; i < array.length; i++) {
if (Array.isArray(array[i]) && depth >= 1) {
res = res.concat(array[i].flat2(depth - 1));
} else {
res.push(array[i])
}
}
return res;
}
console.log([1, 2, [3, 4, [5, 6, [7, [8]]]]].flat2(4));
console.log([1, 2, [3, 4, [5, 6, [7, [8]]]]].flat(4));
console.log("===================================");
reduce
Array.prototype.reduce2 = function (callbackfn, initialValue) {
let array = Array.prototype.slice.call(this);
let pre = initialValue;
for (let i = 0; i < array.length; i++) {
pre = callbackfn.call(null, pre, array[i], i, this);
}
return pre;
}
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(accumulator, currentValue) => {
console.log(`accumulator:${accumulator}, currentValue:${currentValue}`);
return accumulator + currentValue
},
initialValue
);
const sumWithInitial2 = array1.reduce2(
(accumulator, currentValue) => {
console.log(`accumulator:${accumulator}, currentValue:${currentValue}`);
return accumulator + currentValue
},
initialValue
);
console.log(sumWithInitial);
console.log(sumWithInitial2);
curry柯里化
// 什么叫函数柯里化?其实就是将使用多个参数的函数转换成一系列使用一个参数的函数的技术。
function curry(fn) {
let len = fn.length;
let _fn = function () {
let args = arguments;
return args.length >= len ? fn.apply(null, args) : (...arg) => _fn(...args, ...arg);
}
return _fn;
}
var add = (a, b, c, d) => a + b + c + d;
var addCurry = curry(add);
console.log(addCurry(1)(2, 3)(4));
console.log(addCurry(1)(2, 3, 4));
console.log(addCurry(1)(2)(3)(4));
doubounce防抖
// 函数可能有返回值
// 支持立即调用immediate
// 支持取消
function debounce(fn, wait, immdiate) {
let timer = null;
let res;
let context;
let args;
let debounced = function () {
context = this;
args = arguments;
if (!timer) {
timer = setTimeout(() => {
timer = null;
context = args = null;
if (!immdiate) {
res = fn.apply(context, args);
}
}, wait);
if (immdiate) {
res = fn.apply(context, args);
}
}
return res;
}
debounced.cancle = function () {
clearTimeout(timer);
timer = args = context = null;
}
return debounced;
}
// underscore
// function debounce(func, wait, immediate) {
// var timeout, args, result, context;
// var debounced = function () {
// context = this;
// args = arguments;
// if (!timeout) {
// timeout = setTimeout(() => {
// timeout = null;
// if (!immediate) result = func.apply(context, args);
// // This check is needed because `func` can recursively invoke `debounced`.
// if (!timeout) args = context = null;
// }, wait);
// if (immediate) result = func.apply(context, args);
// }
// return result;
// };
// debounced.cancel = function () {
// clearTimeout(timeout);
// timeout = args = context = null;
// };
// return debounced;
// }
throttle 节流
// 支持取消节流
// 支持立即执行和结束后执行options.leading,options.trailing
// 如果你想禁用第一次首先执行的话,传递{leading: false},还有如果你想禁用最后一次执行的话,传递{trailing: false}。
function throttle(fn, wait, options) {
let timer;
let context, args, result;
let previous = 0;
options = typeof options === "object" ? options : {};
let _throttle = function () {
let now = Date.now();
if (!previous && options.leading === false) {
previous = now;
}
let remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
result = fn.apply(context, args);
if (!timer) context = args = null;
} else if (!timer && options.trailing !== false) {
timer = setTimeout(() => {
timer = null;
previous = options.leading === false ? 0 : now;
result = fn.apply(context, args);
if (!timer) context = args = null;
}, remaining);
}
return result;
}
_throttle.cancle = function () {
clearTimeout(timer);
timer = null;
context = args = null;
}
return _throttle;
}
// function throttle(func, wait, options) {
// var timeout, context, args, result;
// var previous = 0;
// if (!options) options = {};
// var later = function () {
// previous = options.leading === false ? 0 : now();
// timeout = null;
// result = func.apply(context, args);
// if (!timeout) context = args = null;
// };
// var throttled = function () {
// var _now = now();
// if (!previous && options.leading === false) previous = _now;
// var remaining = wait - (_now - previous);
// context = this;
// args = arguments;
// if (remaining <= 0 || remaining > wait) {
// if (timeout) {
// clearTimeout(timeout);
// timeout = null;
// }
// previous = _now;
// result = func.apply(context, args);
// if (!timeout) context = args = null;
// } else if (!timeout && options.trailing !== false) {
// timeout = setTimeout(later, remaining);
// }
// return result;
// };
// throttled.cancel = function () {
// clearTimeout(timeout);
// previous = 0;
// timeout = context = args = null;
// };
// return throttled;
// }
deepCopy深拷贝
function deepCopy(original) {
const type = typeof original
if (type !== "object") return original;
let res = original instanceof Array ? [] : {};
for (const key in original) {
if (Object.hasOwnProperty.call(original, key)) {
res[key] = typeof original[key] === "object" ? deepCopy(original[key]) : original[key];
}
}
return res;
}
var a = {
name: "123",
tags: ["男人", "帅气", "有钱"],
area: {
provience: {
name: "浙江",
city: {
name: "杭州"
}
}
}
}
var b = deepCopy(a);
b.name = "456";
b.tags.push("年轻");
b.area.provience.city.name = "宁波";
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
console.log("==================================================");
eventbus 消息发布订阅
class EventBus {
constructor() {
this.watcher = new Map();
}
on(name, callback) {
if (name && callback instanceof Function) {
if (this.watcher.has(name)) {
let watches = this.watcher.get(name);
callback.once = false;
watches.push(callback);
} else {
this.watcher.set(name, [callback])
}
}
}
once(name, callback) {
if (name && callback instanceof Function) {
if (this.watcher.has(name)) {
let watches = this.watcher.get(name);
callback.once = true;
watches.push(callback);
} else {
this.watcher.set(name, [callback])
}
}
}
off(name, callback) {
if (this.watcher.has(name)) {
let watches = this.watcher.get(name);
let index = watches.findIndex((f) => f === callback);
if (index !== -1) {
watches.splice(index, 1);
}
}
}
emit(name, ...args) {
if (this.watcher.has(name)) {
let watches = this.watcher.get(name);
let needDelete = [];
for (let i = 0; i < watches.length; i++) {
const fn = watches[i];
fn(...args);
if (fn.once) {
needDelete.push(i);
}
}
this.watcher.set(name, watches.filter((v, i) => !needDelete.includes(i)));
}
}
}
let fn1 = function (name, age) {
console.log(`${name} ${age}`)
}
let fn2 = function (name, age) {
console.log(`hello, ${name} ${age}`)
}
let fn3 = function (name, age) {
console.log(`woo, ${name} ${age}`)
}
let ev = new EventBus();
ev.on('aaa', fn1);
ev.on('aaa', fn2);
ev.once('aaa', fn3);
ev.emit('aaa', '布兰', 12);
ev.emit('aaa', '看雨', 99);
ev.emit('aaa', '小米', 33);
extend 7种继承
// 一:原型链继承
// 缺点:1、可以看出来无法做到将name传递给最上层的People构造函数,因为new People这一步实在Student上完成的。
// 2、类似于hobbies这种引用类型数据会被所有实例共享
function People1(name) {
this.name = name || "";
this.hobbies = ["足球", "看书"]
}
People1.prototype.say = function (word) {
console.log(this.name + " is speaking " + word);
}
function Student1(grade) {
this.grade = grade || 1;
}
Student1.prototype = new People1();
var xiaoming = new Student1(18);
var xiaohong = new Student1(16);
xiaoming.say("hello");
xiaohong.say("world");
xiaoming.hobbies.push("篮球");
console.log(xiaoming.name, xiaoming.grade, xiaoming.hobbies);
console.log(xiaohong.name, xiaohong.grade, xiaohong.hobbies);
console.log("===============================================");
// 二、借助构造函数
// 好处是解决了引用类型被所有实例共享的问题和实例传参给父类的问题
// 但是每次new 子类都会call父类,都会重复创建一遍父类中的函数,函数无法复用。
function People2(name) {
this.name = name || "";
this.hobbies = ["足球", "看书"]
this.say = function (word) {
console.log(this.name + " is speaking " + word + ", 爱好:" + this.hobbies.join("、"));
}
}
function Student2(name) {
People2.call(this, name)
}
var wang = new Student2("小王");
var li = new Student2("小李");
wang.say("hello");
li.say("world");
wang.hobbies.push("篮球");
console.log(wang.name, wang.grade, wang.hobbies);
console.log(li.name, li.grade, li.hobbies);
console.log("===============================================");
// 三、组合继承
// 好处:借用构造函数解决了引用类型的共享问题,通过new父类实现了原形链继承父类的方法
// 坏处:调用了2次父类的构造函数
function People3(name) {
this.name = name || "";
this.hobbies = ["足球", "看书"]
}
People3.prototype.say = function (word) {
console.log(this.name + " is speaking " + word + ", 爱好:" + this.hobbies.join("、"));
}
function Student3(name, grade) {
People3.call(this, name);//调用构造函数,保证引用类型不会共享
this.grade = grade;
}
Student3.prototype = new People3();//继承父类上的方法
Student3.prototype.constructor = Student3;
Student3.prototype.getHobbies = function () {//子类还可以扩展自己的方法
console.log(this.hobbies);
}
var zhang = new Student3("小张", 18);
var zhao = new Student3("小赵", 17);
zhang.say("hello");
zhao.say("world");
zhang.hobbies.push("篮球");
console.log(zhang.name, zhang.grade, zhang.hobbies);
console.log(zhao.name, zhao.grade, zhao.hobbies);
console.log("===============================================");
// 四、原型式继承,说白了就是Object.create()
// object()是对传入的对象执行了一次浅复制
// 这个就是构造了一个空函数,并将对象赋值给原型,实现继承,但是引用类型还是在所有实例之间共享的
// 如:var a = {name:"123", arr: [1,2,3]}
// var b = Object.create(a);
// a.arr.push(4);
// console.log(b.arr);[1, 2, 3, 4]
// ECMAScript 5通过增加Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的object()方法效果相同:
function object(o) {
function F() { }
F.prototype = o;
return new F();
}
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"],
say: function () {
console.log("say:", this.friends);
}
};
let anotherPerson = object(person);// 效果等同于Object.create(person)
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
console.log(anotherPerson.name, anotherPerson.friends);
anotherPerson.say();
let yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(yetAnotherPerson.name, yetAnotherPerson.friends);
yetAnotherPerson.say();
console.log("person.name:%s", person.name);
console.log("person.friends:%s", person.friends); // "Shelby, Court, Van, Rob, Barbie"
console.log("===============================================");
// 五、寄生式继承,和原型式继承类似,类似于工厂函数,在其中增强对象并返回。
// 缺点也是这个sayHi的函数无法复用,每次创建一个实例都要重新创建一遍
function createAnother(original) {
var clone = object(original);
clone.sayHi = function () {
console.log(this.name);
}
return clone;
}
let p = createAnother(person);
p.sayHi(); // "hi"
console.log("===============================================");
// 六、组合寄生式继承
// 基本是比较完善的了,
// 组合式继承1、构造函数共享属性 2、原型链复用方法
// 寄生:通过object方法,获取父类的原型对象赋值给子类的原型,从而达到继承
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}
function SubType(name, age) {
SuperType.call(this, name); //第二次调用SuperType()
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
console.log(this.age);
};
let a = new SubType("wang", 19);
console.log({ name: a.name, age: a.age, colors: a.colors });
a.sayAge();
a.sayName();
console.log(a.__proto__ === SubType.prototype);
console.log(a instanceof SuperType);
console.log(a.__proto__.__proto__ === SuperType.prototype);
console.log("===============================================");
// 七:通过class实现寄生组合式继承
class SuperClass {
constructor(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
sayName() {
console.log(this.name);
};
}
class SubClass extends SuperClass {
constructor(name, age) {
super(name);
this.age = age;
}
sayAge() {
console.log(this.age);
};
}
let obj = new SubClass("li", 20);
console.log({ name: obj.name, age: obj.age, colors: obj.colors });
obj.sayAge();
obj.sayName();
console.log(obj.__proto__ === SubClass.prototype);
console.log(obj instanceof SuperClass);
console.log(obj.__proto__.__proto__ === SuperClass.prototype);
flatten 扁平化
function flatten(arr) {
let res = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
res = res.concat(flatten(arr[i]));
} else {
res.push(arr[i]);
}
}
return res;
}
console.log(flatten([1, 2, 3, [4, 5, [6, 7, [8, 9]]]]));
unique去重
function unique(arr) {
return arr.filter((value, index, array) => array.indexOf(value) === index);
}
call/apply/bind 函数方法
Function.prototype.call2 = function (context) {
context = context || window;
let args = [...arguments].slice(1);
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
}
var a = 123;
function add(b) {
return (this.a || 0) + b;
}
console.log(add.call2(null, 3));
console.log(add.call2({ a: 234 }, 3));
Function.prototype.apply2 = function (context) {
context = context || window;
let args = arguments[1] || [];
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
}
var a2 = 123;
function add(b2, c2) {
return (this.a2 || 0) + b2 + c2;
}
console.log(add.apply2(null, [3, 7]));
console.log(add.apply2({ a: 234 }, [3, 9]));
// bind 方法会创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
// 实现要点:
// bind() 除了 this 外,还可传入多个参数;
// bind 创建的新函数可能传入多个参数;
// 新函数可能被当做构造函数调用;
// 函数可能有返回值;
Function.prototype.bind2 = function (context) {
context = context || window;
let self = this;
let args = [...arguments].slice(1);
let fNOP = function () { };
fNOP.prototype = this.prototype
let fBind = function () {
let bindArgs = [...arguments];
return self.apply(this instanceof fNOP ? this : context, bindArgs.concat(args));
}
fBind.prototype = new fNOP();
return fBind;
}
var a3 = 123;
function add(b3, c3) {
return (this.a3 || 0) + b3 + c3;
}
console.log(add.bind2(null)(2, 3));
console.log(add.bind2({ a3: 234 })(5, 6));
instanceof 原型判断
function MyInstanceOf(left, right) {
let proto = left.__proto__;
let prototype = right.prototype;
while (true) {
if (proto === prototype) { return true; }
if (proto === null) { return false; }
proto = proto.__proto__;
}
}
console.log(MyInstanceOf([], Array));
typeof 类型判断
// 这里有个问题,和原生的typeof不太一样,如array,这里会输出array,而原生typeof会输出object
// 所以如果要完全和typeof保持一致,可以内部判断一下, 不是那6个基本类型,全部返回object
function myTypeOf(target) {
return Object.prototype.toString.call(target).slice(8, -1).toLowerCase()
}
// ---测试----
function test() { }
console.log(myTypeof(1), typeof 1);
console.log(myTypeof(true), typeof true);
console.log(myTypeof('hello'), typeof 'hello');
console.log(myTypeof(Symbol('symbol')), typeof Symbol('symbol'));
console.log(myTypeof(undefined), typeof undefined);
console.log(myTypeof(null), typeof null);
console.log(myTypeof([]), typeof []);
console.log(myTypeof({ a: 123 }), typeof { a: 123 });
console.log(myTypeof(test), typeof test);
console.log(myTypeof(new Date()), typeof new Date());
JSON.parse/stringfy 方法
// JSON.stringify(value: any, replacer ?: ((this: any, key: string, value: any) => any) | undefined, space ?: string | number | undefined)
JSON.stringify2 = function (value) {
let type = typeof value;// "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "function" | "object"
if (type !== "object") {
let result = value;
if (Number.isNaN(value) || value === Infinity) {
result = "null";
} else if (type === "function" || type === "undefined" || type === "symbol") {
return undefined
} else if (type === "string") {
result = '"' + value + '"';
}
return String(result);
} else if (type === "object") {
if (value === null) {
return 'null';
} else if (value.toJSON && typeof value.toJSON === "function") {
return JSON.stringify2(value.toJSON);
} else if (Array.isArray(value)) {
let result = [];
value.forEach((v, i) => {
if (typeof v === "function" || typeof v === "undefined" || typeof v === "symbol") {
result[i] = 'null'
} else {
result[i] = JSON.stringify2(v);
}
});
result = '[' + result + ']';
return result.replace(/'/g, '"');
} else {
let result = [];
Object.keys(value).forEach((item, index) => {
if (typeof item !== 'symbol') {
//key 如果是 symbol 对象,忽略
if (value[item] !== undefined && typeof value[item] !== 'function' && typeof value[item] !== 'symbol') {
//键值如果是 undefined、function、symbol 为属性值,忽略
result.push('"' + item + '"' + ":" + JSON.stringify2(value[item]));
}
}
});
return ("{" + result + "}").replace(/'/g, '"');
}
}
}
console.log(JSON.stringify2({})); // '{}'
console.log(JSON.stringify2(true));; // 'true'
console.log(JSON.stringify2("foo"));; // '"foo"'
console.log(JSON.stringify2([1, "false", false])); // '[1,"false",false]'
console.log(JSON.stringify2({ x: 5 })); // '{"x":5}'
console.log(JSON.stringify2({ x: 5, y: 6 })); // "{"x":5,"y":6}"
// 未实现
console.log(JSON.stringify2([new Number(1), new String("false"), new Boolean(false)])); // '[1,"false",false]'
console.log(JSON.stringify2({ x: undefined, y: Object, z: Symbol("") })); // '{}'
console.log(JSON.stringify2([undefined, Object, Symbol("")])); // '[null,null,null]'
console.log(JSON.stringify2({ [Symbol("foo")]: "foo" })); // '{}'
console.log(JSON.stringify2({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")])); // '{}'
// 未实现
console.log(JSON.stringify2(
{ [Symbol.for("foo")]: "foo" },
function (k, v) {
if (typeof k === "symbol") {
return "a symbol";
}
}
)); // undefined
// 不可枚举的属性默认会被忽略:
console.log(JSON.stringify2(
Object.create(
null,
{
x: { value: 'x', enumerable: false },
y: { value: 'y', enumerable: true }
}
)
));
// "{"y":"y"}"
console.log('===========================================');
// JSON.parse(text: string, reviver ?: ((this: any, key: string, value: any) => any)
JSON.parse2 = function (text) {
// return eval(`(${text})`);
return new Function(`return ${text}`)()
}
new 操作符
function myNew() {
let Con = Array.prototype.shift.call(arguments);
let obj = {};
obj.__proto__ = Con.prototype;
let res = Con.apply(obj, [...arguments]);
return typeof res === "object" && res !== null ? res : obj;
}
function person(name, age) {
this.name = name
this.age = age
}
let p = myNew(person, '布兰', 12)
console.log(p) // { name: '布兰', age: 12 }
Object.create/assgin方法
// Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
Object.create2 = function (proto) {
function FN() { }
FN.prototype = proto;
let obj = new FN();
if (proto === null) {
// 创建一个没有原型对象的对象,Object.create(null)
obj.__proto__ = null
}
return obj;
}
const person = {
isHuman: false,
printIntroduction: function () {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create2(person);
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // Inherited properties can be overwritten
me.printIntroduction();
// Expected output: "My name is Matthew. Am I human? true"
console.log("=============================================");
Object.assign2 = function (target, ...source) {
let res = Object(target);
for (let i = 0; i < source.length; i++) {
let obj = source[i]
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
res[key] = obj[key];
}
}
}
return res;
}
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign2(target, source);
console.log(target);
// Expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target);
// Expected output: true
let obj1 = { a: 0, b: { c: 0 } };
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign2(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.
parseUrl URL参数格式化
function parseUrl(url) {
let obj = new URL(url);
let params = {};
for (const [key, value] of obj.searchParams) {
params[key] = value;
}
return params
}
console.log(JSON.stringify(parseUrl("https://fe.ecool.fun/topic-answer/fe97f51f-1d7d-49b7-acd2-83f345014633?orderBy=updateTime&order=desc&tagId=10")));
partial 偏函数
// 什么是偏函数?偏函数就是将一个 n 参的函数转换成固定 x 参的函数,剩余参数(n - x)将在下次调用全部传入。
// 说白了就是有3个参数,先传1个参数,后面再传剩余2个参数
// 支持"_"占位符
function partial(fn, ...args) {
let arr = [].concat(args);
let _fn = function () {
let arg = [...arguments];
if (arg.length === 0) return _fn;
arr = arr.map(v => v === "_" && arg.length > 0 ? arg.shift() : v);
arr = arr.concat(arg);
return arr.length >= fn.length ? fn.apply(null, arr) : _fn;
}
return _fn;
}
function add(a, b, c) {
return a + b + c
}
let partialAdd = partial(add, 1)
console.log(partialAdd(2, 3));
function clg(a, b, c) {
console.log(a, b, c)
}
let partialClg = partial(clg, '_', 2)
console.log(partialClg(1, 3));
Promise.all/resolve/reject/race/any/allSettled 方法
// Promise.all()
// Promise.resolve()
// Promise.reject()
// Promise.race()
// Promise.any()
// Promise.allSettled()
Promise.all2 = function (values) {
let result = [];
let count = 0;
return new Promise((resolve, reject) => {
values.forEach((value, i) => {
Promise.resolve(value).then((val) => {
result[i] = val;
count++;
if (count === values.length) {
resolve(result);
}
}).catch(err => {
reject(err);
})
});
})
}
Promise.resolve2 = function (value) {
if (value instanceof Promise) {
return value
}
return new Promise((resolve) => resolve(value));
}
Promise.reject2 = function (reason) {
return new Promise((reject) => reject(reason));
}
Promise.race2 = function (values) {
return new Promise((resolve, reject) => {
values.forEach((v) => {
Promise.resolve(v).then(resolve, reject)
});
})
}
Promise.allSettled2 = function (values) {
return new Promise((resolve, reject) => {
let result = [];
let count = 0;
values.forEach((v, i) => {
Promise.resolve(v).then((value) => {
count++;
result[i] = {
status: "fullfilled",
value
};
if (count === values.length) {
resolve(result);
}
}, (reason) => {
count++;
result[i] = {
status: "rejected",
reason
};
if (count === values.length) {
resolve(result);
}
})
});
})
}
Promise.any2 = function (values) {
return new Promise((resolve, reject) => {
let errors = [];
let count = 0;
values.forEach((v) => {
Promise.resolve(v).then((value) => {
resolve(value);
}, (reason) => {
errors.push(reason);
count++;
if (count === values.length) {
reject(new AggregateError(errors));
}
})
});
})
}
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 3000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000)
})
// let p3 = new Promise((resolve, reject) => {
// setTimeout(() => { reject(Error("cuowu")) }, 2000)
// })
let p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 2000);
})
console.time("Promise.all2")
Promise.all2([p1, p4, p2]).then(res => {
console.log(res);
console.timeEnd("Promise.all2")
})
字符串模板
function render(template, data) {
let reg = /\{\{(\w+)\}\}/;
if (reg.test(template)) {
let name = reg.exec(template)[1]
template = template.replace(reg, data[name]);
return render(template, data);
}
return template;
}
let template = "我是{{name}},年龄{{age}}岁,性别{{sex}}."
let data = {
name: "小明",
age: 17,
sex: "男"
}
console.log(render(template, data));
十大排序算法:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、技术排序、桶排序、基数排序
// https://juejin.cn/post/6844903444365443080
function swap(array, x, y) {
let temp = array[x];
array[x] = array[y];
array[y] = temp;
}
// 冒泡排序是一种简单的排序算法。
// 它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。
// 走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
// 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
// 冒泡排序 时间复杂度O(n2) 空间复杂度O(1) 稳定
function bubleSort(array) {
console.time('冒泡排序耗时');
let len = array.length;
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - i - 1; j++) {
if (array[j] > array[j + 1]) {
swap(array, j, j + 1);
}
}
}
console.timeEnd('冒泡排序耗时');
return array;
}
// 冒泡排序增强版,记录一趟最后交换的位置 时间复杂度O(n2) 空间复杂度O(1) 稳定
function bubleSort2(array) {
console.time('改进后冒泡排序耗时');
let last = array.length - 1;
while (last > 0) {
let pos = 0;
for (let j = 0; j < last; j++) {
if (array[j] > array[j + 1]) {
pos = j;
swap(array, j, j + 1);
}
}
last = pos;
}
console.timeEnd('改进后冒泡排序耗时');
return array;
}
// 冒泡排序再次增强版,一次找到左侧最小值和右侧最大值
function bubleSort3(array) {
console.time('2次改进后冒泡排序耗时');
let low = 0;
let high = array.length - 1;
while (low < high) {
for (let j = low; j < high; j++) {
if (array[j] > array[j + 1]) {
swap(array, j, j + 1);
}
}
--high;
for (let j = high; j > low; j--) {
if (array[j] < array[j - 1]) {
swap(array, j, j - 1);
}
}
++low;
}
console.timeEnd('2次改进后冒泡排序耗时');
return array;
}
// 选择排序(Selection-sort)是一种简单直观的排序算法。
// 它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
// 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
// 以此类推,直到所有元素均排序完毕。
// 选择排序 时间复杂度O(n2) 空间复杂度O(n1) 不稳定
function selectionSort(array) {
console.time('选择排序耗时');
let len = array.length;
let minIndex;
for (let i = 0; i < len; i++) {
minIndex = i;
for (let j = i + 1; j < len; j++) {
if (array[j] < array[minIndex]) {
minIndex = j;
}
}
swap(array, minIndex, i)
}
console.timeEnd('选择排序耗时');
return array;
}
// 插入排序(Insertion - Sort)的算法描述是一种简单直观的排序算法。
// 它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
// 插入排序在实现上,通常采用in - place排序(即只需用到O(1)的额外空间的排序),
// 因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
// 也就是说,左侧是排好的序列,从右侧第一个位置拿出1个元素,不断跟左侧对比,比左面小就把对比的元素往后挪一位留出1个空
// 直到不比左边小,把这个坑换成对比的元素
// 插入排序 O(n2) O(1) 稳定
function insertionSort(array) {
console.time('插入排序耗时');
let len = array.length;
for (let i = 1; i < len; i++) {
let j = i - 1;
let key = array[i];
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
console.timeEnd('插入排序耗时');
return array;
}
// 改进插入排序: 查找插入位置时使用二分查找的方式
function insertionSort2(array) {
console.time('二分插入排序耗时');
let len = array.length;
for (let i = 1; i < len; i++) {
let key = array[i];
let left = 0, right = i - 1;
while (left <= right) {
let middle = Math.floor(left + (right - left) / 2);
if (array[middle] < key) {
left = middle + 1;
} else {
right = middle - 1;
}
}
for (let j = i - 1; j >= left; j--) {
array[j + 1] = array[j];
}
array[left] = key;
}
console.timeEnd('二分插入排序耗时');
return array;
}
// 希尔排序的核心在于间隔序列的设定。
// 既可以提前设定好间隔序列,也可以动态的定义间隔序列。
// 动态定义间隔序列的算法是《算法(第4版》的合著者Robert Sedgewick提出的。
// 希尔排序 O(nlog2n) O(1) 不稳定
function shellSort(array) {
console.time('希尔排序耗时');
const interval = 5;
let len = array.length;
let gap = 1;
while (gap < len / interval) {
gap = gap * interval + 1;
}
for (gap; gap > 0; gap = Math.floor(gap / interval)) {
for (let right = gap; right < len; right++) {
let key = array[right];
let left = right - gap;
for (; left >= 0 && array[left] > key; left = left - gap) {
array[left + gap] = array[left];
}
array[left + gap] = key;
}
}
console.timeEnd('希尔排序耗时');
return array;
}
// 归并排序是建立在归并操作上的一种有效的排序算法。
// 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
// 归并排序是一种稳定的排序方法。
// 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
// 若将两个有序表合并成一个有序表,称为2 - 路归并。
// 归并排序 O(nlogn) O(nlogn) 稳定
function mergeSort(array) {
let len = array.length;
if (len < 2) return array;
let middle = Math.floor(len / 2);
let left = array.slice(0, middle);
let right = array.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
let result = [];
while (left.length > 0 && right.length > 0) {
if (left[0] < right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
if (left.length > 0) {
result = result.concat(left);
}
if (right.length > 0) {
result = result.concat(right);
}
return result;
}
// 快速排序的基本思想:
// 通过一趟排序将待排记录分隔成独立的两部分,
// 其中一部分记录的关键字均比另一部分的关键字小,
// 则可分别对这两部分记录继续进行排序,以达到整个序列有序。
// 快速排序 时间复杂度O(nlogn) 空间复杂度O(nlogn) 不稳定
function quickSort(arr) {
if (arr.length <= 1) return arr;
let kValue = arr.splice(Math.floor(arr.length / 2), 1)[0];
let left = [];
let right = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] <= kValue) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([kValue], quickSort(right));
}
// 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
// 堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
// 堆排序 时间复杂度O(nlogn) 空间复杂度O(1) 不稳定
function heapSort(array) {
let len = array.length;
for (let i = Math.floor(len / 2) - 1; i >= 0; i--) {
heapify(array, i, len);
}
for (let j = len - 1; j >= 1; j--) {
swap(array, 0, j);
heapify(array, 0, --len);
}
return array;
}
function heapify(array, i, size) {
let lastIndex = size - 1;
while (true) {
let leftIndex = i * 2 + 1;
let rightIndex = i * 2 + 2;
let current = i;
if (leftIndex <= lastIndex && array[leftIndex] > array[current]) {
current = leftIndex;
}
if (rightIndex <= lastIndex && array[rightIndex] > array[current]) {
current = rightIndex;
}
if (current !== i) {
swap(array, current, i);
i = current;
} else {
break;
}
}
}
// 计数排序(Counting sort)是一种稳定的排序算法。
// 计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。
// 然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。
// 计数排序 时间复杂度O(n+k) 空间复杂度O(k) 稳定
function countingSort(array) {
let len = array.length;
if (len <= 1) return array;
let result = [];
let C = [];
let min = max = array[0];
for (let i = 0; i < len; i++) {
min = Math.min(min, array[i]);
max = Math.max(max, array[i]);
C[array[i]] = (C[array[i]] ? C[array[i]] : 0) + 1;
}
// 这个是为了计算C的第j个位置前面加上自己上的值合起来,就是在最终结果中的索引值,
// 举例:比如C[17] = 2,C[18] = 1,原来表示有2个17,1个18
// 处理完之后,C[18] = 2+1 = 3, 表示C[18]前面包括自己有3个数,那么在最终数组中,18的索引就是(3-1)=2.
for (let j = min; j < max; j++) {
C[j + 1] = (C[j + 1] || 0) + (C[j] || 0)
}
for (let k = len - 1; k >= 0; k--) {
let index = C[array[k]] - 1;
result[index] = array[k];
C[array[k]]--;
}
return result;
}
// 桶排序 (Bucket sort)的工作的原理:
// 假设输入数据服从均匀分布,将数据分到有限数量的桶里,
// 每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排
// 桶排序 时间复杂度O(n+k) 空间复杂度O(n+k) 稳定
function bucketSort(array, bucketNum) {
let len = array.length;
if (len <= 1) return array;
let result = [];
let buckets = [];
let min = max = array[0];
bucketNum = bucketNum || 10;//桶数量
for (let i = 1; i < len; i++) {
min = min <= array[i] ? min : array[i];
max = max >= array[i] ? max : array[i];
}
let space = Math.ceil((max - min + 1) / bucketNum); // 每个桶要装多少个
for (let j = 0; j < len; j++) {
let bucketIndex = Math.floor((array[j] - min) / space);
if (Array.isArray(buckets[bucketIndex])) {//插入排序
let k = buckets[bucketIndex].length - 1;//从当前需要插入的bucket最后1个元素开始对比
while (k >= 0 && buckets[bucketIndex][k] > array[j]) {
buckets[bucketIndex][k + 1] = buckets[bucketIndex][k]
k--;
}
buckets[bucketIndex][k + 1] = array[j];
} else {
buckets[bucketIndex] = [];
buckets[bucketIndex].push(array[j]);
}
}
// 最后把桶合并起来
let n = 0;
while (n < bucketNum) {
result = result.concat(buckets[n]);
n++
}
return result;
}
// 基数排序是按照低位先排序,然后收集;
// 再按照高位排序,然后再收集;
// 依次类推,直到最高位。
// 有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。
// 最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
// 基数排序基于分别排序,分别收集,所以是稳定的。
// 基数排序 时间复杂度O(n*k) 空间复杂度O(n+k) 稳定
function radixSort(array) {
if (array.length <= 1) {
return array;
}
let max = array[0];
for (let i = 1; i < array.length; i++) {
max = Math.max(max, array[i]);
}
let maxLength = (max + "").length;//最大的数字是几位数
let dev = 1, mod = 10;
for (let i = 0; i < maxLength; i++, dev *= 10, mod *= 10) {
let counter = [];
for (let j = 0; j < array.length; j++) {
let curNum = parseInt((array[j] % mod) / dev);//从后往前分别找出要处理的第几位数字
if (!Array.isArray(counter[curNum])) {
counter[curNum] = [];
}
counter[curNum].push(array[j]);
}
let pos = 0;
for (let k = 0; k < counter.length; k++) {
if (Array.isArray(counter[k])) {
while (counter[k].length > 0) {
array[pos] = counter[k].shift();
pos++;
}
}
}
}
return array;
}
const data = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
bubleSort([...data]);
bubleSort2([...data]);
bubleSort3([...data]);
console.log('======================');
selectionSort([...data]);
console.log('======================');
insertionSort([...data]);
insertionSort2([...data]);
console.log('======================');
shellSort([...data]);
console.log('======================');
console.time('归并排序耗时');
mergeSort([...data]);
console.timeEnd('归并排序耗时');
console.log('======================');
console.time('快速排序耗时');
quickSort([...data]);
console.timeEnd('快速排序耗时');
console.log('======================');
console.time('堆排序耗时');
heapSort([...data]);
console.timeEnd('堆排序耗时');
console.log('======================');
console.time('计数排序耗时');
countingSort([...data]);
console.timeEnd('计数排序耗时');
console.log('======================');
console.time('桶排序耗时');
bucketSort([...data], 4);
console.timeEnd('桶排序耗时');
console.log('======================');
console.time('基数排序耗时');
radixSort([...data]);
console.timeEnd('基数排序耗时');
console.log('======================');
Promise
class MyPromise {
// 用static创建静态属性,用来管理状态
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
// 构造函数:通过new命令生成对象实例时,自动调用类的构造函数
constructor(func) { // 给类的构造方法constructor添加一个参数func
this.PromiseState = MyPromise.PENDING;// 指定Promise对象的状态属性 PromiseState,初始值为pending
this.PromiseResult = null;// 指定Promise对象的结果 PromiseResult
this.onFulfilledCallbacks = [];// 保存成功回调
this.onRejectedCallbacks = [];// 保存失败回调
try {
/**
* func()传入resolve和reject,
* resolve()和reject()方法在外部调用,这里需要用bind修正一下this指向
* new 对象实例时,自动执行func()
*/
func(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
// 生成实例时(执行resolve和reject),如果报错,就把错误信息传入给reject()方法,并且直接执行reject()方法
this.reject(error);
}
}
resolve(result) {// result为成功态时接收的终值
// 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
if (this.PromiseState === MyPromise.PENDING) {
this.PromiseState = MyPromise.FULFILLED;
this.PromiseResult = result;
/**
* 在执行resolve或者reject的时候,遍历自身的callbacks数组,
* 看看数组里面有没有then那边 保留 过来的 待执行函数,
* 然后逐个执行数组里面的函数,执行的时候会传入相应的参数
*/
this.onFulfilledCallbacks.forEach(callback => {
callback(result);
})
}
}
reject(reason) { // reason为拒绝态时接收的终值
// 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
if (this.PromiseState === MyPromise.PENDING) {
this.PromiseState = MyPromise.REJECTED;
this.PromiseResult = reason;
this.onRejectedCallbacks.forEach(callback => {
callback(reason);
})
}
}
/**
* [注册fulfilled状态/rejected状态对应的回调函数]
* @param {function} onFulfilled fulfilled状态时 执行的函数
* @param {function} onRejected rejected状态时 执行的函数
* @returns {function} newPromsie 返回一个新的promise对象
*/
then(onFulfilled, onRejected) {
// 2.2.7规范 then 方法必须返回一个 promise 对象
const promise2 = new MyPromise((resolve, reject) => {
if (this.PromiseState === MyPromise.FULFILLED) {
/**
* 为什么这里要加定时器setTimeout?
* 2.2.4规范 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1
* 这里的平台代码指的是引擎、环境以及 promise 的实施代码。
* 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
* 这个事件队列可以采用“宏任务(macro-task)”机制,比如setTimeout 或者 setImmediate; 也可以采用“微任务(micro-task)”机制来实现, 比如 MutationObserver 或者process.nextTick。
*/
setTimeout(() => {
try {
if (typeof onFulfilled !== "function") {
// 2.2.7.3规范 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
resolve(this.PromiseResult)
} else {
// 2.2.7.1规范 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x),即运行resolvePromise()
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (error) {
// 2.2.7.2规范 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
reject(error);// 捕获前面onFulfilled中抛出的异常
}
})
} else if (this.PromiseState === MyPromise.REJECTED) {
setTimeout(() => {
try {
if (typeof onRejected !== "function") {
// 2.2.7.4规范 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
reject(this.PromiseResult)
} else {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (error) {
reject(error)
}
})
} else if (this.PromiseState === MyPromise.PENDING) {
// pending 状态保存的 onFulfilled() 和 onRejected() 回调也要符合 2.2.7.1,2.2.7.2,2.2.7.3 和 2.2.7.4 规范
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
if (typeof onFulfilled !== "function") {
resolve(this.PromiseResult)
} else {
let x = onFulfilled(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (error) {
reject(error)
}
})
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
if (typeof onRejected !== "function") {
reject(this.PromiseResult)
} else {
let x = onRejected(this.PromiseResult);
resolvePromise(promise2, x, resolve, reject);
}
} catch (error) {
reject(error)
}
})
});
}
})
return promise2;
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
/**
* finally
* @param {*} callBack 无论结果是fulfilled或者是rejected,都会执行的回调函数
* @returns
*/
finally(callback) {
return this.then(callback, callback);
}
/**
* Promise.resolve()
* @param {[type]} value 要解析为 Promise 对象的值
*/
static resolve(value) {
// 如果这个值是一个 promise ,那么将返回这个 promise
if (value instanceof MyPromise) {
return value;
} else if (value instanceof Object && 'then' in value) {
// 如果这个值是thenable(即带有`"then" `方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
return new MyPromise((resolve, reject) => {
value.then(resolve, reject);
})
}
// 否则返回的promise将以此值完成,即以此值执行`resolve()`方法 (状态为fulfilled)
return new MyPromise((resolve) => {
resolve(value);
})
}
/**
* myPromise.reject
* @param {*} reason 表示Promise被拒绝的原因
* @returns
*/
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
/**
* Promise.all
* @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
* @returns
*/
static all(promises) {
return new MyPromise((resolve, reject) => {
// 参数校验
if (Array.isArray(promises)) {
let result = [];// 存储结果
let count = 0;// 计数器
// 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
if (promises.length === 0) {
return resolve(promises);
}
promises.forEach((item, index) => {
MyPromise.resolve(item).then(
value => {
count++;
result[index] = value;// 每个promise执行的结果存储在result中
count === promises.length && resolve(result);// Promise.all 等待所有都完成(或第一个失败)
},
reason => {
/**
* 如果传入的 promise 中有一个失败(rejected),
* Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成
*/
reject(reason);
}
);
});
} else {
return reject(new TypeError("Argument is not iterable"));
}
});
}
/**
* Promise.allSettled
* @param {*} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
* @returns
*/
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
// 参数校验
if (Array.isArray(promises)) {
let result = [];// 存储结果
let count = 0;// 计数器
if (promises.length === 0) {
return resolve(promises);
}
promises.forEach((item, index) => {
MyPromise.resolve(item).then(
value => {
count++;
result[index] = {
status: "fulfilled",
value
};
count === promises.length && resolve(result);
},
reason => {
count++;
result[index] = {
status: "rejected",
reason
};
count === promises.length && resolve(result);
}
);
});
} else {
return reject(new TypeError("Argument is not iterable"));
}
})
}
/**
* Promise.any()
* @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
* @returns
*/
static any(promises) {
return new MyPromise((resolve, reject) => {
if (Array.isArray(promises)) {
let errors = [];// 存储结果
let count = 0;// 计数器
// 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
if (promises.length === 0) {
return reject(new AggregateError('All promises were rejected'));
}
promises.forEach((item, index) => {
MyPromise.resolve(item).then(
value => {
resolve(value)
},
reason => {
count++;
errors.push(reason);
count === promises.length && reject(new AggregateError(errors));
}
);
});
} else {
return reject(new TypeError("Argument is not iterable"));
}
})
}
/**
* Promise.race()
* @param {iterable} promises 可迭代对象,类似Array。详见 iterable。
* @returns
*/
static race(promises) {
return new MyPromise((resolve, reject) => {
if (Array.isArray(promises)) {
if (promises.length > 0) {
promises.forEach((item, index) => {
MyPromise.resolve(item).then(
resolve,
reject
);
});
}
} else {
return reject(new TypeError("Argument is not iterable"));
}
})
}
}
/**
* 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled或onRejected的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1规范 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (x === promise2) {
throw new TypeError("Chaining cycle detected for promise");
}
if (x instanceof MyPromise) {
/**
* 2.3.2 如果 x 为 Promise ,则使 promise2 接受 x 的状态
* 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
*/
x.then(y => {
resolvePromise(promise2, y, resolve, reject);
}, reject);
} else if (x !== null && (typeof x === "object" || (typeof x === "function"))) {
// 2.3.3 如果 x 为对象或函数
try {
// 2.3.3.1 把 x.then 赋值给 then
var then = x.then;
} catch (error) {
// 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
/**
* 2.3.3.3
* 如果 then 是函数,将 x 作为函数的作用域 this 调用之。
* 传递两个回调函数作为参数,
* 第一个参数叫做 `resolvePromise` ,第二个参数叫做 `rejectPromise`
*/
if (typeof then === 'function') {
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
let called = false; // 避免多次调用
try {
then.call(
x,
// 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
if (called) {
return;
}
called = true;
resolvePromise(promise2, y, resolve, reject);
},
// 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) {
return;
}
called = true;
reject(r);
}
)
} catch (error) {
/**
* 2.3.3.3.4 如果调用 then 方法抛出了异常 e
* 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
*/
if (called) {
return;
}
called = true;
/**
* 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
*/
reject(error);
}
} else {
// 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise
resolve(x)
}
} else {
// 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
return resolve(x);
}
}
async/await
正常代码:
const getData = () => new Promise(resolve => setTimeout(() => resolve("test-normal"), 1000))
async function test() {
const data = await getData()
console.log('test-data: ', data);
const data2 = await getData()
console.log('test-data2: ', data2);
return 'success'
}
// 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
test().then(res => console.log(res))
//test-data: test-normal
//test-data2: test-normal
//success
实现代码:
const getData1 = () => new Promise(resolve => setTimeout(() => resolve("testG-data"), 1000));
// 第一步:await编译为yield
// 第二步:函数变为generator
function* testG() {
const data1 = yield getData1();
console.log('testG-data1: ', data1);
const data2 = yield getData1();
console.log('testG-data2: ', data2);
return 'success'
}
// 核心函数,用来替代async操作符
function asyncGenerator(genFunc) {
//返回一个新的函数,类似于async test(){}返回的包裹函数
return function () {
//获取迭代器,相当于testG()执行后返回的迭代器
const gen = genFunc.apply(this, arguments);
//返回一个promise,我们知道上面例子中async test()执行后 返回的就是1个promise
// 因为外部是用.then的方式 或者await的方式去使用这个函数的返回值的
return new Promise((resolve, reject) => {
// 内部定义一个step函数 用来一步一步的跨过yield的阻碍
// arg参数则是用来把promise resolve出来的值交给下一个yield
function step(arg) {
let genResult;
// 这个方法需要包裹在try catch中
// 如果报错了 就把promise给reject掉 外部通过.catch可以获取到错误
try {
//我们发现,每次调用next,都是执行yield后面的函数
//下一次再调用next时,才是把上一次的返回值赋值,然后执行下一次的yield后面的函数
genResult = gen.next(arg);
} catch (error) {
return reject(error);
}
const { value, done } = genResult;//这个value当还没有结束时,就是1个promise,如果结束了,就是最后return的值
if (done) {
// 如果已经完成了 就直接resolve这个promise
// 这个done是在最后一次调用next后才会为true
// 以本文的例子来说 此时的结果是 { done: true, value: 'success' }
// 这个value也就是generator函数最后的返回值
return resolve(value);
} else {
//递归调用
// 除了最后结束的时候外,每次调用gen.next()
// 其实是返回 { value: Promise, done: false } 的结构,
// 这里要注意的是Promise.resolve可以接受一个promise为参数
// 并且这个promise参数被resolve的时候,这个then才会被调用
// 这个value对应的是yield后面的promise
return Promise.resolve(value).then(val => step(val));
}
}
step();
})
}
}
const asyncTest = asyncGenerator(testG);
asyncTest().then(res => console.log(res))
//testG-data1: testG-data
//testG-data2: testG-data
//success
实现一个once 函数,传入函数参数只执行一次
function once(func){
var tag=true;
return function(){
if(tag==true){
func.apply(null,arguments);
tag=false;
}
return undefined
}
}