数据类型判断:
function myTypeof(tar) {
return Object.prototype.toString.call(tar).slice(8, -1).toLowerCase();
}
instanceof实现
function myInstanceof(tar, type) {
if (!Object.getPrototypeOf(tar)) {
return false;
}
return Object.getPrototypeOf(tar) === type.prototype ? true : myInstanceof(tar.__proto__, type);
}
// 测试
let date = new Date
myInstanceof(date, Object)
- 原型链:
// __proto__ 是每个对象都有的属性;prototype是构造函数的属性;他们指向的是同一个对象(原型)。
// 例如:
function F(){};
var f = new F();
f.__proto__ === F.prototype //true
原型链
call,apply和bind实现
- call 和 apply 实现:
Function.prototype.myCall = function(context, ...args) {
context = context || window;
context.fn = this;
const res = context.fn(...args);
delete context.fn;
return res;
}
// apply 只是参数变成数组
Function.prototype.myApply = function(obj, args = []) {
if (!Array.isArray(args)) {
throw Error('not array');
}
context = obj || window;
context.fn = this;
const res = context.fn(...args);
delete context.fn;
return res;
}
// 测试
function test(num) {
this.age = num;
console.log(this, this.name)
}
let obj = {
name: 'sss'
}
test.myCall(obj, 12)
test.myCall(null, 13)
- bind实现
// bind 返回是函数以外,它 的参数和 call 一样,bind会收集参数,
// 子函数被调用时,this指向bind的第一个参数; 当子函数使用new 调用时,该参数无效
Function.prototype.myBind = function(obj, ...fArgs) {
const context = obj || window;
const fn = this;
return function(...childArgs) {
const args = [...fArgs, ...childArgs];
// new 执行函数时,使用call修改了函数的指向为实例对象
return this !== window ? fn.call(this, args) : fn.call(context, args);
}
}
// 测试
function Person(name, age) {
this.name = name;
this.age = age;
}
let o = {};
var _Person = Person.bind(o);
let p = new _Person('sx', 12); // o为{},p为Person {name: 1, age: 2}
let res = _Person('sx', 12); // res为undefined,o为{name: 1, age: 2}
数组扁平化
let arr = [1, 2, 3, [2, [23]], 0];
// 无脑拍平
Array.prototype.mySupFlat = function() {
return this.toString().split(',');
}
// flat 实现,参数 n 控制展开层数
Array.prototype.myFlat = function(n) {
let arr = this.slice();
let tmp = [];
while (n > 0) {
arr.forEach(item => {
Array.isArray(item) ? tmp.push(...item) : tmp.push(item);
})
arr = tmp;
tmp = [];
n--;
}
return arr;
}
数组去重
// es5 需要双层遍历(数组中可能有引用类型)
// es6
Array.prototype.unique = function() {
let set = new Set(this);
return [...set];
}
object.create实现
object.create实际是将参数obj作为空对象的原型,然后返回空对象。如果是参数obj是null,则返回的是原型为null的空对象
Object.prototype.myCreate = function (proto, propertyObject = undefined) {
if (propertyObject === null) {
// 这里没有判断propertyObject是否是原始包装对象
throw 'TypeError'
} else {
function Fn () {}
Fn.prototype = proto
const obj = new Fn()
// 第二个参数
if (propertyObject !== undefined) {
// obj会新增propertyObject的key作为属性,该方法默认新增属性不可枚举
Object.defineProperties(obj, propertyObject)
}
if (proto === null) {
// 创建一个没有原型对象的对象,Object.create(null)
obj.__proto__ = null
}
return obj
}
}
第二个参数
promise系列实现
- promise 实现(非A+)
promise的then和catch理解
const STATUS = {
pend: 'pendding',
res: 'fulfilled',
rej: 'rejected'
}
class myPromise {
constructor(exeuctor) {
this.status = STATUS.pend;
this.val = null;
this.reason = null;
// 1个promise 实例,可以调用多次 then,用于多次触发 then 注册的回调。
this.onResolve = [];
this.onReject = [];
// es6 class内默认开启严格模式,如果实例函数单独调用,this为undefined
// 严格模式下,funcion 声明有问题,没有this,
// => 函数刚好, this 指向实例
const resolve = val => {
if (this.status !== STATUS.pend) return;
this.val = val;
// 只要有 resolve,就修改状态,防止下一次的resolve
this.status = STATUS.res;
//用setTimeout模拟「微任务」执行,延迟绑定 then 收集的回调函数,
// 不延迟绑定时,如果promise 的resolve 是同步执行的话,此时还未执行then,找不到注册的回调(onResolve是空数组),影响执行时机
// promise.then 的「微任务」执行时机就在这,不然就是主函数任务。
setTimeout(() => {
// 循环执行 then 收集的回调,
this.onResolve.forEach(fn => fn(this.val));
}, 0)
};
const reject = reason => {
if (this.status !== STATUS.pend) return;
this.reason = reason;
console.log('reject this', JSON.stringify(this))
this.status = STATUS.rej;
setTimeout(() => {
this.onReject.forEach(fn => {
fn(this.reason)
});
}, 0)
}
// exeuctor 可能不是函数
try {
exeuctor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onResolve, onReject) {
onResolve = onResolve ? onResolve : res => res;
onReject = onReject ? onReject : err => err;
// 返回新promise,用于链式调用
return new myPromise((retRes, retRej) => {
// then 的执行时机跟 resolve 执行的时机相比 『可前可后』
// 当实例的 then 调用,resolve 和 reject 未触发时,则继续收集 then 注册的回调
// 如果在还没有收集到处理函数 onResolve时,this.onResolve是空数组,不会报错
if (this.status === STATUS.pend) {
this.onResolve.push(() => {
// promise1 的 onResolve(this.val) 可能有 return 「值」,把该值作为下个promise2 中 resolve(「值」),链式透传
// retRes(onResolve(this.val)); 此方法链式调用有问题
resolvePromise(retRes, retRej, onResolve, this.val);
});
this.onReject.push(() => {
// retRej(onReject(this.reason));
resolvePromise(retRes, retRej, onReject, this.reason);
});
}
// 当实例的 then 调用,resolve 也已触发时,则直接在「微任务」中触发当前的注册回调,并使promise2 的 resolve 包裹
if (this.status === STATUS.res) {
setTimeout(() => {
// retRes(onResolve(this.val));
resolvePromise(retRes, retRej, onResolve, this.val);
}, 0)
}
if (this.status === STATUS.rej) {
setTimeout(() => {
// retRej(onReject(this.reason));
resolvePromise(retRes, retRej, onReject, this.reason);
}, 0)
}
});
}
catch(errHandle) {
return this.then(() => { }, errHandle);
}
finally(cb) {
return this.then(cb, cb);
}
}
function resolvePromise(retRes, retRej, fn, args) {
// promise1的then注册的onRes或onRej执行时,不同的返回值和类型有不同的触发效果
// 1. 只要then注册的回调函数触发没有返回值,则then 返回的promise2 状态就是fulfilled,value是 undefined
// 2. 只要触发的函数返回的是值 X, 那么promise2 状态就是fulfilled,value是 X,[注意:非promise的普通值返回时,被Promise.resolve(x)包装]
// 3. 只要触发的函数返回的是promise 实例,则promise2 状态和值跟该返回实例保持一致
// 4. 只要触发的函数有「错误」,则promise2的状态是rejected,reason就是抛出的异常对象 e
try {
const res = fn(args);
if (res instanceof myPromise) { // 如果返回的是promise 实例时,则promise2 的状态跟此实例状态和值保持一致
res.then(val => {
retRes(val)
}, reason => {
retRej(reason)
});
}
else {
// promise1的then注册的onRes或onRej执行时,如果不返回值,则promise2的状态变成fulfilled,value为undefined
// 如果onFulfilled或onRejected返回一个值x,那么promise2的状态是fulfilled, value是 X
retRes(res);
}
} catch (error) { // promise1的then注册的onRes或onRej执行时,如果有throw err 则在promise2 中reject
retRej(error);
}
}
- promise.all 实现
myPromise.all = function (promiseArr) {
if (promiseArr.length === 0) return myPromise.resolve([])
promiseArr = promiseArr.map(item => item instanceof myPromise ? item : myPromise.resolve(item));
const length = promiseArr.length;
let sum = 0, result = [];
return new myPromise((res, rej) => {
promiseArr.forEach((promise, index) => {
promise.then(value => {
result[index] = value;
sum++;
if (sum === length) {
res(result);
}
}, err => {
rej(err);
})
})
})
}
- promise.allSettled实现
myPromise.allSettled = function (promises) {
if (promises.length === 0) return myPromise.resolve([])
const _promises = promises.map(
item => item instanceof myPromise ? item : myPromise.resolve(item)
)
return new Promise((resolve, reject) => {
const result = []
let unSettledPromiseCount = _promises.length
_promises.forEach((promise, index) => {
promise.then((value) => {
result[index] = {
status: 'fulfilled',
value
}
unSettledPromiseCount -= 1
// resolve after all are settled
if (unSettledPromiseCount === 0) {
resolve(result)
}
}, (reason) => {
result[index] = {
status: 'rejected',
reason
}
unSettledPromiseCount -= 1
// resolve after all are settled
if (unSettledPromiseCount === 0) {
resolve(result)
}
})
})
})
}
new实现
function myNew(fn, ...args) {
let obj = {};
// 修改原型链,继承方法
obj.__proto__ = fn.prototype;
// 构造函数执行初始化,对obj增加属性
let ret = fn.call(obj, ...args); // apply:第二个参数是数组
// 当构造函数返回的是一个对象时,则new 返回该对象(res),如果无返回或者返回的是基本类型则返回最初的对象(obj)
return typeof ret === 'object' && ret !== null ? ret : obj;
}
函数柯里化
function cury(fn, ...fArgs) {
// 参数个数
const length = fn.length;
return function(...childArgs) {
const args = [...fArgs, ...childArgs];
return args.length >= length ? fn.apply(null, args) : cury(fn, ...args);
}
}
// 测试
function add(a, b ,c) {
console.log(a, b ,c, a+ b+c);
}
let fn = cury(add);
fn(1)(2)(3);
深拷贝
包含特殊对象的深拷贝
function deepCopy(obj, map = new WeakMap()) {
if (typeof obj === 'object' && obj !== null) {
// 防止循环引用
if (map.has(obj)) {
return map.get(obj);
}
map.set(obj, obj);
let res = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
value = obj[key];
res[key] = deepCopy(value, map);
}
}
// 特殊对象如date,map等的拷贝
if (!Array.isArray(res) && !Object.keys(res).length) {
res = new obj.constructor(obj);
}
return res;
}
else {
return obj
}
}
// 测试数据
var test = { name: "test", d: {age: 12} };
test.x = test;
var data = {
a: "123",
b: 123,
c: true,
d: [43, 2],
e: undefined,
f: null,
g: function () { console.log("g"); },
h: new Set([3, 2, null]),
i: Symbol("fsd"),
j: test,
k: new Map([["name", "张三"], ["title", "Author"]])
};
deepCopy(data);