zoukankan      html  css  js  c++  java
  • js高级用法----手写js原生方法

    1、call 方法

    /**
     * _call
     *
     * @param { context } context
     * @param { arguments } arguments
     */
    Function.prototype._call = function(context) {
      // 如果没有传或传的值为空对象 context指向window
      context = context || window
      let fn = Symbol(context)
      context[fn] = this //给context添加一个方法 指向this
      // 处理参数 去除第一个参数this 其它传入fn函数
      let args = [...arguments].slice(1) //[...xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组
      context[fn](...args) //执行fn
      delete context[fn] //删除方法
    }
    
    var obj = {
        name: 'Bob',
        age: '18',
        fn() {
            console.log(`My name is ${this.name}, my age is ${this.age}`)
        }
    }
    
    var dog = {
        name: 'Snoopy',
        age: 3
    }
    obj.fn.call(dog,'daddata','ttt','yuyuyuuy') // My name is Snoby, my age is 3
    obj.fn._call(dog,'daddata','ttt','yuyuyuuy') // My name is Snoby, my age is 3

    2、 apply 方法

    Function.prototype._apply = function(context) {
        // 如果没有传或传的值为空对象 context指向window
        context = context || window
        let fn = Symbol(context)
        context[fn] = this 
        let arg = [...arguments].slice(1) 
        context[fn](arg) //执行fn
        delete context[fn] //删除方法
    }

    3、bind方法

    /**
     * _bind
     *
     * @param {*} context
     */
    Function.prototype._bind = function (context) {
      //返回一个绑定this的函数,我们需要在此保存this
      let self = this
      // 可以支持柯里化传参,保存参数
      let arg = [...arguments].slice(1)
      // 返回一个函数
      return function () {
        //同样因为支持柯里化形式传参我们需要再次获取存储参数
        let newArg = [...arguments]
        // 返回函数绑定this,传入两次保存的参数
        //考虑返回函数有返回值做了return
        return self.apply(context, arg.concat(newArg))
      }
    }

    4、promise方法

    function MyPromise(exceptor) {
      let _this = this;
      _this.reason = '';
      _this.value = '';
      _this.resolveFnArr = [];
      _this.rejectFnArr = [];
      _this.status = 'pending';
      function resolve(val) {
        if (_this.status === 'pending') {
          _this.value = val;
          _this.status = 'fulfiled';
          _this.resolveFnArr.forEach(fn => fn())
        }
      }
    
      function reject(reason) {
        if (_this.status === 'pending') {
          _this.reason = reason;
          _this.status = 'reject';
          _this.rejectFnArr.forEach(fn => fn())
        }
      }
    
      try {
        exceptor(resolve, reject);
      } catch (error) {
        reject();
      }
    }
    
    MyPromise.prototype.then = function(resolve, reject) {
      let _this = this;
      if (_this.status === 'fulfiled') {
        resolve(_this.value);
      }
      if (_this.status === 'reject') {
        reject(_this.reason);
      }
    
      if (_this.status === 'pending') {
        _this.resolveFnArr.push(() => {resolve(_this.value)})
        _this.rejectFnArr.push(() => {reject(_this.reason)})
      }
    }

    5、全面的promise写法

    (function(window,undefined){
     
      // resolve 和 reject 最终都会调用该函数
      var final = function(status,value){
          var _this = this, fn, status;
       
          if(_this._status !== 'PENDING') return;
       
          // 所以的执行都是异步调用,保证then是先执行的
          setTimeout(function(){
              _this._status = status;
              status = _this._status === 'FULFILLED'
              queue = _this[status ? '_resolves' : '_rejects'];
       
              while(fn = queue.shift()) {
                  value = fn.call(_this, value) || value;
              }
       
              _this[status ? '_value' : '_reason'] = value;
              _this['_resolves'] = _this['_rejects'] = undefined;
          });
      }
       
       
      //参数是一个函数,内部提供两个函数作为该函数的参数,分别是resolve 和 reject
      var MyPromise = function(resolver){
          if (!(typeof resolver === 'function' ))
              throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
          //如果不是promise实例,就new一个
          if(!(this instanceof MyPromise)) return new MyPromise(resolver);
       
          var _this = this;
          _this._value;
          _this._reason;
          _this._status = 'PENDING';
          //存储状态
          _this._resolves = [];
          _this._rejects = [];
       
          //
          var resolve = function(value) {
              //由於apply參數是數組
              final.apply(_this,['FULFILLED'].concat([value]));
          }
       
          var reject = function(reason){
              final.apply(_this,['REJECTED'].concat([reason]));
          }
       
          resolver(resolve,reject);
      }
       
      MyPromise.prototype.then = function(onFulfilled,onRejected){
          var _this = this;
          // 每次返回一个promise,保证是可thenable的
          return new MyPromise(function(resolve,reject){
       
              function handle(value) {
                  // 這一步很關鍵,只有這樣才可以將值傳遞給下一個resolve
                  var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value;
       
                  //判断是不是promise 对象
                  if (ret && typeof ret ['then'] == 'function') {
                      ret.then(function(value) {
                          resolve(value);
                      }, function(reason) {
                          reject(reason);
                      });
                  } else {
                      resolve(ret);
                  }
              }
       
              function errback(reason){
                  reason = typeof onRejected === 'function' && onRejected(reason) || reason;
                  reject(reason);
              }
       
              if(_this._status === 'PENDING'){
                  _this._resolves.push(handle);
                  _this._rejects.push(errback);
              }else if(_this._status === FULFILLED){ // 状态改变后的then操作,立刻执行
                  callback(_this._value);
              }else if(_this._status === REJECTED){
                  errback(_this._reason);
              }
          });
      }
       
      MyPromise.prototype.catch = function(onRejected){
          return this.then(undefined, onRejected)
      }
       
      MyPromise.prototype.delay = function(ms,value){
          return this.then(function(ori){
              return MyPromise.delay(ms,value || ori);
          })
      }
       
      MyPromise.delay = function(ms,value){
          return new MyPromise(function(resolve,reject){
              setTimeout(function(){
                  resolve(value);
                  console.log('1');
              },ms);
          })
      }
       
      MyPromise.resolve = function(arg){
          return new MyPromise(function(resolve,reject){
              resolve(arg)
          })
      }
       
      MyPromise.reject = function(arg){
          return MyPromise(function(resolve,reject){
              reject(arg)
          })
      }
       
      MyPromise.all = function(promises){
          if (!Array.isArray(promises)) {
              throw new TypeError('You must pass an array to all.');
          }
          return MyPromise(function(resolve,reject){
              var i = 0,
                  result = [],
                  len = promises.length,
                  count = len
       
              //这里与race中的函数相比,多了一层嵌套,要传入index
              function resolver(index) {
                return function(value) {
                  resolveAll(index, value);
                };
              }
       
              function rejecter(reason){
                  reject(reason);
              }
       
              function resolveAll(index,value){
                  result[index] = value;
                  if( --count == 0){
                      resolve(result)
                  }
              }
       
              for (; i < len; i++) {
                  promises[i].then(resolver(i),rejecter);
              }
          });
      }
       
      MyPromise.race = function(promises){
          if (!Array.isArray(promises)) {
              throw new TypeError('You must pass an array to race.');
          }
          return MyPromise(function(resolve,reject){
              var i = 0,
                  len = promises.length;
       
              function resolver(value) {
                  resolve(value);
              }
       
              function rejecter(reason){
                  reject(reason);
              }
       
              for (; i < len; i++) {
                  promises[i].then(resolver,rejecter);
              }
          });
      }
       
      window.MyPromise = MyPromise;
       
      })(window);

     6、filter

    Array.prototype.filter = function(callback, thisArg) {
      if (this == undefined) {
        throw new TypeError('this is null or not undefined');
      }
      if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function');
      }
      const res = [];
      // 让O成为回调函数的对象传递(强制转换对象)
      const O = Object(this);
      // >>>0 保证len为number,且为正整数
      const len = O.length >>> 0;
      for (let i = 0; i < len; i++) {
        // 检查i是否在O的属性(会检查原型链)
        if (i in O) {
          // 回调函数调用传参
          if (callback.call(thisArg, O[i], i, O)) {
            res.push(O[i]);
          }
        }
      }
      return res;
    }

    7、map方法

    Array.prototype.map = function(callback, thisArg) {
      if (this == undefined) {
        throw new TypeError('this is null or not defined');
      }
      if (typeof callback !== 'function') {
        throw new TypeError(callback + ' is not a function');
      }
      const res = [];
      // 同理
      const O = Object(this);
      const len = O.length >>> 0;
      for (let i = 0; i < len; i++) {
        if (i in O) {
          // 调用回调函数并传入新数组
          res[i] = callback.call(thisArg, O[i], i, this);
        }
      }
      return res;
    }

    8、forEach方法

    Array.prototype.forEach = function(callback, thisArg) {
      if (this == null) {
        throw new TypeError('this is null or not defined');
      }
      if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
      }
      const O = Object(this);
      const len = O.length >>> 0;
      let k = 0;
      while (k < len) {
        if (k in O) {
          callback.call(thisArg, O[k], k, O);
        }
        k++;
      }
    }

    9、reduce方法

    Array.prototype.reduce = function(callback, initialValue) {
      if (this == undefined) {
        throw new TypeError('this is null or not defined');
      }
      if (typeof callback !== 'function') {
        throw new TypeError(callbackfn + ' is not a function');
      }
      const O = Object(this);
      const len = this.length >>> 0;
      let accumulator = initialValue;
      let k = 0;
      // 如果第二个参数为undefined的情况下
      // 则数组的第一个有效值作为累加器的初始值
      if (accumulator === undefined) {
        while (k < len && !(k in O)) {
          k++;
        }
        // 如果超出数组界限还没有找到累加器的初始值,则TypeError
        if (k >= len) {
          throw new TypeError('Reduce of empty array with no initial value');
        }
        accumulator = O[k++];
      }
      while (k < len) {
        if (k in O) {
          accumulator = callback.call(undefined, accumulator, O[k], k, O);
        }
        k++;
      }
      return accumulator;
    }

    10、debounce(防抖)

    const debounce = (fn, time) => {
      let timeout = null;
      return function() {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          fn.apply(this, arguments);
        }, time);
      }
    };

    11、throttle(节流)

    const throttle = (fn, time) => {
      let flag = true;
      return function() {
        if (!flag) return;
        flag = false;
        setTimeout(() => {
          fn.apply(this, arguments);
          flag = true;
        }, time);
      }
    }

    12、模拟new操作

    function newOperator(ctor, ...args) {
      if (typeof ctor !== 'function') {
        throw new TypeError('Type Error');
      }
      const obj = Object.create(ctor.prototype);
      const res = ctor.apply(obj, args);
    
      const isObject = typeof res === 'object' && res !== null;
      const isFunction = typeof res === 'function';
      return isObject || isFunction ? res : obj;
    }

    13、instanceof

    const myInstanceof = (left, right) => {
      // 基本数据类型都返回false
      if (typeof left !== 'object' || left === null) return false;
      let proto = Object.getPrototypeOf(left);
      while (true) {
        if (proto === null) return false;
        if (proto === right.prototype) return true;
        proto = Object.getPrototypeOf(proto);
      }
    }

    14、原型继承

    function Parent() {
      this.name = 'parent';
    }
    function Child() {
      Parent.call(this);
      this.type = 'children';
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;

    15、Object.assign

    Object.defineProperty(Object, 'assign', {
      value: function(target, ...args) {
        if (target == null) {
          return new TypeError('Cannot convert undefined or null to object');
        }
        
        // 目标对象需要统一是引用数据类型,若不是会自动转换
        const to = Object(target);
    
        for (let i = 0; i < args.length; i++) {
          // 每一个源对象
          const nextSource = args[i];
          if (nextSource !== null) {
            // 使用for...in和hasOwnProperty双重判断,确保只拿到本身的属性、方法(不包含继承的)
            for (const nextKey in nextSource) {
              if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                to[nextKey] = nextSource[nextKey];
              }
            }
          }
        }
        return to;
      },
      // 不可枚举
      enumerable: false,
      writable: true,
      configurable: true,
    })

    16、深拷贝

    const cloneDeep1 = (target, hash = new WeakMap()) => {
      // 对于传入参数处理
      if (typeof target !== 'object' || target === null) {
        return target;
      }
      // 哈希表中存在直接返回
      if (hash.has(target)) return hash.get(target);
    
      const cloneTarget = Array.isArray(target) ? [] : {};
      hash.set(target, cloneTarget);
    
      // 针对Symbol属性
      const symKeys = Object.getOwnPropertySymbols(target);
      if (symKeys.length) {
        symKeys.forEach(symKey => {
          if (typeof target[symKey] === 'object' && target[symKey] !== null) {
            cloneTarget[symKey] = cloneDeep1(target[symKey]);
          } else {
            cloneTarget[symKey] = target[symKey];
          }
        })
      }
    
      for (const i in target) {
        if (Object.prototype.hasOwnProperty.call(target, i)) {
          cloneTarget[i] =
            typeof target[i] === 'object' && target[i] !== null
            ? cloneDeep1(target[i], hash)
            : target[i];
        }
      }
      return cloneTarget;
    }

    17、JSONP

    const jsonp = ({ url, params, callbackName }) => {
      const generateUrl = () => {
        let dataSrc = '';
        for (let key in params) {
          if (Object.prototype.hasOwnProperty.call(params, key)) {
            dataSrc += `${key}=${params[key]}&`;
          }
        }
        dataSrc += `callback=${callbackName}`;
        return `${url}?${dataSrc}`;
      }
      return new Promise((resolve, reject) => {
        const scriptEle = document.createElement('script');
        scriptEle.src = generateUrl();
        document.body.appendChild(scriptEle);
        window[callbackName] = data => {
          resolve(data);
          document.removeChild(scriptEle);
        }
      })
    }

    18、AJAX

    const getJSON = function(url) {
      return new Promise((resolve, reject) => {
        const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
        xhr.open('GET', url, false);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.onreadystatechange = function() {
          if (xhr.readyState !== 4) return;
          if (xhr.status === 200 || xhr.status === 304) {
            resolve(xhr.responseText);
          } else {
            reject(new Error(xhr.responseText));
          }
        }
        xhr.send();
      })
    }

    19、图片懒加载

    // 可以给img标签统一自定义属性data-src='default.png',当检测到图片出/现在窗口之后再补充src属性,此时才会进行图片资源加载。
    function lazyload() {
      const imgs = document.getElementsByTagName('img');
      const len = imgs.length;
      // 视口的高度
      const viewHeight = document.documentElement.clientHeight;
      // 滚动条高度
      const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
      for (let i = 0; i < len; i++) {
        const offsetHeight = imgs[i].offsetTop;
        if (offsetHeight < viewHeight + scrollHeight) {
          const src = imgs[i].dataset.src;
          imgs[i].src = src;
        }
      }
    }

    20、滚动加载

    // 原理就是监听页面滚动事件,分析clientHeight、scrollTop、scrollHeight三者的属性关系。
    window.addEventListener('scroll', function() {
      const clientHeight = document.documentElement.clientHeight;
      const scrollTop = document.documentElement.scrollTop;
      const scrollHeight = document.documentElement.scrollHeight;
      if (clientHeight + scrollTop >= scrollHeight) {
        // 检测到滚动至页面底部,进行后续操作
        // ...
      }
    }, false);

    21、渲染几万条数据不卡住页面

    // 渲染大数据时,合理使用createDocumentFragment和requestAnimationFrame,将操作切分为一小段一小段执行。
    setTimeout(() => {
      // 插入十万条数据
      const total = 100000;
      // 一次插入的数据
      const once = 20;
      // 插入数据需要的次数
      const loopCount = Math.ceil(total / once);
      let countOfRender = 0;
      const ul = document.querySelector('ul');
      // 添加数据的方法
      function add() {
        const fragment = document.createDocumentFragment();
        for(let i = 0; i < once; i++) {
          const li = document.createElement('li');
          li.innerText = Math.floor(Math.random() * total);
          fragment.appendChild(li);
        }
        ul.appendChild(fragment);
        countOfRender += 1;
        loop();
      }
      function loop() {
        if(countOfRender < loopCount) {
          window.requestAnimationFrame(add);
        }
      }
      loop();
    }, 0)

    22、将VirtualDom转化为真实DOM结构

    // vnode结构:
    // {
    //   tag,
    //   attrs,
    //   children,
    // }
    
    //Virtual DOM => DOM
    function render(vnode, container) {
      container.appendChild(_render(vnode));
    }
    function _render(vnode) {
      // 如果是数字类型转化为字符串
      if (typeof vnode === 'number') {
        vnode = String(vnode);
      }
      // 字符串类型直接就是文本节点
      if (typeof vnode === 'string') {
        return document.createTextNode(vnode);
      }
      // 普通DOM
      const dom = document.createElement(vnode.tag);
      if (vnode.attrs) {
        // 遍历属性
        Object.keys(vnode.attrs).forEach(key => {
          const value = vnode.attrs[key];
          dom.setAttribute(key, value);
        })
      }
      // 子数组进行递归操作
      vnode.children.forEach(child => render(child, dom));
      return dom;
    }

     

  • 相关阅读:
    Noip2015总结
    BZOJ2457 BeiJing2011 双端队列
    Noip模拟考第三题——饥饿游戏
    HDU 2196 求树上所有点能到达的最远距离
    O(V*n)的多重背包问题
    Noip2008双栈排序
    USACO 4.1.2 栅栏的木料
    字符串专题
    网络流24题刷题记录
    解模线性方程组 非互质中国剩余定理
  • 原文地址:https://www.cnblogs.com/zhangycun/p/14010488.html
Copyright © 2011-2022 走看看