「面面俱到」JS(包含es6)基础篇

在这里插入图片描述

前言

这段时间整理一下《面面俱到系列》,主要还是根据前端面试里面自己总结的一些问题,从基础部分起比较全面。内容非常基础但同样也非常重要,后面也有一些常见问题。当然这一部分并不是全部,后续还有需要补充的可以评论一下加上去就好了。

数据类型

JavaScript 共六种数据类型,分别是:undefined、null、boolean、number、string、object

基本类型

常见的有:string、number、boolean、null、undefined,symbol 我们就暂时不列入进去。
基本类型因为数据大小固定存储在栈之中,我们无法间接改变基本类型的值,就看下面这个很简单的例子。

let a = 1;
let b = a;
b = 2
console.log(a); // 1
function change(num) {
    num = num++;
    return num;
}
change(a);
console.log(a); // 1

引用类型

常见的:对象(当然数组、函数这一些都可以看做是对象),引用类型因为数据大小不固定存储在堆之中。

类型判断方法

  • typeof
    typeof 能检测出六种类型的值string、number、boolean、function、undefined、object,但是,除此之外 Object 下还有很多细分的类型,这就是该方法的缺陷了。
    var date = new Date();
    console.log(typeof date); // object
    
  • toString 方法
    我们基于对象原型上的 toString 方法结合 call 方法可以更好的去做一个类型判断。
    let a = 1;
    let date = new Date();
    console.log(Object.prototype.toString.call(a));  // [object Number]
    console.log(Object.prototype.toString.call(date)) // [object Date]
    

类型转化

类型转化平常可能也是我们会碰到的一个点,类型转化方法大家应该不陌生,这边主要提及一下运算符符隐式类型转换。

  • 原始值转化为布尔值
    这边就列举一下假值

    // 都返回false
    Boolean(undefined);
    Boolean(null)
    Boolean(0)
    Boolean(NaN)
    Boolean("")
    
  • 原始值转化为字符串

    原始值 + ""
    原始值.toString()
    
  • 原始值转为数字

    // 字符串中不是数子这样会返回NAN,中间有空格也会返回NAN
    let a = '11';
    Number(a);
    let result = + a;
    
  • 对象到原始值

    • 对象转布尔值都为 true
    • 对象到字符串
    • 对象到字符串就是基于这两种方法,那么具体的对应汇总概念如下大家可以对应查看。
      • toString()方法
        在这里插入图片描述
      • valueOf 方法
        在这里插入图片描述
  • ==符号

    • 如果一个值是 null,另一个值是undefined,则相等
    • 如果一个是字符串,另一个值是数字,则把字符串转换成数字,进行比较
    • 如果任意值是 true,则把 true 转换成 1 再进行比较;如果任意值是 false,则把 false 转换成 0 再进行比较

深浅克隆

原型原型链这边小编就不在提了备战篇里面有,说到基本类型和引用类型就要提及到深浅拷贝的实现,引用类型对象都不能以直接赋值的方式去实现拷贝(指向的内容地址还是相同的)。

我们把这种复制引用的拷贝方法称之为浅拷贝,与之对应的就是深拷贝,深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。

数组深浅拷贝

  • 数组浅拷贝(这边用到的一些数组方法会在后面详细说道)
    借助数组方法实现

    let arr = ['father', 1, true, undefined, null];
    let new_arr = arr.concat()
    
  • 数组深拷贝
    通过 JSON 方法去实现

    let deep_arr = JSON.parse( JSON.stringify(arr) );
    

对象深浅拷贝

上面几种方法算是针对于数组对象的几种克隆语法糖,那么我们实现一下通用的对象拷贝方法

  • 对象浅拷贝
let shallowClone = function(obj) {
    if (typeof obj !== 'object') {
        return;
    }
    // 根据obj的类型判断是新建一个数组还是对象
    var newObj = obj instanceof Array ? [] : {};
    for (let key in obj) {
        // 这一步做一个判定防止拷贝了原型链上的东西
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}
  • 对象深拷贝
    采用递归的方式是实现,理解上的话往简单的想就好了,如果对象内部属性还是对象则再次递归调用自身。
var deepClone = function(obj) {
    if (typeof obj !== 'object') {
        return;
    }
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}

常见数组问题

数组去重

  • 双重循环
    双重循环原理就是,我们创建一个新的数组,然后将原数组每一项元素插入到新数组中去,并在每一次插入前与新数组里面每一项作对比。相同则取消插入。
let arr = [1, 1, 'srting', 'string'];
function clear(arr) {
    let arrLength = arr.length;
    let new_arr = [];
    for(var i = 0; i < arrLength; i++) {
        for(var j=0; j < new_arr.length; j++) {
            if(new_arr[j] === arr[i]) {
                break;
            }
        }
        // 这里就表示循环对比完,没有相同的
        if(j == new_arr.length) {
            new_arr.push(arr[i])
        }
    }
    return new_arr
}
clear(arr) // [1, 'string']
  • 借助 set、map 方法

    • Set
      这是一种无重复值的有序列表,Set 允许对它包含的数据进行快速访问,从而增加了一个追踪离散值的更有效方式。下面列举几种关于他的基本方法
    // 创建set对象
    let set = new Set();
    set.add(1); // 通过add添加元素
    console.log(set.size);  // 返回set的长度
    console.log(set.has(1))  // 返回true
    set.delete(1); // 删除指定元素
    set.clear();   //删除所有元素
    
    • Map
      Map 更像是一种数据关联,为可以为一个对象设置一个键值一起存储到 Map 对象,然后通过键值去访问到数据,json 中键值只能是字符型,但 Map 键值都可以。
let map = new Map();
map.set(键值,键值对应值);
// 直接添加
let map = new Map([["name", "sichen"], ["age", 20]]);
map.forEach(function(vaule, key, ownSet){
    console.log(vaule);
});// 第一、二个参数为map对象首元素,第三个参数为map自身

去重实现:

var unique = (arr) => [...new Set(arr)]
// 或者通过Array.from方法代替三点运算符操作
var unique = (arr) => Array.from(new Set(arr));

// map实现
function unique (arr) {
  const seen = new Map()
  return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}
  • 字符串去重
    因为这边写到了去重那么还是提及一下字符串去重的一个方法
let str = 'aabc';
    function reudce(str) {
        let result = [...new Set(str)].join('');
        return result;
    }
console.log(reudce(str));

求最大最小值

let arr = [1, 1, 2, 2];
console.log(Math.max(...arr)); // 2
console.log(Math.min(...arr)); // 1

// 排序在取出第一位或最后一位
console.log(arr.sort((a, b) => a-b)[[arr.length - 1]]); // 2

数组排序

// 粗暴直接冒泡排序
let arr = [1, 2, 4, 3];
let len = arr.length
for(let i = 0; i< len; i++) {
    for(let j = 0; j < len - 1; j++) {
        if(arr[j] > arr[j+1]) {
            let temp;
            temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
// 借助sort方法
arr.sort((a, b) => a-b)

数组乱序

arr.sort(function(){
    return Math.random() - 0.5;
});

输出不同项项数

通过 object 的 key 值唯一性来实现

function returnCount(arry) {
    var obj = {};
    for(var i = 0; i < arry.length; i++) {
        var each = arry[i];
        //通过obj的key值确定数组每一项的唯一性
        if(typeof(obj[each]) == 'undefined') {
            obj[each] = 1;
        }
        else {
            obj[each]++;
        }
    };
    return obj;
}
let arr = [1,1,2,2,3,3]
console.log(returnCount(arr));

JS 防抖节流

这个模块的背景很简明,都是防止事件频繁触发,请求频繁会导致页面卡顿,刚刚接触到这两个点的同学可能会有点混淆防抖和节流这两个点,所以这边详细介绍一下。

防抖

防抖是事件触发一定时间后在去执行,如果在这个time内又触发了这个事件,那么就清空之前的那个计时器重新开始计时,如此反复。我们来看代码实现。

// 明确方法参数:触发事件函数、防抖间隔时间、立即执行
function antiShake(fn, wait, immediate) {
    let timeout;
    return function () {
        var context = this;
        var args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 这边要先取一下定时器的值,因为后面又定义了定时器不能直接用!timeout
            var callNow = !timeout;
            timeout = setTimeout(function() {
                timeout = null;
            }, wait)
            if (callNow) fn.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                fn.apply(context, args);
            }, wait);
        }
    }
}

这边还是稍微解析一下代码

仅用定时器我们目的方法调用antiShake方法那么他的 this 指向是windows(我个人这样理解,该方法调用生成闭包antiShake方法执行完作用域链没被回收),但是实际情况我们的方法被绑定到事件上时是指向那个触发事件的 DOM 元素所以这边函数内部利用apply方法改变了一下 this 指向。
事件处理函数中会提供事件对象 event,同上的意思我们如果不定义一下的话 event 对象会是空。
如果我们不加上一个immediate立即触发这样的一个判断的话,每一次执行都要等待一个防抖的时间周期,很显然是没有必要的,我们是防止事件多次触发,但我们触发事件的时候是不能等的嘛

节流

如果你持续触发事件,每隔一段时间,只执行一次事件。当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。

function throttle(func, wait) {
    var timeout;
    return function() {
        context = this;
        args = arguments;
        if (!timeout) {
            timeout = setTimeout(function(){
                timeout = null;
                func.apply(context, args)
            }, wait)
        }
    }
}

总结

这一篇备战主要也是 js 基础部分的一些常见的问题,数据类型、类型判断、数组的一些操作方法、JS 的防抖节流,先就这么多了,后续完善这一篇我在慢慢改动一下,莫忘点赞三连哦嘻嘻嘻~。后续会持续更新《面面俱到》系列

面面俱到系列传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值