手写防抖函数
// 你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,
// 防抖函数
function debounce(fn, wait) {
var timeout;
return function () {
var contxt = this
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function(){
fn.apply(context, args)
}, wait)
}
}
手写节流函数
// 节流(Throttling)实现
// 可以理解为事件在一个管道中传输,
// 加上这个节流阀以后,事件的流速就会减慢。
// 实际上这个函数的作用就是如此,它可以将一个函数的调用频率限制在一定阈值内
function throttle(func, wait) {
var timeout;
var previous = 0;
return function() {
var context = this;
var args = arguments;
if (!timeout) {
timeout = setTimeout(function(){
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
手写new方法
new 做了哪些事?
- 它创建了一个全新的对象。
- 它会被执行[[Prototype]](也就是__proto__)链接。
- 它使this指向新创建的对象。
- 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上。
- 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用。
function aNew (fn) {
if (typeof fn !== 'function') {
throw Error('xxxx')
}
let res = {}
if (fn.prototype !== null) {
res.__proto__ = fn.prototype
}
let ret = fn.apply(res, [...arguments].slice.call(1))
if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
手写call方法
call核心:
- 指定this到函数,不传入参数,默认指向为 window
- 传入给定参数执行函数
- 删除这个函数
- 返回执行结果
Function.prototype.call2 = function(context = window) {
if(typeof this != "function") {
throw Error("not a function")
}
context.fn = this;
let args = [...arguments].slice(1);
let res = context.fn(...args);
delete context.fn;
return res;
}
// 测试方法
let foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
手写apply()
apply()的实现和call()类似,只是参数形式不同。apply是一个「数组」
- 指定this到函数,不传入参数,默认指向为 window
- 传入给定参数执行函数
- 删除这个函数
- 返回执行结果
Function.prototype.apply2 = function(context = window) {
if(typeof this != "function") {
throw Error("not a function")
}
context.fn = this;
let res;
if (arguments[1]) {
res = context.fn(...arguments[1])
} else {
res = context.fn();
}
delete context.fn;
return res;
}
手写bind方法
bind方法 会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
此外,bind实现需要考虑实例化后对原型链的影响。
需要考虑的点
- 因为返回新的函数,要考虑到使用new去调用,并且new的优先级比较高,所以需要判断new的调用
- 还有一个特点就是bind调用的时候可以传参,调用之后生成的新的函数也可以传参,效果是一样的
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
手写promise方法
我们来过一遍Promise/A+规范:
- 三种状态pending| fulfilled(resolved) | rejected
- 当处于pending状态的时候,可以转移到fulfilled(resolved)或者rejected状态
- 当处于fulfilled(resolved)状态或者rejected状态的时候,就不可变。
必须有一个then异步执行方法,then接受两个参数且必须返回一个promise
// 面试够用版
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined; //定义状态为resolved的时候的状态
self.reason=undefined; //定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
// 同时,需要在myPromise的原型上定义链式调用的then方法:
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
// 测试一下:
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
手写科里化
function curry(fn, args) {
var length = fn.length;
args = args || [];
return function() {
var _args = args.slice(0);
var arg, i;
for (i = 0; i < arguments.length; i++) {
arg = arguments[i];
_args.push(arg);
}
if (_args.length < length) {
return curry.call(this, fn, _args);
} else {
return fn.apply(this, _args);
}
}
}