- 封装Lodash库里的get方法
- 防抖函数vs节流函数
- 封装call
- 简单的三种继承方式
- 快速排序
封装lodash的get方法
// 给定一个对象和一个路径,得到路径对应的值
// 输入输出样例
// 样例一
// 输入:get({a: 1}, 'a')
// 输出:1
// 样例二
// 输入:get({a: [1, {b: 2}]}, 'a[1].b')
// 输出:2
// 'a.b.c.b.c.d'
function get(obj, param) {
eval(`const x = ${JSON.stringify(obj)}; this.ret = x.${param}`);
return this.ret;
}
eval()函数的使用:
防抖函数
- 防抖函数与节流函数的区别及使用场景
- 防抖函数的实现为什么用了柯里化和闭包
防抖类似电梯关门,每10s钟关一次门,当10s内又有人按开门,则关门的10s清零重新计算,所以当10s总是有人按开门,电梯门永远不能成功关闭,所以说防抖函数设置的回调执行的时间应小于时间执行的频率。
防抖函数使用场景如:
- 表单提交
- 对于输入框连续输入进行AJAX验证时,用函数防抖能有效减少请求次数
- 判断scroll是否滑到底部,滚动事件+函数防抖
防抖函数的实现思想,实际上就是将多次触发的时间延迟到一定时间之后执行。既定时调用回调函数。
function debounce(fn, wait) {
var timer = null;
return function () {
var args = arguments;
var context = this;
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(() => {fn.apply(context, args)}, wait);
}
}
节流函数:上一次执行时间和此次执行时间差值小于规定时间,则不执行。
function throttle(fn, time) {
let _lastTime = null;
return () => {
const _nowTime = + new Date();
if (_nowTime - _lastTime > time || !time) {
fn();
_lastTime = _nowTime;
}
}
}
fn() {
console.log('防抖');
}
//调用
setInterval(throttle(fn, 1000), 10);
对比总结一下防抖函数和节流函数的区别:
- 防抖:可以理解为一定时间内触发多次事件,合并成一次执行。要注意的就是,当回调函数触发频率高于延迟时间,防抖函数则一直不能被触发。
- 节流:可以理解为缩小事件的执行频率。
封装一个call:call是用来改变this指向的。
思想:给foo对象增加一个属性,属性的方法是调用的函数。这个时候函数的this就指向了想指向的对象上。但是这样却给 foo 对象本身添加了一个属性,所以需要用 delete 再删除它。
所以我们模拟的步骤可以分为:
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
//封装思想:
function bar () {
console.log(this.value);
}
var foo = {
value: 'hyt'
}
bar.call(foo);
//实现:
var testObj = {
value: 'wl',
testFn: function () {
console.log(this.value);
}
}
testObj.testFn(); // wl
delete testObj.testFn;
console.log(testObj);
//封装成品:
Function.prototype.call2 = function (context) {
context.fn = this; //this是调用call的函数,既bar();
context.fn();
//考虑不能丢失bar函数本身传进来得参数;记得要从arguments数组的第二个位置取值哦
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
var result = eval('context.fn(' + args + ')');
delete context.fn;
return result;
}
bar.call2(foo);
//封装apply,这个是张yixiong的方法,我看着有点绕,不过比我写的高级一点:
Function.prototype.apply = function (context, args = []) {
const target = Object(context);
const thisSymbol = Symbol();
target[thisSymbol] = this;
const result = target[thisSymbol](...args);
delete target[thisSymbol];
return result;
};
这里有加入arguments对象的处理主要是考虑到bar函数传入得其他参数,现在简单介绍一下arguments对象是干什么的。
arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。
例如,如果一个函数传递了三个参数,你可以以如下方式引用他们:
arguments[0],arguments[1],arguments[2]…
function test () {
var len = arguments.length;
var sum = 0;
for (var i = 0; i < len; i++) {
sum += arguments[i];
}
return sum;
}
test(1,2,3,4);//10
值得注意的是,arguments是一个伪数组,只具有length属性,和索引元素外没有任何数组的属性,如map、reduce、pop等方法,并且在箭头函数中不能使用arguments。
继承
构造继承:
function Animal(name) {
this.name = name;
this.sleep = () => {
console.log('睡觉');
}
}
Animal.prototype.eat = food => {
console.log('eat' + food);
}
function Cat() {
Animal.call(this);
this.name = 'kavin';
}
var cat = new Cat();
console.log(cat instanceof Animal);
console.log(cat instanceof Cat);
console.log(cat.name);
console.log(cat.eat());
console.log(cat.sleep());
原型继承:父类的实例是子类的原型
function Animal(name) {
this.name = name;
this.sleep = () => {
console.log('睡觉');
}
}
Animal.prototype.eat = food => {
console.log('eat' + food);
}
function Cat () {
}
Cat.prototype = new Animal();
var cat = new Cat();
console.log(cat.sleep());
console.log(cat.eat());
实例继承:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型);
function Cat() {
var instance = new Animal();
return instance;
}
var cat = new Cat();
console.log(cat instance Animal);
console.log(cat instance Cat);
快速排序
var quicksort = function(arr) {
if (arr.length <= 1) {
return arr;
}
var middle = Math.floor(arr.length / 2);
var midNum = arr.splice(middle, 1)[0];
var left = [];
var right = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < midNum) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quicksort(left).concat([midNum], quicksort(right));
}
快排思想:
1. 选出数组中间为数作为基准。
2. 生成左右两个指针,循环遍历数组,比基准大的数放在左边,反之放右边。
3. 利用递归当指针数组长度等于1时,返回新数组。
slice(start, end),截取数组中的一段; splice(index, howmany, ''); 方法向/从数组中index的位置开始添加/删除homany个项目,然后返回被删除的项目。这两个数组的方法不要混淆哦!
心得:
在实现快排、斐波那契数、数组扁平化等场景中都会用到递归算法,我将抽时间整理出用到递归算法的各种场景,以便补充我对递归算法的熟悉程度。
最近在复习一些js的基础知识,比如原型链、 闭包、this指向、执行上下文,以及一些常用功能函数,比如防抖节流、递归、归并排序我发现我想问题都不是很清楚,思考了原因大概是上学的时候没好好学数学,导致想问题很慢,时常感觉自己不如别人聪明。
所以我决定未来花时间恶补大学欠下的数学知识和算法,追上别的同学。
对了,发现FF同学基本功很扎实,她很踏实的看完了很多js相关书籍,我觉得我也要克服一下浮躁懒惰的问题,开始深入看一些专业书籍。并且养成深入思考的习惯。
懒于思考,会花费我更多时间,只考死记硬背记住一些答案,却在一段时间不用后,又会忘掉,我觉得我要克服的问题还挺多的。
- 养成深入阅读技术书的习惯
- 善于找到好的知识资源
- 勤于思考
- 归纳总结