闭包:
概念:函数执行后返回结果是一个内部函数, 并被外部变量所引用,如果内部函数持有被执行函数的作用域变量,形成了闭包。
可以在内部函数访问到外部函数作用域。使用闭包,一可以读取函数中的变量,二可以将函数中的变量存储在内存中,保护变量不被污染。正是因闭包可以把函数中的变量存储在内存中,会对内存有消耗,所以不能滥用闭包,否则会造成内存泄露。
防抖
概念: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时!
场景: 防止按钮多次提交,只执行最后一次;搜索框-防止输入重复发请求。
function fun(a) {
console.log(a, 'btn click!')
}
function debounce(callback, wait) {
let timer;
return function () {
const that = this, args = arguments;
clearInterval(timer);
timer = setTimeout(() => {
callback.apply(that, args);
}, wait);
}
}
const btn = document.getElementById('btn');
btn.onclick = debounce(() => { fun('8888') }, 1000);
节流
概念:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
场景: 拖拽;缩放场景。
实现方案: 时间戳和定时器实现。
时间戳
function throttle(callback, time) {
let pre = 0;
let that, agrs;
return function () {
that = this; agrs = arguments;
let now = +new Date();
if (now - pre > time) {
callback.apply(that, agrs);
pre = now;
}
}
}
定时器
function throttles(callback, time) {
let timer = null;
return function () {
const that = this; const agrs = arguments;
timer = setInterval(() => {
callback.apply(that, agrs)
timer = null;
}, time);
}
}
const btn = document.getElementById('btn');
btn.onclick = throttles(() => { fun('9999') }, 1000);
浅拷贝&&深拷贝
浅拷贝:是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝, 如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
区别:主要区别是其在内存中的存储类型不同,栈为自动分配的内存空间,它由系统自动释放;而堆则是动态分配的内存,大小不定也不会自动释放。[基本数据类型存放在栈中,引用类型存放在堆中]。
举个?
let k = {
a: 1,
b: 2,
c: 3,
d: {
e: 4,
},
f: {
g: 5,
}
};
let h = JSON.parse(JSON.stringify(k)); // 深复制包含子对象- 深拷贝
let l = { ...k }; // 拷贝一层但不包含子对象 // 浅拷贝
let o = k; // 赋值
解析:
o.b = 22; // k、o 相同: { a: 1, b: 22, c:3, d: {e: 5}, f: {g: 6} }
l.c = 33; // k: 不变; l: { a: 1, b: 22, c:33, d: {e: 5}, f: {g: 6} }
l.d.e = 55; // k、l 相同: { a: 1, b: 2, c:3, d: {e: 55}, f: {g: 6} }
h.f.g = 66; // k:不变; h: { a: 1, b: 2, c:3, d: {e: 5}, f: {g: 66} }
结论:
浅拷贝方法:
- es6-解构;
- Object.assign();
- lodash-_.clone;
- Array.prototype-[concat()|slice()].
深拷贝方法:
- JSON.parse(JSON.stringify());
- lodash-_.cloneDeep;
- 递归拷贝所有层级属性.
数组
- 数组去重
const arr = [1, 2, 3, 4, 5, 3, 2, 5, 5, 4, 3, 6];
1.定义一个新数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组.
function repeat1(arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) { // forEach / filter
if (newArr.indexOf(arr[i]) === -1) { // !newArr.includes(arr[i])
newArr.push(arr[i]);
}
}
return newArr;
}
2.先将原数组排序,再与相邻的进行比较,如果不同放入新数组.
function repeat2(arr) {
let as = arr.sort();
let newArr = [arr[0]];
for (let i = 1; i < arr.length; i++) {
if (as[i] !== as[i - 1]) {
newArr.push(as[i]);
}
}
return newArr;
}
3.使用es6 Set.
function repeat3(arr) {
return [...new Set(arr)];
}
4.与相邻的进行比较,截取相同的元素.
function repeat4(arr) { // 会改变原数组
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
- 数组排序
冒泡排序:比较每相邻的两个数,若前者大于后者,把两个数进行位置交换;第一轮可以选出最大的数放在后面,经过n-1轮,完成每个数的排序。
const sort = [1, 2, 10, 8, 3, 6, 7, 5];
function arrSort(arrs) {
for (let i = 0; i < arrs.length - 1; i++) {
for (let j = 0; j < arrs.length - i - 1; j++) {
if (arrs[j] > arrs[j + 1]) {
const temp = arrs[j];
arrs[j] = arrs[j + 1];
arrs[j + 1] = temp;
}
}
}
return arrs;
}
console.log(arrSort(sort)); // [1, 2, 3, 5, 6, 7, 8, 10]
插入排序:获取当前索引元素, 从右向左搜索放到数组正确位置。
function insertionSort(arr) {
let preIndex, current;
for (let i = 1; i < arr.length; i++) {
preIndex = i - 1; current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
return arr;
}
console.log(insertionSort(sort)); // [1, 2, 3, 5, 6, 7, 8, 10]
快速排序:找到数组的中间数, 并创建两个空数组left&right; 然后遍历数组,数组里每一项与中间数比较,小于中间数放在left,大于中间数放在right, 最后将left和right递归调用拼接完整的数组。
function fastSort(sort) {
const arr = JSON.parse(JSON.stringify(sort));
if (arr.length <= 1) return arr;
const index = Math.floor(arr.length / 2);
const temp = arr.splice(index, 1);
let left = [], right = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] < temp[0]) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return fastSort(left).concat(temp, fastSort(right));
}
console.log(fastSort(sort)) // [1, 2, 3, 5, 6, 7, 8, 10]
执行结果
- 执行函数
function side(arr) {
arr[0] = arr[2];
return arr;
}
function a(a, b, c = 3) {
c = 10;
console.log('arguments', arguments)
side(arguments);
return a + b + c;
}
const result = a(1, 1, 1);
console.log(result); // 12
- 获取 arguments 值
function getArguments() {
const args = new Array(arguments.length); // 创建 Array 对象
console.log('args', args);
for (let i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return args;
}
getArguments(1, 2, 3); // [1, 2, 3]
- 比较大小值
var min = Math.min(), max = Math.max();
console.log(min < max);
解析:Math.min 的参数是 0 个或者多个,如果多个参数很容易理解,返回参数中最小的。如果没有参数,则返回 Infinity,无穷大; 而 Math.max 没有传递参数时返回的是-Infinity.所以输出 false。
- 立即执行的函数
var a = 1;
(function a() {
a = 2;
console.log(a);
})();
解析:立即执行的函数表达式(IIFE)的函数名称跟内部变量名称重名后,函数名称优先,因为函数名称是不可改变的,内部会静默失败,在严格模式下会报错。
- 隐式类型转化
var b = [0]; // "0"
if (b) {
console.log(b === true);
} else {
console.log(b)
}
解析:数组从非 primitive 转为 primitive 的时候会先隐式调用 join 变成“0”,string 和 boolean 比较的时候,两个都先转为 number 类型再比较,最后就是 0==1 的比较了
- delete使用原则
var company = {
address: 'beijing'
}
var yideng = Object.create(company);
delete yideng.address
console.log(yideng.address); // beijing
解析:delete 操作符用来删除一个对象的属性。
- 函数表达式
var foo = function bar() { return 12; };
// console.log(typeof bar()); // bar is not defined
解析:这种命名函数表达式函数只能在函数体内有效
- typeof
var x = 1;
if (function f() { }) {
// alert(typeof f) // undefind
x += typeof f;
}
console.log(x);
- var | let
for (let i = 0; i < 3; i++) {
setTimeout(() => {
// console.log(i)
}, 1);
}
// 0 1 2
for (var j = 0; j < 3; j++) {
setTimeout(() => {
// console.log(j)
}, 1);
}
// 3 3 3
- this指向
const num = {
a: 10,
add() {
return this.a + 2;
},
reduce: () => this.a -2
};
console.log(num.add()); // 12
console.log(num.reduce()); // NaN
解析:对于箭头函数,this关键字指向是它所在上下文(定义时的位置)的环境,与普通函数不同! 这意味着当我们调用reduce时,它不是指向num对象,而是指其定义时的环境(window)。没有值a属性,返回undefined。
- call | bind
const person = { name: "yideng" };
function sayHi(age) {
return `${this.name} is ${age}`;
}
console.log(sayHi.call(person, 5));
console.log(sayHi.bind(person, 5));
解析:使用两者,我们可以传递我们想要this关键字引用的对象。 但是,.call方法会立即执行!.bind方法会返回函数的拷贝值,但带有绑定的上下文! 它不会立即执行。
Git 相关
-
git cherry pick:将指定的提交(commit)应用于其他分支,当只需要部分代码变动(某几个提交),可以使用 git cherry pick。
例如: 在Master上创建功能分支, 在功能分支上提交的 "feature2",应用到Master上。
git checkout Master -> git cherry pick "feature2"
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!