instanceof的实现
function myInstanceof(left, right) {
let rightProto = right.prototype;
let leftProto = left._proto_;
// 判断实例对象的类型是否等于类型的原型
while (true) {
if (rightProto === null) return false;
if (leftProto === rightProto) return true;
leftProto = leftProto.prototype;
}
}
new的实现
function New(Constructor, ...args){
let obj = {}; // 创建一个新对象
Object.setPrototypeOf(obj, Constructor.prototype); // 连接新对象与函数的原型
return Constructor.apply(obj, args) || obj; // 执行函数,改变 this 指向新的对象
}
function Foo(a){
this.a = a;
}
New(Foo, 1); // Foo { a: 1 }
// 第二种
function _new() {
let target = {}; //创建的新对象
//第一个参数是构造函数
let [constructor, ...args] = [...arguments];
//执行[[原型]]连接;target 是 constructor 的实例
target.__proto__ = constructor.prototype;
//执行构造函数,将属性或方法添加到创建的空对象上
let result = constructor.apply(target, args);
if (result && (typeof (result) == "object" || typeof (result) == "function")) {
//如果构造函数执行的结构返回的是一个对象,那么返回这个对象
return result;
}
//如果构造函数返回的不是一个对象,返回创建的新对象
return target;
}
//第三种
function _new2(constructor, ...args) {
if (typeof constructor !== 'function') {
throw new TypeError('Type Error')
}
let obj = Object.create(constructor.prototype);
const res = constructor.apply(obj, args);
const isObject = typeof res === 'object';
const isFunction = typeof res === 'function';
return isObject || isFunction ? res : obj;
}
Ajax封装
function ajax(type, url, data, success, fail) {
var xhr = null;
if (widnow.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject();
}
if (typeof data === 'object') {
var dataStr;
for (var key in data) {
dataStr += key + '=' + data[key] + '&';
}
data = dataStr.replace(/&$/, '');
}
type = type.toUpperCase();
//随机数,清缓存
var random = Math.random();
if (type === 'GET') {
if (data) {
xhr.open('GET', url + '?' + data, true);
} else {
xhr.open('GET', url + '?t='+ random, true);
}
} else {
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-type", 'application/x-www-form-urlencoded');
xhr.send(dataStr);
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
success && success(xhr.responseText);
} else {
fail && fail(xhr.status);
}
}
}
}
// Promise版
const getJSON = function(url) {
return new Promise((resolve, reject) => {
const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
xhr.open('GET', url, false);
xhr.setRequestHeader('Accept', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
}
xhr.send();
})
}
浅拷贝与深拷贝
Object.assign
与slice()
都是浅拷贝,JSON.parse(JSON.stringify(obj))
是深拷贝- JSON.stringify的几个缺点
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor
6、如果对象中存在循环引用的情况也无法正确实现深拷贝
- 浅拷贝
```javascript
// 简易例子
function cloneObject(source) {
let target = {};
for(let prop in source) {
if(source.hasOwnProperty(prop)){
} } return target; }target[prop] = source[prop];
// 对象merge export default merge(target) { for( let i = 1; i < arguments; i++){ let source = arguments[i]; for( let prop in source){ if(source.hasOwnProperty(prop)) { let value = source[prop]; if(value != undefined) { target[prop] = value; } } } } }
// 实现Object.assign if (type of Object._assign != ‘function’) { Object.defineProperty(Object, ‘assign2’, { value: function (target) { if (target == null) { throw new TypeError(‘Cannot not undefined or null to object’); }
target = Object(object);
for (let i = 1; i < arguments.length; i++) {
let source = arguments[i];
if (source != null) {
for (let prop in source) {
if (source.hasOwnProperty(prop)) {
let value = source[prop]
if (value != undefined) {
target[prop] = value
}
}
}
}
}
}
})
}
- 深拷贝
```javascript
// 简易
function deepClone(source) {
let target = {};
for (let prop in source) {
if(typeof source[prop] === 'object') {
target[prop] = deepClone(source[prop]);
} else {
target[prop] = source[prop];
}
}
return target;
}
// 考虑数组与对象情况
function clone(Obj) {
var buf;
if (Obj instanceof Array) {
buf = []; // 创建一个空的数组
let i = Obj.length;
while (i--) {
buf[i] = clone(Obj[i]);
}
return buf;
} else if (Obj instanceof Object){
buf = {}; // 创建一个空对象
for (let k in Obj) { // 为这个对象添加新的属性
buf[k] = clone(Obj[k]);
}
return buf;
}else{
return Obj;
}
}
// 最完整示例,包括对象循环引用
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);
// 考虑symbol情况
const cloneDeep1 = (target, hash = new WeakMap()) => {
// 对于传入参数处理
if (typeof target !== 'object' || target === null) {
return target;
}
// 哈希表中存在直接返回
if (hash.has(target)) return hash.get(target);
const cloneTarget = Array.isArray(target) ? [] : {};
hash.set(target, cloneTarget);
// 针对Symbol属性
const symKeys = Object.getOwnPropertySymbols(target);
if (symKeys.length) {
symKeys.forEach(symKey => {
if (typeof target[symKey] === 'object' && target[symKey] !== null) {
cloneTarget[symKey] = cloneDeep1(target[symKey]);
} else {
cloneTarget[symKey] = target[symKey];
}
})
}
for (const i in target) {
if (Object.prototype.hasOwnProperty.call(target, i)) {
cloneTarget[i] =
typeof target[i] === 'object' && target[i] !== null
? cloneDeep1(target[i], hash)
: target[i];
}
}
return cloneTarget;
}
防抖与节流
// 防抖
function debounce(fn) {
// 创建一个标记用来存放定时器的返回值
let timeout = null;
return function() {
//每次当用户点击/输入的时候,把前一个定时器清除
clearTimeout(timeout);
// 然后创建一个新的 setTimeout,
// 这样就能保证点击按钮后的 interval 间隔内
// 如果用户还点击了的话,就不会执行 fn 函数
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, 1000);
};
}
// 节流
function throttle(fn) {
// 通过闭包保存一个标记
let canRun = true;
return function() {
// 在函数开头判断标志是否为 true,不为 true 则中断函数
if(!canRun) {
return;
}
// 将 canRun 设置为 false,防止执行之前再被执行
canRun = false;
// 定时器
setTimeout( () => {
fn.call(this, arguments);
// 执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
canRun = true;
}, 1000);
};
}
数组方法
扁平化
const arr = [1, [2, [3, [4, 5]]], 6];
// 1.使用flat
const res1 = arr.flat(Infinity);
// 2.使用正则匹配
const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');
// 3.使用reduce
const flatten = arr => {
return arr.reduce((pre, cur) => {
return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, [])
}
const res4 = flatten(arr);
// 4. 递归
const res5 = [];
const fn = arr => {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
fn(arr[i]);
} else {
res5.push(arr[i]);
}
}
}
fn(arr);
数组去重
const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// 1.利用Set
const res1 = Array.from(new Set(arr));
// 2.利用Map
const unique5 = arr => {
const map = new Map();
const res = [];
for (let i = 0; i < arr.length; i++) {
if (!map.has(arr[i])) {
map.set(arr[i], true)
res.push(arr[i]);
}
}
return res;
}
// 3.两次for循环
const unique1 = arr => {
let len = arr.length;
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
// 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能
len--;
j--;
}
}
}
return arr;
}
// 4.使用indexOf
const unique2 = arr => {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
}
return res;
}
// 5.使用includes
const unique3 = arr => {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (!res.includes(arr[i])) res.push(arr[i]);
}
return res;
}
filter实现
Array.prototype.filter = function(callback, thisArg) {
if (this == undefined) {
throw new TypeError('this is null or not undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function');
}
const res = [];
// 让obj成为回调函数的对象传递(强制转换对象)
const obj = Object(this);
// >>>0 无符号移位0位,保证len为number,且为正整数
const len = obj.length >>> 0;
for (let i = 0; i < len; i++) {
// 检查i是否在O的属性(会检查原型链)
if (i in obj) {
// 回调函数调用传参
if (callback.call(thisArg, obj[i], i, obj)) {
res.push(obj[i]);
}
}
}
return res;
}
map实现
Array.prototype.map = function(callback, thisArg) {
if (this == undefined) {
throw new TypeError('this is null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const res = [];
const obj = Object(this);
const len = obj.length >>> 0;
for(let i = 0; i < len; i++) {
if (i in obj) {
res[i] = callback.call(thisArg, obj[i], i, this);
}
}
return res;
}
forEach实现
Array.prototype.forEach = function(callback, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
const obj = Object(this);
const len = obj.length >>> 0;
let k = 0;
while (k < len) {
if (k in obj) {
callback.call(thisArg, obj[k], k, obj);
}
k++;
}
}
reduce实现
Array.prototype.reduce = function(callback, initialValue) {
if (this === undefined) {
throw new TypeError('this is null or not undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function');
}
const obj = Object(this);
const len = this.length;
let accumulator = initialValue;
let k = 0;
// 如果无初始值则取第一个值
if (accumulator === undefined) {
while(k < len && !(k in obj)) {
k++;
}
if(k >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = obj[k++];
}
while (k < len) {
accumulator = callback.call(this, accumulator, obj[k], k, obj);
k++;
}
return accumulator;
}
apply与call的实现
Function.prototype.apply = function(context = window, args) {
if (typeof this !== 'function') {
throw TypeError('this is not a function');
}
context.fn = this;
const res = context.fn(...args);
delete context.fn;
return res;
}
Function.prototype.call = function(context = window, ...args) {
if (typeof this !== 'function') {
throw TypeError('this is not a function');
}
context.fn = this;
const res = context.fn(args);
delete context.fn;
return res;
}
bind的实现
Function.prototype.bind = function(context, ...args) {
if (typeof this !== 'function') {
throw new Error('');
}
// 保存this
const selfFn = this;
return function() {
// 考虑new情况
if (this instanceof F) {
return new self(...args, ...arguments)
}
return selfFn.apply(context, [...args, ...arguments]);
}
}
柯理化
// 利用==判断会调用toString方法
const arr = [1,2,3];
function add() {
const _args = [...arguments];
function fn() {
_args.push(...arguments);
return fn;
}
fn.toString = function() {
console.log(_args);
return _args.reduce((sum, item) => sum + item);
}
return fn;
}
fn1 = add(1);
console.log(fn1(2));
// 固定参数个数的柯理化
function sum(a, b, c) {
return a + b + c;
function curry(fn) {
return function curried(...args) {
// function的length属性指函数所需形参的个数,如果参数有默认值则为第一个有默认值之前的参数个数
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
}
}
const sum2 = curry(sum);
console.log(sum2(1, 2)(3)); // 6
// 非固定参数个数的珂理化
// 非固定个数入参
function curry2(fn) {
return function curried(...args) {
//args,累计保存的参数
return function(...args2) {
// args2,每次执行传入的参数
if (args2.length) {
return curried.apply(this, args.concat(args2))
} else {
return fn.apply(this, args);
}
}
}
}
instanceof实现
function _instanceof() {
if (typeof left !== 'object' || left === null) return false;
let proto = Object.getPrototypeOf(left);
while(true) {
if (proto === null) return false;
if (proto === right.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
继承实现
function Parent() {
this.name = 'parent';
}
function Child() {
Parent.call(this);
this.type = 'children';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Object.assign实现
Object.defineProperty(Object, 'assign', {
value: function(target, ...args) {
if (target == null) {
return new TypeError('Cannot convert undefined or null to object');
}
let obj = Object(target);
for(let i = 0; i < args.length; i++) {
if (args[i] !== null) {
for(const key in args[i]) {
if (Object.hasOwnProperty.call(obj, key)) {
obj[key] = args[i][key];
}
}
}
}
return obj;
},
enumerable: false, // 不可枚举
writable: false, // 不可修改
configurable: false, // 是否可使用delete,是否可重新定义属性特性
})
Promise实现
/**
* Promise 实现
*/
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(exector) {
this.status = 'PENDING'; // 状态,初始等待状态
this.value = undefined; // 值,便于后续then,catch访问
this.reason = undefined; // 失败原因保存
this.onFulfilledCallbacks = []; // 成功回调队列
this.onRejectedCallbacks = []; // 失败回调队列
const resolve = (value) => {
// 等待中状态才能进行更改
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn(this.value));
}
}
const reject = (reason) => {
// 等待中状态才能进行更改
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onFulfilledCallbacks.forEach(fn => fn(this.reason));
}
}
try {
// 立即执行并传入修改状态的两个方法
exector(resolve, reject);
} catch(e) {
// 出现错误直接reject回去
reject(e);
}
}
then(onFulfilled, onRejected) {
// 初始化两个回调
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected :
reason => {throw new TypeError(reason instanceof Error ? reason.message : reason)}
// 保存this, 即当前Promise实例
const self = this;
// 返回一个新Promise
return new Promise((resolve, reject) => {
// 等待状态
if (self.status === PENDING) {
// 加入成功回调队列
self.onFulfilledCallbacks.push(() => {
try {
setTimeout(() => {
const result = onFulfilled(self.value);
// 处理链式传递
resolvePromise(result, resolve, reject);
})
} catch(e) {
reject(e)
}
})
// 加入失败回调队列
self.onRejectedCallbacks.push(() => {
try {
setTimeout(() => {
const result = onRejected(self.reason);
// 处理链式传递
resolvePromise(result, resolve, reject);
})
} catch(e) {
reject(e)
}
})
}
})
}
catch(onRejected) {
this.then(null, onRejected);
}
static resolve(value) {
if (value instanceof Promise) {
return value;
} else {
return new Promise((resolve, reject) => resolve(value));
}
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all(promiseArr) {
const len = promiseArr.length;
let values = new Array(len);
let count = 0;
return new Promise((resolve, reject) => {
for(let i = 0; i < len; i++) {
// 使用Promise.resolve处理,保证每个都是Promise实例
Promise.resolve(promise[i]).then(value => {
count++;
values[i] = value;
if (count === len) {
resolve(values);
}
}, err => reject(err))
}
})
}
static race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(promiseItem => {
Promise.resolve(promiseItem).then(value => resolve(value), err => reject(err));
})
})
}
}
// 处理链式调用then, 传递返回值result
function resolvePromise(result, resolve, reject) {
// 1. 返回是Promise类型,执行它的then
// 2. 返回不是Promise, 执行当前新Promise的resolve, 将值传递下去
if (result instanceof Promise) {
result.then(resolve, reject);
} else {
resolve(result);
}
}
参考资料
promise实现—前端进阶之道
从零开始实现一个promise