function New() {
var obj = {};
var constructor = Array.prototype.shift.call(arguments);
if (typeof constructor !== "function") {
return console.warn(`${constructor} is not a constructor`);
}
obj.__proto__ = constructor.prototype;
var ret = constructor.apply(obj, arguments);
if (typeof ret === "function" || (typeof ret === "object" && ret !== null)) {
return ret;
}
return obj;
}
function jsonStringify(obj) {
const type = typeof obj;
if (/undefined|function|symbol/.test(type)) {
return undefined;
}
if (/number|boolean/.test(type) || obj === null) {
return String(obj);
}
if (type === "string") {
return `"${obj}"`;
}
let result = [];
switch (Object.prototype.toString.call(obj)) {
case "[object Array]":
for (let v of obj) {
let value = jsonStringify(v);
value = value === undefined ? "null" : value;
result.push(value);
}
return "[" + result.join() + "]";
case "[object Object]":
for (let [key, value] of Object.entries(obj)) {
value = jsonStringify(value);
if (value !== undefined) {
result.push(`"${key}":${value}`);
}
}
return "{" + result.join() + "}";
case "[object Date]":
return `"${obj.toJSON ? obj.toJSON() : obj.toString()}"`;
default:
return "{}";
}
}
function jsonParse(json) {
return eval("(" + json + ")");
}
function jsonParse2(json) {
return new Function("return " + json)();
}
JSON.parse 三种实现方式 youngwind/blog#115
Function.prototype._call = function(context) {
context = context || window;
context.fn = this;
var args = [];
for (var i = 1, len = arguments.length; i < len; i++) {
args.push("arguments[" + i + "]");
}
var result = eval("context.fn(" + args + ")");
delete context.fn;
return result;
};
Function.prototype._call = function(context = window, ...args) {
context.fn = this;
let result = context.fn(...args);
delete context.fn;
return result;
};
Function.prototype._apply = function(context, argsArr) {
context = context || window;
argsArr = argsArr || [];
context.fn = this;
var args = [];
for (var i = 0, len = argsArr.length; i < len; i++) {
args.push("argsArr[" + i + "]");
}
var result = eval("context.fn(" + args + ")");
delete context.fn;
return result;
};
// call
Function.prototype.call2 = function(context = window, ...args) {
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
// apply
Function.prototype.apply2 = function(context = window, arr = []) {
context.fn = this;
const result = context.fn(...arr); // 相当于执行了context.fn(arguments[1], arguments[2]);
delete context.fn;
return result; // 因为有可能this函数会有返回值return
};
// bind
Function.prototype.bind2 = function() {
const fn = this;
const args = [...arguments];
return function() {
fn.call(...args, ...arguments);
};
};
Function.prototype._bind = function(context) {
if (typeof this !== "function") {
throw Error("bind must be called on a function");
}
var source = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function() {};
var bound = function() {
var bindArgs = Array.prototype.slice.call(arguments);
return source.apply(this instanceof bound ? this : context, args.concat(bindArgs));
};
// Function.prototype doesn't have a prototype property
if (source.prototype) {
fNOP.prototype = source.prototype;
}
bound.prototype = new fNOP();
return bound;
};
var bound = function() {
var bindArgs = Array.prototype.slice.call(arguments);
if (!(this instanceof bound)) {
return source.apply(context, args.concat(bindArgs));
}
var fNOP = function() {};
if (source.prototype) {
fNOP.prototype = source.prototype;
}
var newObj = new fNOP();
var ret = source.apply(newObj, args.concat(bindArgs));
if (ret !== null && (typeof ret === "object" || typeof ret === "function")) {
return ret;
}
return newObj;
};
bind 方法的兼容实现 lessfish/underscore-analysis#19
function create(proto) {
if (Object.create) {
return Object.create(proto);
}
function F() {}
F.prototype = proto;
return new F();
}
function currying(func) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var newArgs = args.concat([].slice.call(arguments));
if (newArgs.length === func.length) {
return func.apply(this, newArgs);
}
newArgs.unshift(func);
return currying.apply(this, newArgs);
};
}
/**
* 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
* 2. executor 接受两个参数,分别是 resolve 和 reject
* 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
* 4. promise 的状态一旦确认,就不会再改变
* 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled,
* 和 promise 失败的回调 onRejected
* 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
* 如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
* 如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
* 7. then 的参数 onFulfilled 和 onRejected 可以缺省
* 8. promise 可以then多次,promise 的then 方法返回一个 promise
* 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
* 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
* 11.如果 then 返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功,
* 就走下一个then的成功,如果失败,就走下一个then的失败
*/
// promise 三个状态
const PENDING = "Pending";
const FULFILLED = "Fulfilled";
const REJECTED = "Rejected";
function Promise(excutor) {
this.status = PENDING; // 初始状态
this.value = undefined; // fulfilled状态时 返回的信息
this.reason = undefined; // rejected状态时 拒绝的原因
this.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
this.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
let self = this; // 缓存当前promise实例对象
function resolve(value) {
// 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(cb => cb(self.value));
}
}
function reject(reason) {
// 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.reason));
}
}
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
/**
* resolve中的值几种情况:
* 1.普通值
* 2.promise对象
* 3.thenable对象/函数
*/
/**
* 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
// 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
return reject(new TypeError("循环引用"));
}
// 判断 x 是否是 promise 对象
if (x != null && (typeof x === "object" || typeof x === "function")) {
let called = false; // 避免多次调用
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
reason => {
if (called) return;
called = true;
reject(reason);
},
);
} else {
// 说明是一个普通对象/函数
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
/**
* [注册fulfilled状态/rejected状态对应的回调函数]
* @param {function} onFulfilled fulfilled状态时 执行的函数
* @param {function} onRejected rejected状态时 执行的函数
* @return {function} newPromsie 返回一个新的promise对象
*/
Promise.prototype.then = function(onFulfilled, onRejected) {
let self = this;
let promise2;
// 处理参数默认值 保证参数后续能够继续执行
onFulfilled =
typeof onFulfilled === "function"
? onFulfilled
: value => {
return value;
};
onRejected =
typeof onRejected === "function"
? onRejected
: reason => {
throw reason;
};
// then里面的FULFILLED/REJECTED状态时 为什么要加setTimeout ?
// 原因:
// 其一 2.2.4规范 要确保 onFulfilled 和 onRejected 方法异步执行(且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行) 所以要在resolve里加上setTimeout
// 其二 2.2.6规范 对于一个promise,它的then方法可以调用多次.(当在其他程序中多次调用同一个promise的then时 由于之前状态已经为FULFILLED/REJECTED状态,则会走的下面逻辑),所以要确保为FULFILLED/REJECTED状态后 也要异步执行onFulfilled/onRejected
// 其二 2.2.6规范 也是resolve函数里加setTimeout的原因
// 总之都是 让then方法异步执行 也就是确保onFulfilled/onRejected异步执行
// 如下面这种情景 多次调用p1.then
// p1.then((value) => { // 此时p1.status 由pending状态 => fulfilled状态
// console.log(value); // resolve
// // console.log(p1.status); // fulfilled
// p1.then(value => { // 再次p1.then 这时已经为fulfilled状态 走的是fulfilled状态判断里的逻辑 所以我们也要确保判断里面onFuilled异步执行
// console.log(value); // 'resolve'
// });
// console.log('当前执行栈中同步代码');
// })
// console.log('全局执行栈中同步代码');
//
if (self.status === FULFILLED) {
// 成功态
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch (e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
});
return promise2;
}
if (self.status === REJECTED) {
// 失败态
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
return promise2;
}
if (self.status === PENDING) {
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
// 为什么加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
// 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
promise2 = new Promise((resolve, reject) => {
self.onFulfilledCallbacks.push(value => {
setTimeout(() => {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
self.onRejectedCallbacks.push(reason => {
setTimeout(() => {
try {
let x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
});
return promise2;
}
};
Promise.resolve = function(value) {
if (value instanceof Promise) {
return value;
}
return new Promise((resolve, reject) => {
if (value && value.then && typeof value.then === "function") {
setTimeout(() => {
value.then(resolve, reject);
});
} else {
resolve(value);
}
});
};
Promise.reject = function(reason) {
return new Promise((resolve, reject) => reject(reason));
};
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.finally = function(callback) {
return this.then(
value => {
return Promise.resolve(callback()).then(() => {
return value;
});
},
err => {
return Promise.resolve(callback()).then(() => {
throw err;
});
},
);
};
Promise.race = function(promises) {
if (promises.length === 0) {
return;
}
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise).then(resolve, reject);
});
});
};
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
let count = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
for (let i = 0; i < promises.length; i++) {
// promises[i] 可能是普通值
Promise.resolve(promises[i]).then(data => {
result[i] = data;
if (++count === promises.length) {
resolve(result);
}
}, reject);
}
}
});
};
function debounce(func, wait) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
func.apply(context, args);
}, wait);
};
}
function throttle(func, wait) {
var timer = null;
var previous = 0;
return function() {
var now = Date.now();
var remaining = wait - (now - previous);
var context = this;
var args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
func.apply(context, args);
} else if (!timer) {
timer = setTimeout(function() {
previous = Date.now();
func.apply(context, args);
timer = null;
}, remaining);
}
};
}
function deepClone(obj) {
if (!obj || typeof obj !== "object") {
return obj;
}
if (obj.nodeType && "cloneNode" in obj) {
return obj.cloneNode(true);
}
var result;
switch (Object.prototype.toString.call(obj)) {
case "[object Array]":
result = [];
for (let v of obj) {
result.push(deepClone(v));
}
return result;
case "[object Object]":
result = obj.constructor ? new obj.constructor() : {};
for (let [k, v] of Object.entries(obj)) {
result[k] = deepClone(v);
}
return result;
case "[object Date]":
return new Date(obj.getTime());
case "[object RegExp]":
var flags = "";
if (obj.global) flags += "g";
if (obj.multiline) flags += "m";
if (obj.ignoreCase) flags += "i";
return new RegExp(obj.source, flags);
default:
return obj;
}
}
function instanceOf(source, target) {
let proto = source.__proto__;
let prototype = target.prototype;
while (proto) {
if (proto === prototype) return true;
proto = proto.__proto__;
}
return false;
}
浅谈 instanceof 和 typeof 的实现原理 https://juejin.im/post/5b0b9b9051882515773ae714
function async(generator) {
return new Promise(function(resolve, reject) {
const it = generator();
function step(next) {
let result;
try {
result = next();
} catch (e) {
return reject(e);
}
if (result.done) {
return resolve(result.value);
}
Promise.resolve(result.value).then(
function(v) {
step(function() {
return it.next(v);
});
},
function(e) {
step(function() {
return it.throw(e);
});
},
);
}
step(function() {
return it.next(undefined);
});
});
}
function ajax(
options = {
url: "",
method: "GET",
data: {},
timeout: 60,
},
) {
options.method = (options.method || "GET").toUpperCase();
const paramString = formatParams(options.data);
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.timeout = (options.timeout || 60) * 1000;
if (options.method === "GET") {
let url = options.url + (options.url.indexOf("?") > -1 ? "&" : "?") + paramString;
xhr.open("GET", url);
// xhr.setRequestHeader("User-Agent", "Mozilla/5.0 (Linux; X11)");
xhr.send(null);
}
if (options.method === "POST") {
xhr.open("POST", options.url);
// 设置请求头
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// 跨域携带cookie
// xhr.withCredentials = true;
xhr.send(paramString);
}
xhr.onload = function() {
const result = {
status: xhr.status,
statusText: xhr.statusText,
headers: xhr.getAllResponseHeaders(),
data: xhr.response || xhr.responseText,
};
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
resolve(result);
} else {
reject(result);
}
};
// 错误处理
xhr.onerror = function() {
reject(new TypeError("请求出错"));
};
xhr.ontimeout = function() {
reject(new TypeError("请求超时"));
};
xhr.onabort = function() {
reject(new TypeError("请求被终止"));
};
});
}
function formatParams(data) {
let arr = [];
for (var key in data) {
if (data.hasOwnProperty(key)) arr.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
}
return arr.join("&");
}
function jsonp(url, data) {
return new Promise((resolve, reject) => {
// 1.将传入的data数据转化为url字符串形式
var queryString = url.indexOf("?") == -1 ? "?" : "&";
var arr = [];
for (var key in data) {
arr.push(key + "=" + encodeURIComponent(data[key]));
}
queryString += arr.join("&");
// 2.处理url中的回调函数
var callbackName =
"jsonp_callback_" +
Math.random()
.toString(16)
.replace(".", "");
queryString += "&callback=" + callbackName;
// 3.创建一个script标签并插入到页面中
var script = document.createElement("script");
script.src = url + queryString;
// 4.挂载回调函数
window[callbackName] = function(data) {
resolve(data);
// 处理完回调函数的数据之后,删除jsonp的script标签
document.body.removeChild(script);
};
document.body.appendChild(script);
});
}
Fisher–Yates Shuffle
// 方法一
function shuffle(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
var random = Math.floor(Math.random() * (len - i));
var temp = arr[random];
arr[random] = arr[len - i - 1];
arr[len - i - 1] = temp;
}
return arr;
}
// 方法二
function shuffle(arr) {
let len = arr.length,
random;
while (len) {
random = (Math.random() * len--) >>> 0; // 无符号右移位运算符向下取整
[arr[len], arr[random]] = [arr[random], arr[len]];
}
return arr;
}
// 从前往后遍历元素的方式
function shuffle(a) {
var length = a.length;
var shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
rand = ~~(Math.random() * (index + 1));
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = a[index];
}
return shuffled;
}
var sleep = function(time, value) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(value);
}, time);
});
};
var start = async function() {
for (let i = 0; i < 6; i++) {
let result = await sleep(1000, i);
console.log(result);
}
};
start();
/*深度优先遍历三种方式*/
function deepTraversal1(node, nodeList = []) {
if (node !== null) {
nodeList.push(node);
let children = node.children;
for (let i = 0; i < children.length; i++) {
deepTraversal1(children[i], nodeList);
}
}
return nodeList;
}
function deepTraversal2(node) {
let nodes = [];
if (node !== null) {
nodes.push(node);
let children = node.children;
for (let i = 0; i < children.length; i++) {
nodes = nodes.concat(deepTraversal2(children[i]));
}
}
return nodes;
}
// 非递归
function deepTraversal3(node) {
let stack = [];
let nodes = [];
if (node) {
// 推入当前处理的node
stack.push(node);
while (stack.length) {
let item = stack.pop();
let children = item.children;
nodes.push(item);
for (let i = children.length - 1; i >= 0; i--) {
stack.push(children[i]);
}
}
}
return nodes;
}
function BFS(node) {
let nodes = [];
let stack = [];
if (node) {
stack.push(node);
while (stack.length) {
let item = stack.shift();
let children = item.children;
nodes.push(item);
for (let i = 0; i < children.length; i++) {
stack.push(children[i]);
}
}
}
return nodes;
}
var q = function(url) {
let result = {};
const query = url.split("?")[1];
if (!query) return result;
const pairs = query.split("&");
pairs.forEach(pair => {
let [key, value] = pair.split("=");
try {
value = value ? decodeURIComponent(value).replace(/\+/g, " ") : "";
} catch (e) {
return;
}
if (/^\d+(\.\d+)?$/.test(value)) {
value = Number(value);
}
if (typeof result[key] === "undefined") {
result[key] = value;
} else {
result[key] = [].concat(result[key], value);
}
});
return result;
};
var flatten = function(array) {
var result = [];
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
if (Array.isArray(value)) {
result = result.concat(flatten(value));
} else {
result.push(value);
}
}
return result;
};
function flatten(array) {
return array.reduce((result, current) => {
return Array.isArray(current) ? result.concat(flatten(current)) : result.concat(current);
}, []);
}
console.log(flatten([1, [2, [[3, 4], 5], 6]]));
console.log(flatten(["abc", ["a", [[3, "a"], { a: "a" }], null, false]]));
var parseURL = function(url) {
var result = {};
var keys = ["href", "origin", "protocol", "host", "hostname", "port", "pathname", "search", "hash"];
var regexp = /(((?:https?|ftp|file):)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/;
var match = regexp.exec(url);
if (match) {
for (var i = keys.length - 1; i >= 0; i--) {
result[keys[i]] = match[i] ? match[i] : "";
}
}
return result;
};
function URLParser(url) {
const a = document.createElement("a");
a.href = url;
// 非浏览器环境 const urlObj = new URL(url);
return {
protocol: a.protocol,
username: a.username,
password: a.password,
hostname: a.hostname, // host 可能包括 port, hostname 不包括
port: a.port,
pathname: a.pathname,
search: a.search,
hash: a.hash,
};
}
console.log(parseURL("https://www.cnblogs.com:8080/speeding/p/5097790.html?xxx=9999#test"));
var unique = function(arr) {
// 实现一
// return [...new Set(arr)]
// 实现二
const result = [];
arr.forEach(item => {
if (result.indexOf(item) === -1) result.push(item);
});
return result;
};
var arrarr = [1, 2, 3, 3, 4, 4, 5, 5, 6, 1, 9, 3, 25, 4];
console.log(unique(arrarr));
var escapeMap = {
"<": "<",
">": ">",
"&": "&",
'"': """,
"'": "'",
"`": "`",
};
var escapeHtml = function(htmlStr) {
var source = "(?:" + Object.keys(escapeMap).join("|") + ")";
var reg = RegExp(source, "g");
return htmlStr.replace(reg, function(match) {
return escapeMap[match];
});
};
// 如何将浮点数点左边的数每三位添加一个逗号,如 12000000.11 转化为『12,000,000.11』?
function milliFormat(num) {
return (
num &&
num.toString().replace(/\d+/, function(s) {
return s.replace(/(\d)(?=(\d{3})+$)/g, "$1,");
})
);
}
function render(template, data) {
return template.replace(/{{\s*(\w+)\s*}}/g, function(a, b) {
return data[b] || "";
});
}
var t = '<p><a href="{{url}}">{{name}}</a><span>{{greetting}}</span></p>';
console.log(
render(t, {
url: "http://www.alibaba.com",
name: "Alibaba",
greetting: "Welcome",
}),
);
Underscore _.template 方法使用详解 lessfish/underscore-analysis#26
窥探数据结构的世界- ES6 版 https://juejin.im/post/5cd1ab3df265da03587c142a
在 JavaScript 中学习数据结构与算法 https://juejin.im/post/594dfe795188250d725a220a
Data Structure Visualizations https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
从斐波那契数列求值优化谈 _.memoize 方法 lessfish/underscore-analysis#23