1.手写new操作符
function myNew(Fn) {
if (typeof Fn !== 'function') throw new TypeError('This is not a constructor') // Fn校验
var args = Array.from(arguments).slice(1) // 取入参
var obj = {} // 1.创建一个空的简单JavaScript对象(即` {} `)
obj.__proto__ = Fn.prototype // 2. 为步骤1新创建的对象添加属性` __proto__ `,将该属性链接至构造函数的原型对象
var res = Fn.call(obj, ...args) // 3. 将步骤1新创建的对象作为this的上下文并传入参数;
return Object(res) === res ? res : obj // 4. 如果该函数没有返回对象,则返回this。
}
var Fn = function (name, age) {
this.name = name
this.age = age
}
let newobj = myNew(Fn, 'xiaoming', '15')
console.log(newobj)
2.手写promise
function myPromise(callback) {
// 每一个promise对象内部,都会有一个状态信息, 有三个可能值
// pending 状态 表示等待状态, promise对象的默认状态,
// resolve 状态 表示成功状态, 当调用了resolve函数时,状态变成成功状态
// reject 状态 表示失败状态, 当调用了reject函数时,状态变成失败状态
// 注意: 状态值只能变化一次,一旦变更为成功或失败状态,则会一致保持这个状态
this.state = "pending"; // 初始化状态
let self = this
function resolve(data) {
self.state = "resolve" // 修改状态值为成功
self.value = data; // 成功时value属性记录成功数据
self.success(data) // 成功时,调用then函数中成功回调
}
function reject(err) {
self.state = "reject" // 修改状态值为失败
self.msg = err; // 失败时用msg属性记录失败信息
self.fail(err) // 失败时,调用then函数中失败回调
}
// MyPromise在创建对象时,其回调函数会直接执行, 所以在构造函数中直接调用, 并传入成功和失败状态对应的函数
callback(resolve, reject)
}
// / then方法是promise对象调用的方法,所以定义到构造函数原型中
MyPromise.prototype.then = function (success, fail = () => { }) {
// 由于then函数可以在任意时刻调用, 所以调用then时,promise状态值不确定
if (this.state == "pending") {
// 说明此时异步任务还未结束, 还不能调用success或fail, 此时可以把success和fail这个回调函数传入this这个promise对象, 在异步任务结束后调用
this.success = success;
this.fail = fail;
}
if (this.state == "resolve") {
// 如果当前状态是成功状态, 则调用then参数中的第一个成功回调
success(this.value) // 参数传入成功数据
}
if (this.state == "reject") {
// 如果当前状态是失败状态, 则调用then参数中的第二个失败回调
fail(this.msg) // 参数传入失败信息
}
// then函数返回当前promise对象, 用于链式调用
return this;
}
3.手写promise.all
Promise.all = function (iterators) {
return new Promise((resolve, reject) => {
if (!iterators || iterators.length === 0) {
resolve([])
} else {
// 计算器,用于判断所有任务是否执行完成
let count = 0;
// 结果数组
let result = [];
// 执行数组长度
let len = iterators.length
for (let i = 0; i < len; i++) {
// 考虑到iterators[i]可能是普通对象,则统一包装为Promise对象
Promise.resolve(iterators[i]).then((data) => {
// 按顺序保存对应的结果
result[i] = data;
// 判断++count 的次数是否等于 传入执行数组的长度
if (++count === len) {
resolve(result);
}
}, (err) => {
// 任何一个Promise对象执行失败,则调用reject()方法
reject(err);
})
}
}
})
}
4.防抖节流
防抖
function debounce(fn, wait) {
let time = null
return function () {
let that = this
args = arguments
if (time) {
clear(time)
time = null
}
time = setInterval(() => {
fn.apply(that, args)
}, wait)
}
}
节流
function throttle(fn, wait) {
let curTime = Date.now();
return function () {
let that = this,
args = arguments,
nowTime = Date.now();
if (nowTime - curTime > wait) {
curTime = Date.now();
fn.apply(that, args)
}
}
}
function throttle(fn,delay) {
let timeout
return function() {
let args = arguments;//注意如果要传参的话 这句不能省略
if(!timeout){
timeout = setTimeout(()=>{
timeout = null;
fn.apply(this,args)
},delay)
}
}
}
5.call和apply手写
call
function myCall(context) {
if (Object.prototype.toString.call(this) !== "[object,function]") {
console.error("type error");
}
let args = [...arguments].slice(1),
result = null;
context = context || window;
// 将调用函数设为对象的方法
// 将调用函数设为对象的方法
context.fn = this;
// 调用函数
result = context.fn(...args);
// 将属性删除
delete context.fn;
return result;
}
apply
function myApply(context) {
if (Object.prototype.toString.call(this) !== "[object,function]") {
console.error("type error");
}
let res = null
var context = context || window
context.fn = this
//判断有没有传入第二个参数数组
if (arguments[1]) {
res = context.fn(...arguments[1])
} else {
res = context.fn()
}
delete context.fn
return res
}
6.函数柯里化
function createCurry(func, args) {
var arity = func.length;
var args = args || [];
return function () {
var _args = [].slice.call(arguments);
[].push.apply(_args, args);
// 如果参数个数小于最初的func.length,则递归调用,继续收集参数
if (_args.length < arity) {
return createCurry.call(this, func, _args);
}
// 参数收集完毕,则执行func
return func.apply(this, _args);
}
}
7.深拷贝
function deepClone(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
8.数组去重
let arr = [1, 2, 1, 2, 11, 1]
// 1)
function noRepeat() {
Array.from(new Set(arr))
}
// 2)
function noRepeat(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
// 3)
function noRepeat(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length; j++) {
if (arr[i] == arr[j] && i != j) {//将后面重复的数删掉
arr.splice(j, 1);
}
}
}
return arr;
}
7.继承(es5)
function Parent() {
this.name = '无法传参';
this.loves = ['敲代码', '摄影']
}
Parent.prototype.getName = function () {
console.log(this.name);
}
Parent.prototype.getLoves = function () {
console.log(this.loves);
}
function Child(age) {
this.age = age
}
// 原型链继承
// 优点
// 1、父类方法可以复用
// 缺点
// 1、父类的所有 引用属性(如:loves) 会被所有子类共享,当其中一个子类的引用属性被修改后,会影响其他子类
// 2、子类型实例不能给父类型构造函数传参
Child.prototype = new Parent()
Child.prototype.getAge = function () {
console.log(this.age);
}
// 构造函数继承
// 优势
// 1、可以在子类构造函数中向父类传参数
// 2、父类的引用属性不会被共享
// 缺点
// 1、子类不能访问父类原型 User.prototype 上定义的方法,因此所有方法属性都写在构造函数中,每次创建实例都会初始化
function Child(name, age) {
// 子类继承父类的属性
Parent.call(this, name);
// 实例属性
this.age = age
}
Child.prototype.getAge = function () {
console.log(this.age);
}
// 组合继承
function Child(name, age) {
// 子类继承父类的属性
Parent.call(this, name);
// 实例属性
this.age = age
}
Child.prototype = new Parent()
Child.prototype.getAge = function () {
console.log(this.age);
}
8.继承(es6)
class Parent {
constructor(name) {
this.name = name;
this.loves = ['敲代码', '摄影']
}
getName() {
console.log(this.name)
};
getLoves() {
console.log(this.loves)
};
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类的构造函数
this.age = age;
};
getAge() {
console.log(this.age)
};
}
9.实现add(1)(2)==3
// 题意的答案
const add = (num1) => (num2)=> num2 + num1;
// 我自己整了一个加强版 可以无限链式调用 add(1)(2)(3)(4)(5)....
function add(x) {
// 存储和
let sum = x;
// 函数调用会相加,然后每次都会返回这个函数本身
let tmp = function (y) {
sum = sum + y;
return tmp;
};
// 对象的toString必须是一个方法 在方法中返回了这个和
tmp.toString = () => sum
return tmp;
}
alert(add(1)(2)(3)(4)(5))
10.数组转化为tree
let arr = [
{ id: 0, name: '1', parent: -1, childNode: [] },
{ id: 1, name: '1', parent: 0, childNode: [] },
{ id: 99, name: '1-1', parent: 1, childNode: [] },
{ id: 111, name: '1-1-1', parent: 99, childNode: [] },
{ id: 66, name: '1-1-2', parent: 99, childNode: [] },
{ id: 1121, name: '1-1-2-1', parent: 112, childNode: [] },
{ id: 12, name: '1-2', parent: 1, childNode: [] },
{ id: 2, name: '2', parent: 0, childNode: [] },
{ id: 21, name: '2-1', parent: 2, childNode: [] },
{ id: 22, name: '2-2', parent: 2, childNode: [] },
{ id: 221, name: '2-2-1', parent: 22, childNode: [] },
{ id: 3, name: '3', parent: 0, childNode: [] },
{ id: 31, name: '3-1', parent: 3, childNode: [] },
{ id: 32, name: '3-2', parent: 3, childNode: [] }
]
function arrToTree(arr, parentId) {
// 判断是否是顶层节点,如果是就返回。不是的话就判断是不是自己要找的子节点
const filterArr = arr.filter(item => {
return parentId === undefined ? item.parent === -1 : item.parent === parentId
})
// 进行递归调用把子节点加到父节点的 childNode里面去
filterArr.map(item => {
item.childNode = arrToTree(arr, item.id)
return item
})
return filterArr
}
arrToTree(arr)
11.手写instanceof
function instanceOf1(obj, fun) {
//判断数据类型
if (typeof obj !== 'object' || obj === null || typeof fun !== 'function') {
return false
}
//实现循环查找
let proto = obj.__proto__
while (true) {
//到达原型链尽头,也就是查找循环终止的条件
if (proto === null) return false
if (proto === fun.prototype) return true
//obj的原型不是fun的prototype的时候,沿着原型链继续向上寻找
proto = proto.__proto__
}
}
eventLoop 手写题
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
- 执行整段代码,遇到
console.log('script start')直接打印结果,输出script start - 遇到定时器了,它是宏任务,先放着不执行
- 遇到
async1(),执行async1函数,先打印async1 start,下面遇到await怎么办?先执行async2,打印async2,然后阻塞下面代码(即加入微任务列表),跳出去执行同步代码 - 跳到
new Promise这里,直接执行,打印promise1,下面遇到.then(),它是微任务,放到微任务列表等待执行 - 最后一行直接打印
script end,现在同步代码执行完了,开始执行微任务,即await下面的代码,打印async1 end - 继续执行下一个微任务,即执行
then的回调,打印promise2 - 上一个宏任务所有事都做完了,开始下一个宏任务,就是定时器,打印
settimeout
所以最后的结果是:script start、async1 start、async2、promise1、script end、async1 end、promise2、settimeout