各种常用js函数实现
1.call/apply
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') return;
var fun = this;
context._fn = fun; // TODO:确保_fn 键不存在
var res = context._fn(args);
return res;
}
// test
function say (...args) {
return args + this.name
}
var obj = { name: 'rencoo' }
say.myCall(obj, 'hello, ') // "hello, rencoo"
2.bind
- 绑定环境
- 如果以new调用的话,
// var fn = func.bind(obj, ...args);
// fn(...args);
// new fn(...args); // 对 this 来说, new 的优先级高于 bind; 会忽视传入的 context
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== 'function') return;
var fun = this;
return function (...innerArgs) {
args = args.concat(innerArgs);
var res = fun.apply(context, args);
return res;
}
}
考虑到 new 调用,在返回函数里做一层判断,如果函数是使用使用 new 进行调用的,那么绑定的环境就会失效
var obj = { age: 25 };
function Person(name) {
this.name = name;
console.log(this); // (*)
}
Person.prototype.sayHi = function () {
console.log('hi');
}
var fn = Person.bind(obj, 'Bob');
// 调用后, (*)的打印内容
// 普通调用
fn();
console: { age: 25, name: "Bob" }
// 通过new调用
var p = new fn();
console.log(p); // Person { name: 'Bob' }
console.log(p.sayHi); // f () { console.log('hi') }
改进我们的 myBind
Function.prototype.myBind = function (context, ...args) {
var fn = this;
if (typeof fn !== 'function') return;
var boundFn;
var binder = function (...innerArgs) {
args = args.concat(innerArgs);
// console.log(this instanceof boundFn); // 使用 new 调用时, this 即为构造函数的实例, 判断结果为 true (优先级高于 bind 传入的 context)
if(this instanceof boundFn) { // new 调用; 不使用bind传入的 context
var res = fn.apply(this, args);
if (typeof res === 'object') {
return res;
}
return this;
} else { // 普通调用
return fn.apply(context, args);
}
}
boundFn = Function('binder', 'return function (){ return binder.apply(this,arguments); }')(binder);
if (fn.prototype) {
var Empty = function Empty() {};
Empty.prototype = fn.prototype;
boundFn.prototype = new Empty();
Empty.prototype = null;
}
return boundFn;
}
// 测试
var obj = { age: 25 };
function Person(name) {
this.name = name;
console.log(this); // (*)
}
var fn = Person.myBind(obj, 'Bob');
// 调用后, (*)的打印内容
// 普通调用
fn();
// console: { age: 25, name: "Bob" }
// 通过new调用
new fn();
// console: Person { name: 'Bob' }
常用的装饰器函数 once/throttle/debounce
3.once
Ensure a function is called only once,函数调用一次即失效
function once (fn) {
var flag = false;
return function (...args) {
!flag && fn.apply(this, args);
flag = true;
}
}
// more elegant way to write a once function
function once (fn) {
// closure here; var fn = fn;
return function (...args) {
var res = fn && fn.apply(this, args);
fn = null;
return res;
}
}
4.throttle
节流:连续的高频操作,只有最后一次操作后的一段时间后才调用传入的函数(最终的状态最重要),后一次操作会覆盖前一次操作
应用:实时搜索里的搜索函数、鼠标移动函数中的更新函数、窗口resize里的更新函数
(原因是这些传入的函数可能都是一些耗时耗性能的操作,无法也没必要在每次微小动作上执行)
function throttle (fn, delay) {
var timer = null;
return function (...args) {
// 如果上一次操作的定时器尚未执行,那么取消它并将其替换为一个新的定时器
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
}
}
5.debounce
防抖:第一次生效,之后一定时间内无论怎么触发都无效
function debounce (fn, wait) {
var flag = true, timer = null;
return function (...args) {
if (flag) {
flag = false;
timer = setTimeout(function () {
flag = true;
clearTimeout(timer);
}, wait);
return fn.apply(this, args);
}
}
}
// 另一种写法
function debounce (fn, wait) {
var timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
timer = null;
}, wait);
return fn.apply(this, args);
}
}
}
6.throttle_optimize
升级版的 throttle_optimize 结合了 throttle 与 debounce 的特点
与 throttle 的区别:throttle_optimize 对于用户的第一次操作总是调用传入的函数
与 debounce 的区别:如果被忽略的调用是冷却期间的最后一次,那么它会在延迟结束时执行传入的函数
function throttle_optimize (fn, delay) {
var flag = true, timer = null;
return function (...args) {
if (flag) {
flag = false;
fn.apply(this, args);
args = null;
}
clearTimeout(timer);
timer = setTimeout(() => {
flag = true;
if (args) {
fn.apply(this, args);
}
}, delay);
}
}
7.throttle_optimize
用于动画渲染的 throttle_optimize
对于第一次操作,总是 update;之后的 delay 时间内的最后一次操作总是在 delay 时刻渲染一次;
也就是说装饰器函数 throttle_optimize 会经常调用,但是传入的更新函数,最多每 delay 时间调用一次(也可能不调用,没有任何操作就不会调用)
比如,使用鼠标移动和屏幕坐标原点绘制矩形,鼠标持续移动过程中,每隔delay时间渲染一次矩形,而不是每个移动都绘制,或者说只有最后一次移动结束的一段时间后才绘制(throttle的原理)
function throttle_optimize(func, ms) {
let isThrottled = false,
savedArgs,
savedThis;
function wrapper() {
if (isThrottled) {
savedArgs = arguments;
savedThis = this;
return;
}
func.apply(this, arguments);
isThrottled = true;
setTimeout(function () {
isThrottled = false;
if (savedArgs) {
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
}, ms);
}
return wrapper;
}
8.new
function myNew (Fn, ...args) {
// 1.生成一个空对象作为this
// 2.将对象链接到原型上
let obj = Object.create(Fn.prototype)
// 3.执行构造函数, 并将obj作为context传入(为实例对象添加属性)
let res = Fn.apply(obj, args) // 构造函数内的 this 动态改变为 obj
// 4.return this或者构造函数的执行结果(引用类型)
return typeof res === 'object' ? res : obj
// return result instanceof Object ? res : obj
}
// test
function Person (name, age) {
this.name = name;
this.age = age;
}
var a = myNew(Person, 'rencoo', 25)
console.log(a) // Person {name: "rencoo", age: 25}
var b = new Person('ge can', 26)
console.log(b) // Person {name: "gecan", age: 26}
Person.prototype.sayHi = function () {
console.log(this.name)
}
a.sayHi() // rencoo
b.sayHi() // gecan
9.promise
class Promise {
constructor (executor) {
// 设置属性 status value resolveCbs rejectCbs
}
then (onResolved, onRejected) {
}
catch (cb) {
return this.then(null, cb)
}
}
promise链式,实现必须上一个异步完成后再去跑下一个任务
// 1.
const template = Promise.resolve();
promises.forEach((p) => {
template = template.then(p)
})
// 2. 使用 await
10.deep copy(deepclone)
11.extends
12.singleton
13.pub-sub
14.打印出html里所有标签
15.lazyman
16.快排
17.数组乱序
18.LRU
19.两数之和
20.找出一个集合的所有子集
21.Object.defineProperty
- 实现的关键在那个闭包
22.requestAnimation(京东讲座,react间隙渲染)
/**
* Provides requestAnimationFrame in a cross browser way.
* http://paulirish.com/2011/requestanimationframe-for-smart-animating/
*/
if ( !window.requestAnimationFrame ) {
window.requestAnimationFrame = ( function() {
return window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(
/* function FrameRequestCallback */ callback,
/* DOMElement Element */ element ) {
window.setTimeout( callback, 1000 / 60 );
};
} )();
}
23.手写 Proxy(使用Object.defineProperty将一个对象的属性代理到另一个对象上)
24.webpack
- loader plugin的区别
- tree-shaking 的工作原理(一个模块导出十个方法,但只用到了一个,那么只打包那一个方法)
- code splitting用的是什么插件
- 如何提高 webpack 的构建速度
- 利用 DIIPlugin 预编译资源模块
- 利用 Happypack 加速代码构建
25.jsonp
//通过JQuery Ajax 发起jsonp请求
(注:不是必须通过jq发起请求 ,
例如:Vue Resource中提供 $.http.jsonp(url, [options]))
$.ajax({
// 请求方式
type: "get",
// 请求地址
url: "http://169.254.200.238:8080/jsonp.do",
// 标志跨域请求
dataType: "jsonp",
// 跨域函数名的键值,即服务端提取函数名的钥匙(默认为callback)
jsonp: "callbackparam",
// 客户端与服务端约定的函数名称
jsonpCallback: "jsonpCallback",
// 请求成功的回调函数,json既为我们想要获得的数据
success: function(json) {
console.log(json);
},
// 请求失败的回调函数
error: function(e) {
alert("error");
}
});
@RequestMapping({"/jsonp.do"})
public String jsonp(@RequestParam("callbackparam") String callback){
// callback === "jsonpCallback"
// return callback + "({\"result\":\"success\"})";
return (request.from === jsonp) ? callback(data) : data ;
}
26.实现一个Queue类,要求包含两个函数
task函数:新增一个任务。包含两个参数,等待时间和回调函数
start函数:执行任务队列。将所有任务按队列顺序执行, 执行完一个任务才能执行下一个任务
new Queue()
.task(1000, () => {
console.log(1)
})
.task(2000, () => {
console.log(2)
})
.task(1000, () => {
console.log(3)
})
.start()
// 链接:https://www.zhihu.com/question/358075914/answer/915814865
const queue = {
a() {
setTimeout(() => {
console.log("a is done");
this.next();
}, 1000);
},
b() {
setTimeout(() => {
console.log("b is done");
this.next();
}, 1000);
},
c() {
setTimeout(() => {
console.log("c is done");
}, 1000);
}
};
Object.defineProperty(queue, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value() {
const o = this;
const ks = Object.keys(o);
let idx = 0;
return {
next() {
const key = ks[idx++];
const func = o[key];
if (typeof func === "function") {
func.call(this);
}
return {
value: key,
done: idx > ks.length
};
}
};
}
});
const qt = queue[Symbol.iterator]();
qt.next();
// a is done
// b is done
// c is done
let queue = new Queue();
queue.push((next) => {
// do something async
next();
})
queue.push((next) => {
// do something async
Ajax.post('xxx').then(() => next());
});
queue.run()
27.实现一个wait
function wait (time) {
return new Promise((resolve, rejec) => {
setTimeout(resolve, time);
});
}
wait(2000).then(() => {
console.log('hello1');
return wait(2000);
}).then(() => {
console.log('hello2');
});
// 使用 await
(async function () {
var res = await wait(2000);
// console.log(res); // undefined
console.log('hello1');
var res1 = await wait(2000);
// console.log(res1); // undefined
console.log('hello2');
})();
28.实现一个jquery事件代理
function on(parent, currentClassName, eventName, cb) {
parent.addEventListener(eventName, function (evt) {
var target = evt.target;
var currentTarget;
while (target) {
if (target.tagName === 'BODY') break; // 找到body还没找到(说明点击的位置已经是目标元素的父级了; 不可能找到的)
if (target.getAttribute('class') === currentClassName) {
currentTarget = target;
break;
} else {
target = target.parentNode; // 往上找; 从目标元素的子元素触发事件
}
}
if (currentTarget) {
// 获取 currentTarget 上的数据; 并执行事件回调
cb.apply(currentTarget);
}
});
}
各种常用js函数实现的更多相关文章
- 原生JS研究:学习jquery源码,收集整理常用JS函数
原生JS研究:学习jquery源码,收集整理常用JS函数: 1. JS获取原生class(getElementsByClass) 转自:http://blog.csdn.net/kongjiea/ar ...
- api日常总结:前端常用js函数和CSS常用技巧
我的移动端media html{font-size:10px} @media screen and (min-width:321px) and (max-width:375px){html{font- ...
- web前端关于html转义符的常用js函数
web前端关于html转义符的常用js函数 //去掉html标签 function removeHtmlTab(tab) { return tab.replace(/<[^<>]+? ...
- 基础常用JS函数和语法
100多个基础常用JS函数和语法集合大全 来源:http://www.cnblogs.com/hnyei/p/4605103.html 网站特效离不开脚本,javascript是最常用的脚本语言,我 ...
- 常用 JS 函数
各种业务开发都离不开对数据的处理,然而遇到的很多数据都是不好处理的.这个时候就需要寻求搜索引擎的帮助.这种方法效率是非常低下的,而且根据作者的个性不能保证其对自己的口味.因此这篇文字包含了一份 JS ...
- 100多个基础常用JS函数和语法集合大全
网站特效离不开脚本,javascript是最常用的脚本语言,我们归纳一下常用的基础函数和语法: 1.输出语句:document.write(""); 2.JS中的注释为//3.传统 ...
- 一些常用JS函数和技巧总结
1.JS原生函数parseInt(),返回字符串的第一个数字,默认是十进制. 2.!!data.success //强制转换成布尔类型
- 常用js函数整理--common.js
var h = {}; h.get = function (url, data, ok, error) { $.ajax({ url: url, data: data, dataType: 'json ...
- 一些常用JS 函数总结
搜索url参数 /** * 搜索url参数 * @param {String} name 参数键名 * @return {String} 对应值 */ function getQueryVariabl ...
随机推荐
- thinkphp两表,多表联合查询及分页的连贯操作写法
ThinkPHP中关联查询(即多表联合查询)可以使用 table() 方法或和join方法,具体使用如下例所示: 1.原生查询示例: 代码如下: $Model = new Model(); $sql ...
- C++中对C的扩展学习新增语法——For循环以及AUTO类型
For循环以及AUTO类型 For代码实现: AUTO类型: 1.auto自动类型推导,省事. 2.auto主要在模板中用的多,模板的类型比较长,使用auto可以简化代码. 3.不要轻易使用,我们在编 ...
- 张孝祥java高新技术 --- jkd1.5 新特性 -- 精华总结
1. 抽象方法的使用 如果一个方法中大量出现if语句, 那么, 就应该考虑使用抽象来处理. 如下例: package com.lxl; public class Weekend { //周日 publ ...
- thinkphp6.0 多应用模块下提示控制器不存在
thinkphp6.0 多应用模块下提示控制器不存在 在项目根目录下使用Composer composer require topthink/think-multi-app 参考链接
- 性能测试专题:Locust工具实战之“蝗虫”降世
阅读全文需5分钟. 1. 前言 在上一篇文章中,我们已经为大家介绍了什么是Locust,具体可参照:性能专题:Locust工具实战之开篇哲学三问,简单来说,Locust 是基于 Python 语言下的 ...
- PHP编程20大效率要点
1.如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍. 2.$row[’id’] 的速度是$row[id]的7倍. 3.echo 比 print 快,并且使用ech ...
- vue 父子组件传值,兄弟组件传值
父子组件中的传值 父向子 v-bind props <!-- 组件使用v-bind传值 --> <router :msg="msg"></rou ...
- 【Luogu P1972】HH的项链
Luogu P1972 一开始非常naive随便打了个树状数组统计就交上去了,然后不出意料的爆零了-- 然后删一删改一改过了. 重点:对于区间[1,r]中重复出现的数,我们只需要关心最右边那一个是否在 ...
- ubuntu 交叉编译 busybox 1.31.1
目的:静态编译 Busybox_arm64 1.13.1 环境:Ubuntu 18.04.3 #----------------环境配置 # aarch64-linux-gnu-g++ sudo ap ...
- 剑指Offer-32.丑数(C++/Java)
题目: 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含质因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 分析: ...