手写系列
手写系列
1. 数据类型判断
typeof()不能准确判断数据类型 比如Array、Date
思路:使用Object的原型的toString方法得到数据类型,然后通过slice和tolowercase去掉多余的部分整理成都是小写的形式。
subString也可以用,但是subString不能识别-1,所以只能通过求长度的方法得到右边的”]”索引,另外subStr即将被弃用。
1 | let typeOf = function(obj){Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()} |
1 | (function(){ |
2. 继承
- 原型链继承
(实现:实例属性定义在父类构造函数中,方法定义在父类原型中;子类原型指向父类的实例
缺点:原型中的引用类型实例属性会被所有子类实例共享,基本类型不会是因为复制的是值,而引用类型被复制的是地址。) - 借用构造函数继承
(实现:实例属性和方法都定义在父类构造函数中;子类原型指向父类实例
缺点:每次创建子类实例都会导致方法被创建一次。
但是解决了原型链继承的父类实例引用类型被子类实例共享的问题以及传参问题,传参问题?) - 组合继承
(以上两种方法的结合体,原型链继承是方法写在父类原型中,借用构造函数是借用父类的构造函数作为函数使用。原型链继承只用了原型链,借用构造函数只借用了构造函数。而在组合继承中将两种方法结合起来,给父类原型添加方法来继承方法,借用构造函数来继承实例属性。
那么给父类赋予属性是什么继承??和原型链继承效果一样而且使得父类原型多了多余的属性。
所以原型链继承没有将实例属性也写到父类实例中,因为效果相同,反而改变父类实例更多。
原型链继承可以将属性和方法全都写入父类原型,但是发现属性写到构造函数效果相同,所以写到了构造函数,但是写到构造函数中还是不能使得每个子类实例获得独立的引用变量,于是子类构造函数需要借用父类构造函数声明和父类一样的子类实例属性;而借用构造函数继承会使得方法复制多份,所以将方法写到父类原型中。
缺点:调用两次父类构造函数) - 原型式继承
(无实例属性的临时构造函数根据父类原型创造一个无实例属性的实例作为子类原型) - 寄生
(原型式继承中得到的父类实例基础上给作为子类原型的无实例属性父类实例增加属性,然后将这个增强的父类实例作为子类原型) - 寄生组合继承
(为解决组合继承每次实例化子类时调用两次父类构造函数的问题,使用寄生组合继承。
寄生组合继承通过借用构造函数继承实例属性,通过原型链连接增强的父类原型继承方法)
1 | // 继承想要做的是继承父类的属性和方法 不只方法,属性也要继承 只是原型链的继承方式继承属性有瑕疵 |
- 数组去重
1 | // ES5 indexOf()方法返回第一次出现元素的位置,filter使得只有索引值为第一个索引值的同值元素才能输出 即同一个值只能输出一次 也就是说只有第一次出现的值才能够输出 如果item的index和indexOf(item)的值不一样,说明这个值不是第一次出现,返回false,则不会输出 |
- 数组扁平化
1 | [1, [2, [3]]].flat(2) // [1, 2, 3] 原生方法 |
字符串模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function render(template, data) {
const reg = /\{\{(\w+)\}\}/
if (reg.test(template)) {
const prop = reg.exec(template)[1]; // 查找模板中第一个变量
template = template.replace(reg, data[prop]); // 用data对象中对应的变量替换模板的{{prop}}
return render(template, data); // 两个动作:1. 调用自己递归继续渲染模板 2. 返回渲染后的结果(这个必须返回) 同时,这一行不会同时执行,而是会先执行render函数,层层递归,直到最后一个render有了返回值,然后从深层依次往浅层return。 最先从这里声明return,所以最后在这里return最后的结果。
}
return template; // 注意:!! 这个返回的template不会直接给控制台,而是给调用它的函数,最后一次render调用了它。所以它会返回给render
}
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let person = {
name: '布兰',
age: 12
}
render(template, person); // 我是布兰,年龄12,性别undefined图片懒加载
img.getBoundingClientRect()
img.dataset.src
- 防抖
高频事件 触发N秒后执行一次 N秒钟内再次触发则重新计时
1 | function debounce(func, wait) { |
有即时执行功能的防抖
一开始触发一次,N秒后不再触发,否则会导致点击一次触发两次。
重新计时有两步:1. 停掉之前的 2. 设置新开的
即时执行:一开始就触发,之后的N秒内点击不能触发,重新计算不能触发的时间
从n秒后执行到n秒后才能执行。
设置setTimeout的内容为n秒后将timeout置为null,唯有时间才能将timeout值为null,所以设置timeout值为null时能够触发函数,当计时器不为null时,说明计时器还没有计时结束,此时不能触发函数。
clearTimeout只是将计时器暂停了,计时器还存在,timout变量还存有计时器的id。
timeout有值不执行,为null则执行,每当点击一次,则计时器重新计时,timeout变成null的事件就往后延迟,从而不能再次使事件发生。
1 | function debounce(fn, delay, immediate = true) { |
- 节流
1 | // timer = null为初始化,函数执行后返回一个闭包函数,当timer为null时执行if里面的函数给timer赋一个计时器并执行一次函数,赋值之后if里面的语句将不能被执行,这个计时器在delay时间后将timer变为null以使得可以再次触发if里面的函数,来达到节流的效果。 |
ref
传参问题
意思是每个子类实例不能传参,而给作为子类原型的父类实例传参又不能保证每个子类实例有独立的实例属性(引用类型的情况)
- Post title:手写系列
- Post author:Willem Zhang
- Create time:2021-11-21 22:42:23
- Post link:https://ataraxia.top/2021/11/21/手写系列/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
Comments