本文主要是介绍「面面俱到」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 方法
- toString()方法
-
==符号
- 如果一个值是 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 键值都可以。
- Set
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) {// 这边要先取一下定时器的值,因为后面又定义了定时器不能直接用!timeoutvar 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 的防抖节流,先就这么多了,后续完善这一篇我在慢慢改动一下,莫忘点赞三连哦嘻嘻嘻~。后续会持续更新《面面俱到》系列
面面俱到系列传送门
- JS(包含es6)基础篇
- CSS篇
- 原型继承篇
- Vue篇
- 前端须知网络
- webpack篇
这篇关于「面面俱到」JS(包含es6)基础篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!