1、fn.call(obj, args),call函数接收多个参数,第一个参数是指定函数执行时候的this,后面的一个或多个参数是调用函数fn的传参
Function.prototype.call = function() {
// 使用解构赋值获取要绑定的this对象和其他参数
let [context, ...args] = [...arguments]
// 要绑定的对象this为空
if (!context || typeof context !== 'object') {
args.unshift(context);
context = typeof window === 'undefined' ? global : window
}
let sym = Symbol()
context[sym] = this
// 保存返回值
let result = context[sym](...args)
// 删除该属性
delete(context[sym])
return result;
}
2、fn.apply(obj, args),第一个参数是函数执行时候的this,第二个参数是fn调用时候的参数组成的数组。
Function.prototype.apply = function(context, args) {
// 如果传入的参数不是Object的实例,如‘123’,抛出错误
if (args && !(args instance Object)) {
throw Error('wrong')
}
if (!context) {
context = typeof window === 'undefined' ? global : window;
}
let sym = Symbol()
context[sym] = this
let result = args ? context[sym](...args) : context[sym]();
delete context[sym]
return result;
}
3、fun.bind()
Function.prototype.bind = function() {
//将参数拆解为数组
const args = Array.prototype.slice.call(arguments);
//获取this
const t = args.shift();
//fn.bind(...)中的fn
const self = this;
return function() {
return self.apply(t, args)
}
}
4、new
function _new(fn, ...args) {
// 创建一个空的对象,空对象的__proto__属性指向构造函数的原型
var obj = Object.create(fn.prototype);
// 把上面创建的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象
const result = fn.apply(obj, ...args);
// 如构造函数返回一个非基本类型的值,则返回这个值,否则返回上面创建的对象
return Object.prototype.toString.call(result) === '[Object Object]' ? result : obj;
}
5、实现Object.create():它返回了一个新的空对象,并且这个空对象的构造函数的原型(prototype)是指向obj的
function _create = function(obj) {
let fn = function() {}
obj && typeof obj === 'object' && (fn.prototype = obj)
return new fn()
}
6、数组去重,使用Set或遍历
//第一种
function arraySet(arr) {
if (!Array.isArray(arr)) {
throw Error('not array')
}
return [...new Set(arr)]
}
//第二种
function arraySet(arr) {
if (!Array.isArray(arr)) {
throw Error('not array')
}
let obj = {};
let tmpArr = [];
for (var i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) {
obj[arr[i]] = 1
tmpArr.push(arr[i])
}
}
return tmpArr;
}
7、实现instanceof:遍历左边变量的原型链,直到等于右边变量的prototype,如果没有找到返回false
function new_instance_of(leftValue, rightValue) {
let rightProto = rightValue.prototype
leftValue = leftValue.__proto__
while(leftValue) {
if (leftValue === null) {
return false;
}
if (leftValue === rightProto) {
return true;
}
leftValue = leftValue.__proto__;
}
return false;
}
8、实现深拷贝deepClone
function deepClone(obj={}) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
let result;
if (obj instanceof Array) {
result = [];
} else {
result = {};
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
}
9、实现 a == 1 && a == 2 && a == 3
实现原理:要实现这个效果,变量a必然是一个引用类型的数据,巧妙利用==在比较时两边的数据会发生隐式转换这一特性
- 如果引用类型a部署了[Symbol.toPrimitive]接口,那么调用此接口,若返回的不是基本类型的数据,则抛出错误
- 如果引用类型a没有部署[Symbol.toPrimitive]接口,那么会根据需要转换的类型转换,如果和数值比较,则默认转换成number类型,会先调用a的valueof方法,是基本类型的数据则返回,否则调用a的toString方法,返回基本类型的数据则结束,否则抛出错误
- 非Date类型的引用类型在转换成原始类型的时候默认转换成numer类型,但是Date类型的数据转换成原始类型的时候默认转换成String类型
//写法一
var a = {
value: [3,2,1],
valueof: function() {
return this.value.pop()
}
}
//写法二
var a = {
value: [3,2,1],
[Symbol.toPrimitive]: function() {
return this.value.pop()
}
}
// 可以通过对Object.defineProperty和Proxy的使用来实现
var obj = {age: 1};
var age = 1;
Object.defineProperty(obj, 'a', {
get: function() {
return age++;
}
});
10、简易Promise
function MyPromise(excutor) {
const self = this
self.status = 'pending'
self.value = null
self.reason = null
self.onFulfilledCallbacks = [] // 存储成功的方法
self.onRejectedCallbacks = [] // 存储失败的方法
//resolve函数接收成功时传的参数
function resolve(value) {
if (self.status === 'pending') {
//改变当前执行函数的状态为成功状态
self.status = 'fulfilled'
//存储成功时的值
self.value = value
// promise实例状态成功,执行之前存储成功方法的数组,把传给resolve的参数传进去
self.onFulfilledCallbacks.forEach(item => item(self.value))
}
}
//reject函数接收失败时传的参数
function reject(reason) {
if (self.status === 'pending') {
self.status = 'rejected'
self.reason = reason
self.onRejectedCallbacks.forEach(item => item(self.reason)
}
}
try {
//如果没有错直接执行
excutor(resolve, reject)
} catch (error) {
//一旦发现错误,直接扔给reject
reject(error)
}
}
//加上then的完整代码
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const self = this
if (self.status === 'fulfilled') {
onFulfilled(self.value)
}
if (self.status === 'rejected') {
onRejected(self.reason)
}
if (self.status === 'pending') {
//处于pending状态也就是promise实例还在执行,异步状态情况下
self.onFulfilledCallbacks.push(onFulfilled)
self.onRejectedCallbacks.push(onRejected)
}
}
11、手写ajax请求
function ajax(url, success) {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
// true表示异步请求,false表示同步
xhr.open("GET", "/api", true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
} else if (xhr.status === 404) {
reject(new Error('404 not found'));
}
}
}
xhr.send(null);
})
return p;
}
const url = ""
ajax(url).then(res => console.log(res)).catch(err => console.log(err))
12、防抖:最后一次触发
function debounce(fn, delay=500) {
let timer = null
return function() {
//如果定时器存在清空,然后重新设置定时器
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
13、节流:定时触发
function throttle(fn, delay=100) {
let timer = null
return function() {
//已经存在定时器,直接return
if (timer) {
return
}
timer = setTimer(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
14、trim
String.prototype.trim = function() {
return this.replace(/^\s/, '').replace(/\s+$/, '');
}
15、max
function max() {
const nums = Array.prototype.slice.call(arguments)
let max = 0
nums.forEach(n => {
if (n > max) {
max = n
}
})
return max
}
16、数组扁平化
function flat(arr) {
//验证arr中还有没有深层数组
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr
}
const res = Array.prototype.concat.apply([], arr)
return flat(res)
}
17、requestAnimationFrame实现setInterval
实现原理:定义一个mySetInterval接收两个参数,回调函数callback和时间间隔interval。定义一个loop函数,实现每次在浏览器下次重绘之前调用。loop函数里面判断endTime和startTime的差值大于interval的时候,执行callback函数,并重置startTime,从而实现setInterval
function mySetInterval(callback, interval) {
let timer = null
let startTime = new Date()
const loop = () => {
let endTime = new Date()
timer = window.requestAnimationFrame(loop)
if (endTime - startTime >= interval) {
startTime = new Date()
callback(timer)
}
}
loop()
return timer
}
//代码
let a = 0
mySetInterval((timer) => {
a++;
console.log(a);
if (a>10) {
window.cancelAnimationFrame(timer)
}
}, 1000)
18、实现一个简单的双向绑定
实现原理:给input绑定change事件,当数据发生变化,通知依赖该数据的地方更新
const input = document.getElementById('input')
const span = document.getElementById('span')
let obj = {}
input.onchange = function inputChange(e) {
obj.text = t.target.value
}
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
return obj.text
},
set(newVal) {
span.innerText = newVal
}
})
19、实现isEqual
function isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if (obj1Keys.length !== obj2Keys.length) {
return false;
}
for (let key in obj1) {
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false;
}
}
return true;
}
20、函数柯里化 实现原理: 函数柯里化,就是定义一个已知函数fn,该函数有n个,实现一个柯里化函数currying,接收参数fn函数,currying函数主要负责收集fn函数的参数,当收集的参数达到fn参数的个数的时候调用fn函数。
function currying(fn, ...args) {
return args.length < fn.length ? (...arguments) => currying(fn, ...args, ...arguments) : fn(...args);
}
function countTotal(a, b, c, d) {
return a * b *c * d;
}
var sum = currying(countTotal);
sum(1)(2)(3)(4); // 24
sum(1, 2)(3)(4); // 24
sum(1, 2, 3)(4); // 24
sum(1, 2, 3, 4); // 24