call
函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。call方法传入的参数第一个为调用的对象,后面的参数是按顺序传入方法。
var obj = {};var f = function () {return this;};f() === window // truef.call(obj) === obj // true
手写源码实现
- 将obj.fn指定为调用的fn,即this
- 执行obj.fn
删除obj.fn
function mycall(){//传入的第一个参数永远是调用的参数,如果不存在则默认为windowvar ctx=arguments[0]||windowctx.fn=this//将传入的参数设置为一个数组var args = [];for(let i=1;i<arguments;i++){args.push(arguments[i])}//将参数传入ctxvar result = ctx.fn(...args)//删除fndelete ctx.fnreturn result}
apply
apply跟call作用一样,唯一的不同是传入的参数是两个,第一个是调用的对象,第二个是参数数组。所以更改一下传入的参数即可
function myapply(obj,args){obj.fn=thisvar arrs=args||[]var result = obj.fn(arrs.join(","))delete ctx.fnreturn result}
bind
一个函数通过调用bind()方法创建一个新的绑定函数,调用新绑定函数,会在指定的作用域中传入参数并执行。使用bind()方法函数创建一个新绑定函数,它包装了原函数对象,调用绑定函数通常会导致执行包装函数。
简单的说法,bind返回的函数调用的时候,其实就调用apply,不过注意的是,bind传入的参数跟call的方式是一样的,是一个个传入的function mybind(){var self=thisvar obj=arguments[0]var args=[...arguments].slice(1)return function(){self.apply(obj, args)}}
new
new 通过构造方法生产新的对象。
实现new:创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的prototype属性。
- 将这个空对象赋值给函数内部的this关键字。
开始执行构造函数内部的代码。
也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。
new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。如若return语句返回的是字符串,new命令就会忽略了该语句。
function myNew(fn,...args){if(typeof fn!=='function){return false}var newobj={}// fn.prototype.constructor=fnnewobj._proto_=fn.prototypelet result=fn.call(newobj,...args)return typeof result==='object'?result:newobj}
promise
其实前面异步编程中已经实现了promise的源码,这边一起整理下,并且加上Promise.all跟Promise.race的源码实现。
Promise.all
promise.all传入的是一个数组,数组的所有项都是promise对象,如果所有的promise对象都resolved,那么Promise.all获得的一个数组,数组内对应的是各个promise对象的值,但是其中只要有一个promise对象被reject了,那么promise返回的就是reject的值。
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
- 先用new Promise包装成一个新的promise对象
- 定义一个方法跟数组,用来判断相应的执行操作,还有存入相应位置的值
- 循环执行传入的promise对象数组,并判断是否是promise对象
- 一旦获取到reject,就返回reject的值
```javascript
// 校验是否是 promise
function isPromise(x) {
if((typeof x ===’object’ && x !== null) || typeof x ===’function’) {
} return false }if(typeof x.then == 'function') {return true}
Promise.all = function(promises) { //因为Promise.all返回的是一个新的promise对象,所以要套一层new Promise return new Promise((resolve, reject) => { let arr = [] let idx = 0 // 执行个数 //将每个执行项加入到dealProcess中,每次执行这个方法就将idx加1 let dealProcess = (val, index) => { arr[index] = val if(++idx == promises.length) { resolve(arr) } } promises.forEach((item, i) => { if(isPromise(item)) { item.then(y => { dealProcess(y, i) },reject) }else { dealProcess(item, i) } }); }) }
<a name="0NKMW"></a>### Promise.race这个很简单,同promise一样返回一个新的promise对象,然后谁先执行就直接resolve。直接进行循环处理就行了,只要谁resolve了就直接抛出来```javascriptPromise.race(promises){return newPromis((resolve,reject)=>{promises.forEach(item=>{item.then(res=>{resolve(res)}).catch(err=>{reject(err)})})})}
instanceof
boolean result = obj ``instanceof Object
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
只要一直用obj=obj.proto顺着原型链一直往上找就好了。
function instance_of(L,R){const baseType = ['string', 'number','boolean','undefined','symbol']if(baseType.includes(typeof(L))) { return false }let RP = R.prototype; //取 R 的显示原型L = L.__proto__; //取 L 的隐式原型while(true){ // 无线循环的写法(也可以使 for(;;) )if(L === null){ //找到最顶层return false;}if(L === RP){ //严格相等return true;}L = L.__proto__; //没找到继续向上一层原型链查找}}
Object.create
**Object.create()**方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
function create(obj){let fn=obj._proto_.constructorlet newobj=new fn()return newobj}
防抖与节流
防抖函数:短时间内大量操作的最后一次,只有满足最后一次的那段时间没有被触发,就会执行。可以通过设置一个timer,如果timer存在并且又被触发,则重置timer,这样子除非最后一次才会触发
防抖常用于搜索框/滚动条/按钮点击 的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费
function debounce(fn,second){let timerreturn function(args){if(timer){clearTimeout(timer)}timer=setTimeout({timer=nullfn.call(this,...args)},second)}}
节流函数,限制在一定时间内只触发一次。而节流在定时器到时间后再清空定时器。实现原理:在点击后执行时间,然后这段时间内的所有点击都无效。可以利用锁机制来判断是否这段时间已经过了。突然发现平时用了好多节流的场景,其实就是利用锁机制。
应用场景 => DOM元素拖拽 射击游戏 计算鼠标距离 scroll滚动事件
function throttel(fn,delay){let lock='unlock'if(lock==='lock'){return false}lock='lock'return function(args){lock='lock'let timer=setTimeout({fn.call(this,...args)lock='unlock'},delay)}}
深拷贝deepClone
说到底,想要实现深拷贝,就是浅拷贝加递归,也就是如果对象的属性值还是一个对象的话,再进行一次拷贝
//判断是否是对象function isObject(obj) {return typeof obj === 'object' && obj != null;}function deepCopy(source){// 判断如果参数不是一个对象,返回改参数if(!isObject(source)) return source;// 判断参数是对象还是数组来初始化返回值let res = Array.isArray(source)?[]:{};// 循环参数对象的keyfor(let key in source){// 如果该key属于参数对象本身if(Object.prototype.hasOwnProperty.call(source,key)){// 如果该key的value值是对象,递归调用深拷贝方法进行拷贝if(isObject(source[key])){res[key] = deepCopy(source[key]);}else{// 如果该key的value值不是对象,则把参数对象key的value值赋给返回值的keyres[key] = source[key];}}}// 返回返回值return res;}
reduce
这是ES6的一个语法,简单来说就是循环返回前面处理好的值。
Array.prototype.reduce = function (callback,prev) {//遍历this 数组for (let i = 0; i < this.length; i++) {//判断有没有设置初始值if (typeof prev === "undefined") {//没有初始值,则调用callback,转入当前值,下一个值,当前index为下一个,当前数组prev = callback(this[i], this[i + 1], i + 1, this);} else {//有初始值,则调用callback,传入prev为第一个值,第二个为当前的i,当前index为i,当前数组prev = callback(prev, this[i], i, this);}}return prev;};arr.reduce((pre,cur,index,arr)=>{})
