基础知识-手写篇

214 阅读7分钟

数组扁平化flat方法的多种实现

Array.prototype.flat()

flat()  方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

语法

var newArray = arr.flat([depth])

参数

  • depth 可选

    指定要提取嵌套数组的结构深度,默认值为 1。

返回值

一个包含将数组与子数组中所有元素的新数组。

var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Array.prototype.toString()

toString() 返回一个字符串,表示指定的数组及其元素。

Array 对象覆盖了 Object 的 toString 方法。对于数组对象,toString 方法返回一个字符串,该字符串由数组中的每个元素的 toString() 返回值经调用 join() 方法连接(由逗号隔开)组成。

当一个数组被作为文本值或者进行字符串连接操作时,将会自动调用其 toString 方法。

var i = [1, 2, 3,[1, 2, 3, [1, 2, 3]]];
i.toString()
//"1,2,3,1,2,3,1,2,3"

concat

concat()  方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

//合并两个数组
var alpha = ['a', 'b', 'c'];
var numeric = [1, 2, 3];

alpha.concat(numeric);
// result in ['a', 'b', 'c', 1, 2, 3]
// 合并三个数组
var num1 = [1, 2, 3],
    num2 = [4, 5, 6],
    num3 = [7, 8, 9];

var nums = num1.concat(num2, num3);

console.log(nums);
// results in [1, 2, 3, 4, 5, 6, 7, 8, 9]

Array.isArray()

Array.isArray() 用于确定传递的值是否是一个 Array

总结

let arr = [
  [1],
  [2, 3],
  [4, 5, 6, [7, 8, [9, 10, [11]]]],
  12
]
// 1 flat
console.log(arr.flat(Infinity));
//2 toString
console.log(arr.toString().split(',').map(item => Number(item)));
// 3 stringify
console.log(JSON.stringify(arr).replace(/\[|\]/g, '').split(',').map(item => Number(item)));
// 4 concat 可以展开一层
while (arr.some(item => Array.isArray(item))) {
  arr = [].concat(...arr);
}
console.log(arr)
// 5 实现flat
Array.prototype.myFlat = function () {
  _this = this
  let res = []
  function _flat (arr) {
    for (let i = 0; i < arr.length; i++) {
      if (Array.isArray(arr[i])) {
        _flat(arr[i])
      } else {
        res.push(arr[i])
      }
    }
    return res
  }
  return _flat(_this)
}
console.log(arr.myFlat())

实现一个不可变的对象

不可扩展

Object.preventExtensions()可以使一个对象不可再添加新的属性,参数为目标对象,返回修改后的对象

var obj = { name: 'zhufeng' }
Object.preventExtensions(obj)
obj.age = 10
console.log(obj.age)//undefined

密封

Object.seal()可以使一个对象无法添加新属性的同时,也无法删除旧属性,参数为目标对象,返回修改后的对象

let obj = { name: 'zhufeng' }
Object.seal(obj)

冻结

Object.freeze()可以使一个对象不能再添加新属性,也不可以删除旧属性,且不能修改属性值。参数是目标对象,返回修改后的对象 Object.isFrozen()可以检测一个对象是否冻结,即是否可以增删改,参数的是目标对象,返回布尔值,true表示已经冻结不可再删改

let obj = { name: 'zhufeng' }
Object.freeze(obj)

函数柯里化

定义

在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

通俗易懂的解释:用闭包把参数保存起来,当参数的数量足够执行函数了,就开始执行函数。

原理

function say (company) {
    return function (academy) {
        return function (name) {
            console.log(`我的公司是${company},专业是${academy},名字是${name}`)
        }
    }
}

实现

  • 判断当前函数传入的参数是否大于或等于fn需要参数的数量,如果是,直接执行fn
  • 如果传入参数数量不够,返回一个闭包,暂存传入的参数,并重新返回currying函数
    function currying(fn, ...args) {
      if (args.length >= fn.length) {
        return fn(...args);
      } else {
        return (...args2) => currying(fn, ...args, ...args2);
      }
    }

使用

    function say(company, academy, name) {
        console.log(`我的公司是${company},专业是${academy},名字是${name}`)
    }
    let r = currying(say)
    let r1 = r('蜀国')('武将')
    r1('赵云')//我的公司是蜀国,专业是武将,名字是赵云

字符串提取数字

强类型转换

适用于提取数字在字符串前端

let str = "1412.123abd"
console.log(parseInt(str),parseFloat(str)); // 输出1412  1412.123

遍历拼接

缺点:获取所有的数字,即使是不需要的数字段也会被拼接. 这里是用parseInt(i)>=0判断是否为数字,若使用isNaN(),会把空字符串和空格当做0处理.

let str="%1086143iluhugufu086";
let num="";
for(i of str){
    if(parseInt(i)>=0){      //parseInt返回的是数字或者NAN,只要大于0,即使数字
       num += i;
    }
}
console.log(num);           // 1086143086 会拼接所以的数字

正则

let str ="%1086143iluhugufu086";
let num= str.replace(/[^0-9]/ig,"");
let numArr = str.match(/\d+/g);
let decimalArr = str.match(/\d+\.\d+/g);    // 匹配小数
console.log(num);           // 1086143086
console.log(numArr);        // [ '1086143', '086' ]
console.log(decimalArr);    // ["123.456", "456.789"]

数组去重

给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。

Array.from()

Array.from()  方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]

console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]

参数

  • arrayLike

    想要转换成数组的伪数组对象或可迭代对象。

  • mapFn 可选

    如果指定了该参数,新数组中的每个元素会执行该回调函数。

  • thisArg 可选

    可选参数,执行回调函数 mapFn 时 this 对象。

返回值:一个新的数组实例。

set去重

const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];

let newArr = Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
let newArr = [...new Set(array)]; // [1, 2, 3, 5, 9, 8]

利用对象属性去重

function uniqueArray(array) {
  let map = {};
  let res = [];
  for(var i = 0; i < array.length; i++) {
    if(!map.hasOwnProperty([array[i]])) {
      map[array[i]] = 1;
      res.push(array[i]);
    }
  }
  return res;
}
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
uniqueArray(array); // [1, 2, 3, 5, 9, 8]

indexOf方法去重

利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,如果不等则说明该元素是重复元素

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    return Array.prototype.filter.call(arr, function(item, index){
        return arr.indexOf(item) === index;
    });
}

获取url参数

? & 的区别和作用

1. #

一、#的涵义
#代表网页中的一个位置。其右面的字符,就是该位置的标识符。指定网页位置标识符,有两个方法。一是使用锚点,比如<a name="print"></a>,二是使用id属性,比如<div id="print">

二、HTTP请求不包括#
#是用来指导浏览器动作的,对服务器端完全无用。所以,HTTP请求中不包括#。
比如,访问下面的网址,www.example.com/index.html#…,浏览器实际发出的请求是这样的:

GET /index.html HTTP/1.1
Host: www.example.com

三、#后的字符
在第一个#后面出现的任何字符,都会被浏览器解读为位置标识符。这意味着,这些字符都不会被发送到服务器端。比如,下面URL的原意是指定一个颜色值:www.example.com/?color=#fff,但是,浏览器实际发出的请求是:

GET /?color= HTTP/1.1
Host: www.example.com

四、改变#不触发网页重载
单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页。

五、改变#会改变浏览器的访问历史
每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用"后退"按钮,就可以回到上一个位置。这对于ajax应用程序特别有用,可以用不同的#值,表示不同的访问状态,然后向用户给出可以访问某个状态的链接。值得注意的是,上述规则对IE 6和IE 7不成立,它们不会因为#的改变而增加历史记录。

2. ?

1)连接作用:比如

http://www.xxx.com/Show.asp?id=77&nameid=2905210001&page=1

2)清除缓存:比如

http://www.xxxxx.com/index.html 
http://www.xxxxx.com/index.html?test123123

两个url打开的页面一样,但是后面这个有问号,说明不调用缓存的内容,而认为是一个新地址,重新读取。

3. &

不同参数的间隔符

获取url参数

参考 题目链接 image.png

一、常规正则处理

replace

replace()  方法返回一个由替换值(replacement)替换部分或所有的模式(pattern)匹配项后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。如果pattern是字符串,则仅替换第一个匹配项。

原字符串不会改变。

语法

str.replace(regexp|substr, newSubStr|function)

参数

  • regexp (pattern)

    一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。

  • substr (pattern)

    一个将被 newSubStr 替换的 字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。

  • newSubStr (replacement)

    用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名。参考下面的使用字符串作为参数

  • function (replacement)

    一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考下面的指定一个函数作为参数

返回值

一个部分或全部匹配由替代模式所取代的新的字符串。

代码

function queryURLParams (url) {
  let obj = {};
  url.replace(/([^?=&#]+)=([^?=&#]+)/g, (_, key, value) => {
    if (obj[key] && Array.isArray(obj[key])) { //如果有多个同名参数返回数组
      obj[key].push(value)
    } else if (obj[key]) {
      obj[key] = [].concat(obj[key], value)
    } else {
      obj[key] = value
    }
  });
  url.replace(/#([^?=&#]+)/g, (_, hash) => obj['HASH'] = hash);
  return obj;
}
console.log(queryURLParams('http://www.xxxxxxx.cn/?lx=1&name=JS&from=baidu&lx=2&lx=3#video'));

二、利用字符串内置方法

原理:利用字符串indexOf检测索引位置,substring截取字符串,split按指定符号转为数组...等一系列的字符串内置方法完成需求;

function queryURLParams (url) {
  // 1.首选获取问号和井号后面的值
  let askIndex = url.indexOf('?'), polIndex = url.lastIndexOf('#');
  let askText = url.substring(askIndex + 1, polIndex), polText = url.substring(polIndex + 1);
  // console.log(askText, polText); //=>"lx=1&name=JS&from=baidu"  "video"

  // 2.把获取到的结果进行解析,最后拼成一个对象返回
  let obj = {};
  // 处理哈希值(井号后面的值)
  polText.length > 0 ? obj['HASH'] = polText : null;
  // 问号参数的处理
  if (askText) {
    // askText.split('&') =>["lx=1","name=JS","from=baidu"]
    askText.split('&').forEach(item => {
      // item:循环数组中的每一项
      let arr = item.split('='); //=>"lx=1" => ["lx",1]
      obj[arr[0]]? obj[arr[0]] = [].concat(obj[arr[0]], arr[1]) : obj[arr[0]] = arr[1];
    });
  }
  return obj;
}

console.log(queryURLParams('http://www.xxxxxxx.cn/?lx=1&name=JS&from=baidu&lx=2&lx=3#video'))

反转链表

参考视频 题目

image.png

var reverseList = function(head) {
    let pre = null, cur = head
    while(cur) {
        let tmp = cur.next
        cur.next = pre
        pre = cur
        cur = tmp
    }
    return pre
};

删除链表倒数n节点

题目

image.png

var removeNthFromEnd = function(head, n) {
    let ret = new ListNode(0, head),
        slow = fast = ret;
    while(n--) fast = fast.next;
    if(!fast) return ret.next;
    while (fast.next) {
        fast = fast.next; 
        slow = slow.next
    };
    slow.next = slow.next.next;
    return ret.next;
};