call

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。call方法传入的参数第一个为调用的对象,后面的参数是按顺序传入方法。

  1. var obj = {};
  2. var f = function () {
  3. return this;
  4. };
  5. f() === window // true
  6. f.call(obj) === obj // true

手写源码实现

  1. 将obj.fn指定为调用的fn,即this
  2. 执行obj.fn
  3. 删除obj.fn

    1. function mycall(){
    2. //传入的第一个参数永远是调用的参数,如果不存在则默认为window
    3. var ctx=arguments[0]||window
    4. ctx.fn=this
    5. //将传入的参数设置为一个数组
    6. var args = [];
    7. for(let i=1;i<arguments;i++){
    8. args.push(arguments[i])
    9. }
    10. //将参数传入ctx
    11. var result = ctx.fn(...args)
    12. //删除fn
    13. delete ctx.fn
    14. return result
    15. }

    apply

    apply跟call作用一样,唯一的不同是传入的参数是两个,第一个是调用的对象,第二个是参数数组。所以更改一下传入的参数即可

    1. function myapply(obj,args){
    2. obj.fn=this
    3. var arrs=args||[]
    4. var result = obj.fn(arrs.join(","))
    5. delete ctx.fn
    6. return result
    7. }

    bind

    一个函数通过调用bind()方法创建一个新的绑定函数,调用新绑定函数,会在指定的作用域中传入参数并执行。使用bind()方法函数创建一个新绑定函数,它包装了原函数对象,调用绑定函数通常会导致执行包装函数。
    简单的说法,bind返回的函数调用的时候,其实就调用apply,不过注意的是,bind传入的参数跟call的方式是一样的,是一个个传入的

    1. function mybind(){
    2. var self=this
    3. var obj=arguments[0]
    4. var args=[...arguments].slice(1)
    5. return function(){
    6. self.apply(obj, args)
    7. }
    8. }

    new

    new 通过构造方法生产新的对象。
    实现new:

  4. 创建一个空对象,作为将要返回的对象实例。

  5. 将这个空对象的原型,指向构造函数的prototype属性。
  6. 将这个空对象赋值给函数内部的this关键字。

开始执行构造函数内部的代码。
也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。
new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。如若return语句返回的是字符串,new命令就会忽略了该语句。

  1. function myNew(fn,...args){
  2. if(typeof fn!=='function){
  3. return false
  4. }
  5. var newobj={}
  6. // fn.prototype.constructor=fn
  7. newobj._proto_=fn.prototype
  8. let result=fn.call(newobj,...args)
  9. return typeof result==='object'?result:newobj
  10. }

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失败状态的值。

  1. 先用new Promise包装成一个新的promise对象
  2. 定义一个方法跟数组,用来判断相应的执行操作,还有存入相应位置的值
  3. 循环执行传入的promise对象数组,并判断是否是promise对象
  4. 一旦获取到reject,就返回reject的值 ```javascript // 校验是否是 promise function isPromise(x) { if((typeof x ===’object’ && x !== null) || typeof x ===’function’) {
    1. if(typeof x.then == 'function') {
    2. return true
    3. }
    } return false }

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) } }); }) }

  1. <a name="0NKMW"></a>
  2. ### Promise.race
  3. 这个很简单,同promise一样返回一个新的promise对象,然后谁先执行就直接resolve。直接进行循环处理就行了,只要谁resolve了就直接抛出来
  4. ```javascript
  5. Promise.race(promises){
  6. return newPromis((resolve,reject)=>{
  7. promises.forEach(item=>{
  8. item.then(res=>{resolve(res)}).catch(err=>{reject(err)})
  9. })
  10. }
  11. )
  12. }

instanceof

boolean result = obj ``instanceof Object
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
只要一直用obj=obj.proto顺着原型链一直往上找就好了。

  1. function instance_of(L,R){
  2. const baseType = ['string', 'number','boolean','undefined','symbol']
  3. if(baseType.includes(typeof(L))) { return false }
  4. let RP = R.prototype; //取 R 的显示原型
  5. L = L.__proto__; //取 L 的隐式原型
  6. while(true){ // 无线循环的写法(也可以使 for(;;) )
  7. if(L === null){ //找到最顶层
  8. return false;
  9. }
  10. if(L === RP){ //严格相等
  11. return true;
  12. }
  13. L = L.__proto__; //没找到继续向上一层原型链查找
  14. }
  15. }

Object.create

**Object.create()**方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

  1. function create(obj){
  2. let fn=obj._proto_.constructor
  3. let newobj=new fn()
  4. return newobj
  5. }

防抖与节流

防抖函数:短时间内大量操作的最后一次,只有满足最后一次的那段时间没有被触发,就会执行。可以通过设置一个timer,如果timer存在并且又被触发,则重置timer,这样子除非最后一次才会触发
防抖常用于搜索框/滚动条/按钮点击 的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费

  1. function debounce(fn,second){
  2. let timer
  3. return function(args){
  4. if(timer){
  5. clearTimeout(timer)
  6. }
  7. timer=setTimeout({
  8. timer=null
  9. fn.call(this,...args)
  10. },second)
  11. }
  12. }

节流函数,限制在一定时间内只触发一次。而节流在定时器到时间后再清空定时器。实现原理:在点击后执行时间,然后这段时间内的所有点击都无效。可以利用锁机制来判断是否这段时间已经过了。突然发现平时用了好多节流的场景,其实就是利用锁机制。
应用场景 => DOM元素拖拽 射击游戏 计算鼠标距离 scroll滚动事件

  1. function throttel(fn,delay){
  2. let lock='unlock'
  3. if(lock==='lock'){
  4. return false
  5. }
  6. lock='lock'
  7. return function(args){
  8. lock='lock'
  9. let timer=setTimeout({
  10. fn.call(this,...args)
  11. lock='unlock'
  12. },delay)
  13. }
  14. }

深拷贝deepClone

说到底,想要实现深拷贝,就是浅拷贝加递归,也就是如果对象的属性值还是一个对象的话,再进行一次拷贝

  1. //判断是否是对象
  2. function isObject(obj) {
  3. return typeof obj === 'object' && obj != null;
  4. }
  5. function deepCopy(source){
  6. // 判断如果参数不是一个对象,返回改参数
  7. if(!isObject(source)) return source;
  8. // 判断参数是对象还是数组来初始化返回值
  9. let res = Array.isArray(source)?[]:{};
  10. // 循环参数对象的key
  11. for(let key in source){
  12. // 如果该key属于参数对象本身
  13. if(Object.prototype.hasOwnProperty.call(source,key)){
  14. // 如果该key的value值是对象,递归调用深拷贝方法进行拷贝
  15. if(isObject(source[key])){
  16. res[key] = deepCopy(source[key]);
  17. }else{
  18. // 如果该key的value值不是对象,则把参数对象key的value值赋给返回值的key
  19. res[key] = source[key];
  20. }
  21. }
  22. }
  23. // 返回返回值
  24. return res;
  25. }

reduce

这是ES6的一个语法,简单来说就是循环返回前面处理好的值。

  1. Array.prototype.reduce = function (callback,prev) {
  2. //遍历this 数组
  3. for (let i = 0; i < this.length; i++) {
  4. //判断有没有设置初始值
  5. if (typeof prev === "undefined") {
  6. //没有初始值,则调用callback,转入当前值,下一个值,当前index为下一个,当前数组
  7. prev = callback(this[i], this[i + 1], i + 1, this);
  8. } else {
  9. //有初始值,则调用callback,传入prev为第一个值,第二个为当前的i,当前index为i,当前数组
  10. prev = callback(prev, this[i], i, this);
  11. }
  12. }
  13. return prev;
  14. };
  15. arr.reduce((pre,cur,index,arr)=>{})