JS操作题

173 阅读4分钟

数组相关

多维数组扁平化

1Array.flat()
   [1,2,3,4,[5,6]].flat()
   
2、[].concat(...[1, 2, 3, [4, 5]]);
   
3、reduce方法
    function flatten(arr) {  
        return arr.reduce((result, item)=> {
            return result.concat(Array.isArray(item) ? flatten(item) : item);
        }, []);
     }
     
4、join & split
    function flatten(arr) {
        return arr.join(',').split(',').map(function(item) {
            return parseInt(item);
        })
    }
    

5、递归
    function flatten(arr) {
        var res = [];
        arr.map(item => {
            if(Array.isArray(item)) {
                res = res.concat(flatten(item));
            } else {
                res.push(item);
            }
        });
        return res;
    }

数组去重

1.ES6 Set对象存储的值总是唯一的】
...new Set(arr)
    var arr = [1, 1, 4, 2, 2, 3, 3, 3, 6, 6, 6];
    arr = Array.from(new Set(arr));
    //console.log(arr);//[1, 4, 2, 3, 6]

2.【使用indexOf】同理 forEach和lastIndexOf forEach和includes ,fliter和includes
创建一个新数组,判断新数组中是否存在该元素如果不存在则将此元素添加到新数组中
	var arr = [1, 1, 4, 2, 2, 3, 3, 3, 6, 6, 6];
	var newArr = [];
	arr.forEach((item) => {
  		newArr.indexOf(item) === -1 ? newArr.push(item) : "";
	});
	//console.log(newArr);//[1, 4, 2, 3, 6]
        
3、【使用双重for循环加splice方法】
	var arr = [1, 1, 4, 2, 2, 3, 3, 3, 6, 6, 6];
	for (var i = 0; i < arr.length; i++) {
 		for (var j = i + 1; j < arr.length; j++) {
    		if (arr[i] == arr[j]) {
      			arr.splice(j,1);
      			j--;
    		}
 		}
	}
	//console.log(arr);//[1, 4, 2, 3, 6]
	
4【sort排序完后,对比前后两个值 一样的删除】
    var arr = [1, 2, 5, 1];
    arr.sort(function(a, b) {
        return a - b
    });
  //  console.log(arr) // [1,1,2,5]
    var result = [];
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] != arr[i + 1]) {
            result.push(arr[i])
        }
    }
  //  console.log(result); [1,2,5]

类数组转化为数组


Array.from(类数组)
Array.prototype.slice.call(类数组)
Array.prototype.concat.apply([], 类数组);

对象相关

深拷贝浅拷贝

www.cnblogs.com/magicg/p/12…


深拷贝
function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断obj子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}  
浅拷贝
function shallowCopy(obj) {
    var newObj = {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}

函数相关

函数防抖(debounce)

参考:www.cnblogs.com/cc-freiheit…

// 非立即执行:触发事件后函数不会立即执行,而是在 n 秒后执行
function debounce(func,wait){
  let timer;
  reuturn function(){    //闭包存储了值
   let context = this; 
   let args =arguments;
   if(timer) clearTimeout(timer);
   timer = setTimerout(()=>{
       func.apply(this,args)
   },wait)
  }
}

//立即执行:触发事件后函数会立即执行,然后 n 秒内不触发事件
 function debounce(func,wait){
   let timer ;
   return function(){
     let context = this;
     let args= arguments;
     if(timer) clearTimeout(timer);
     let callNow = !timer;
     timer = setTimeout(()=>{
         timer =null
     },wait)
     if(callNow) func.apply(context,args);
   }
 }

节流

//时间戳版:函数触发是在时间段内开始的时候
    function throttle(func,wait){
      let previous = 0;
      return function(){
        let now = Date.now()
        let context = this;
        let args = arguments;
        if(now - previous > wait){
          func.apply(this,args);
          previous = now;
        }
      }
    }
 //定时器版本: 函数触发是在时间段内结束的时候
     function throttle = function (func,wait){
       let timeout;
       return function(){
          let context = this;
          let args = arguments;
          if(!timeout){
           timeout = setTimeout(()=>{
             timeout = null;
             func.apply(context,wait)
           },wait)
          }
       }
     }
  
  // 使用方法
  content.onmousemove = throttle(count,1000);

手写call apply bind

1.bind返回对应函数,需要重新调用;call和apply则是立即调用 
2.call的第二个参数,是函数原来的参数;apply的第二个参数可以是一个数组或者类数组

手写call
首先 obj 为可选参数,如果不传的话默认上下文为 window
接下来给 obj 创建一个 fn 属性,并将值设置为需要调用的函数
因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来
然后调用函数并将对象上的函数删除
Function.prototype.myCall = function(obj) {
  obj = obj || window
  obj.fn = this
  const args = [...arguments].slice(1)
  const result = obj.fn(...args)
  delete obj.fn
  return result
}

手写apply
Function.prototype.myApply = function(obj) {
  obj = obj || window
  obj.fn = this
  let result
  // 处理参数和 call 有区别
  if (arguments[1]) {
    result = obj.fn(...arguments[1])
  } else {
    result = obj.fn()
  }
  delete obj.fn
  return result
}

#######call ,apply测试:
---------
var name = '张三';
var state = {
name: 'yf.lin'
};
function fn(a, b) {
    console.log(a + b  + this.name); 
};
call测试:
fn.mycall(state,"我的","名字") // 我的名字yf.lin
fn.mycall(null,"我的","名字") // 我的名字张三
apply测试:
fn.myApply(state,["我的","名字"])  // 我的名字yf.lin
fn.myApply(null,["我的","名字"])  // 我的名字张三
----------


手写bind
bind 的实现对比其他两个函数略微地复杂了一点,因为 bind 需要返回一个函数,需要判断一些边界问题
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

手写 intanceof


function myInstanceof(left, right) {
    // 获取对象的原型
    let proto = Object.getPrototypeOf(left) 
    // 获取构造函数的 prototype 对象 
    let prototype = right.prototype; 
    // 判断构造函数的 prototype 对象是否在对象的原型链上 
    while (true) {
        if (!proto) return false; 
        if (proto === prototype) 
        return true;
       // 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型
       proto = Object.getPrototypeOf(proto); 
   }
}

手写new 操作符

function Person (){
    this.name =name
}

function newOperator(Father){
    if(typeof Father !== 'function'){ 
        throw 'newOperator function the first param must be a function';
    }
    var obj = Object.create(Father.prototype);
    var result = Father.aplly(obj,Array.prototype.sclice.call(arguments,1))
    return result && type of result==='object' && result !== null ?  result :obj;
}


手写ajax

function ajax(options) {
  const {
    url,
    method,
    async,
    data,
    timeout
  } = options;

  const xhr = new XMLHttpRequest()

  //  配置超时事件
  if (timeout) {
    xhr.timeout = timeout;
  }

  return new Promise((resolve, reject) => {
    // 成功
    xhr.onreadystatuschange = () => {
      if (xhr.readyStatus === 4) {
        // 判断http状态码
        if (xhr.status >= 200 &&
          xhr.status < 300 ||
          xhr.status == 304) {
          // 返回拦截器
          resolve(xhr.responseText)
        } else {
          reject()
        }
      }
    }

    // 失败
    xhr.onerror = err => reject(err)
    xhr.ontimeout = () => reject('timeout')

    // 传参处理
    let _params = []
    let encodeData = ''
    if (data instanceof Object) {
      for (let key in data) {
        // 参数编码
        _params.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
      }
      encodeData = _params.join('&')
    }

    // method判断连接
    if (method === 'get') {
      const index = url.indexOf('?')

      if (index === -1) {
        url += '?'
      } else if (index !== url.length - 1) {
        url += '&'
      }

      url += encodeData
    }

    // 建立连接
    xhr.open(method, url, async)
    // 请求拦截器……
    // 发送请求
    if (method === 'get') {
      xhr.send(null)
    } else {
      // post
      xhr.setRequestHeader({
        'content-type': 'application/x-www-form-urlencoded'
      })
      xhr.send(encodeData)
    }
  })
}

hash路由

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>hash router</title>
</head>
<body>
  <ul>
    <li><a href="#/">turn yellow</a></li> 
    <li><a href="#/blue">turn blue</a></li>
    <li><a href="#/green">turn green</a></li>
  </ul>
  <script>
    class Routers {
      constructor() {
        this.routes = {}; // 以键值对的形式储存路由
        this.currentUrl = '';// 当前路由的URL
        window.addEventListener('hashchange', this.refresh.bind(this), false);
      }

      route(path, callback) {
        this.routes[path] = callback ; // {{/: ƒ, /blue: ƒ, /green: ƒ}}
      }

      refresh() {
        this.currentUrl = location.hash.slice(1) || '/'; // 获取当前的hash值
        this.routes[this.currentUrl](); // 执行routes对象中对应k的f
      }
    }

    window.Router = new Routers(); // 实例化routers

    var content = document.querySelector('body');
    function changeBgColor(color) {
      content.style.backgroundColor = color;
    }
    Router.route('/', function () {
      changeBgColor('yellow');
    });
    Router.route('/blue', function () {
      changeBgColor('blue');
    });
    Router.route('/green', function () {
      changeBgColor('green');
    });
  </script>
</body>
</html>

定时器,倒计时

function countDown(startime, endtime) {
    let timer;
    let str = new Date(startime).getTime();
    let end = new Date(endtime).getTime();
    let count = (end - str) / 1000;
    if (count > 0) {
    clearInterval(timer);
    timr = setInterval(() => {
        let d = Math.floor(count / 60 / 60 / 24);
        let h = Math.floor(count / 60 / 60 % 24);
        let m = Math.floor(count / 60 % 60);
        let s = Math.floor(count % 60);
        content.innerHTML = `${d}:${h}:${m}:${s}`
        count--;
    }, 1000)
    }
}
countDown("2019-09-29 12:10:20", '2019-09-29 13:20:30')

实现add(1)(2)(3)

第一种:
var add = function (m) {
    var temp = function (n) {
        return add(m + n);
    }
    temp.toString = function () {
        return m;
    }
    return temp;
};
add(3)(4)(5); // 12

第二种(闭包形式):
function add(a){
  const sum = (b)=>{ // 使用闭包
    a = a+b;
    return sum ;
  };
  sum.toString = ()=>a; // 重写toSting() 方法
  return sum; // 返回一个函数
}
console.log(add(1)(2)(3)); //6

第三种(函数的柯里化):
const add = function() {
      let args = Array.prototype.slice.call(arguments);
      
        // 内层处理
        let inner = function() {
            args.push(...arguments); // 内外层参数合并
            return inner;
        }

        inner.toString = function() {
            return args.reduce((prev, cur) => {
                return prev + cur;
            });
        }
        
       return inner;
   }
console.log(add1(1)(2)(3,5)); //10