目录
ES6
防抖
节流
防抖、节流应用
改变this
call
typeof this !== 'function'
context = context || window
context._this = this
delete context._this
bind: return _this.apply(context, [...arguments].slice(1));
深拷贝
!arr|| arr == null || typeof arr != 'object'
arr instanceof Array ? [] : {}
for (const key in arr)
result[key] = cloneDeep(arr[key])
setTimeout()
倒计时
setTimeout模拟实现setInterval
setInterval模拟实现setTimeout
new :Fn=[...arguments].shift()
*寄生组合式继承:call,create,constructor
Object.defineProperty(obj, prop, descriptor)
Object.create
Object.freeze
Object for of
*instance of
算法
合法的URL
千位分割:num.slice(render, len).match(/\d{3}/g).join(',')
ES6
防抖
触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,会重计算函数执行时间。
function debounce(fun,time) {let flag // 定义状态return function () {clearTimeout(flag)// 在执行之前 清除 定时器的 flag 不让他执行flag = setTimeout(() => {fun.call(this,arguments)//拿到正确的this对象,即事件发生的dom}, time)}}
节流
连续触发事件但是在 n 秒中只执行一次函数。两种方式可以实现,分别是时间戳版和定时器版。
function throttle(fun, time) {let flag // 定义一个空状态return function () { // 内部函数访问外部函数形成闭包if (!flag) { // 状态为空执行flag = setTimeout(() => {fns.apply(this, arguments) // 改变this指向 把 event 事件对象传出去flag = null // 状态为空}, time)}}}
防抖、节流应用
防止某一时间频繁触发
- 防抖debounce:time内只执行一次
- search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
- window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
- 节流throttle: 间隔time执行
- 鼠标不断点击触发,mousedown(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
改变this
call
-
typeof this !== 'function'
-
context = context || window
-
context._this = this
-
delete context._this
// 给function的原型上面添加一个 _call 方法Function.prototype._call = function (context) {// 判断调用者是否是一个函数 this 就是调用者if (typeof this !== 'function') {throw new TypeError('what is to be a function')}// 如果有 context 传参就是传参者 没有就是windowcontext = context || window// 保存当前调用的函数context._this = this // 截取传过来的参数/*argumentsa: 1fn: ƒ fns()*/// 通过 slice 来截取传过来的参数const local = [...arguments].slice(1)// 传入参数调用函数let result = context._this(...local)// 删属性delete context._thisreturn result}let obj = { a: 1 }function fns(a, b) {console.log(a, b);console.log(this)}fns._call(obj, 23, 555)
bind: return _this.apply(context, [...arguments].slice(1));
深拷贝
-
!arr|| arr == null || typeof arr != 'object'
-
arr instanceof Array ? [] : {}
-
for (const key in arr)
-
result[key] = cloneDeep(arr[key])
function cloneDeep(arr = {}) {// 终止递归 if (!arr|| arr == null || typeof arr != 'object' ) return arr// 用 instanceof 判断原型链上是否有该类型的原型 是 Array => [] ! Arrays =>{}let result=arr instanceof Array ? [] : {}// forin 循环对象的key值for (const key in arr) {// 对象 key 赋值 resultresult[key] = cloneDeep(arr[key])}return result}
setTimeout()
倒计时
//返回值timeoutID是一个正整数,表示定时器的编号。
let timeoutID = scope.setTimeout(function[, delay]),//delay表示最小等待时间,真正等待时间取决于前面排队的消息
clearTimeout(timeoutID) //取消该定时器。
function timedCount() {c -=1;if(c===0){clearTimeout(t);return;}t = setTimeout(function() {timedCount()}, 1000);
}
setTimeout模拟实现setInterval
// 使用闭包实现
function mySetInterval(fn, t) {let timer = null;function interval() {fn();timer = setTimeout(interval, t);}interval();return {// cancel用来清除定时器cancel() {clearTimeout(timer);}};
}
setInterval模拟实现setTimeout
function mySetTimeout(fn, time) {let timer = setInterval(() => {clearInterval(timer);fn();}, time);
}// 使用
mySetTimeout(() => {console.log(1);
}, 2000);
new :Fn=[...arguments].shift()
"_new"函数,该函数会返回一个对象,
该对象的构造函数为函数参数、原型对象为函数参数的原型,核心步骤有:
- 创建一个新对象
- 获取函数参数
- 将新对象的原型对象和函数参数的原型连接起来
- 将新对象和参数传给构造器执行
- 如果构造器返回的不是对象,那么就返回第一个新对象
const _new = function() {const object1 = {}const Fn = [...arguments].shift()object1.__proto__ = Fn.prototypeconst object2 = Fn.apply(object1, arguments)return object2 instanceof Object ? object2 : object1
}
*寄生组合式继承:call,create,constructor
通过寄生组合式继承使"Chinese"构造函数继承于"Human"构造函数。要求如下:
1. 给"Human"构造函数的原型上添加"getName"函数,该函数返回调用该函数对象的"name"属性
2. 给"Chinese"构造函数的原型上添加"getAge"函数,该函数返回调用该函数对象的"age"属性
- 在"Human"构造函数的原型上添加"getName"函数
- 在”Chinese“构造函数中通过call函数借助”Human“的构造器来获得通用属性
- Object.create函数返回一个对象,该对象的__proto__属性为对象参数的原型。此时将”Chinese“构造函数的原型和通过Object.create返回的实例对象联系起来
- 最后修复"Chinese"构造函数的原型链,即自身的"constructor"属性需要指向自身
- 在”Chinese“构造函数的原型上添加”getAge“函数
function Human(name) {this.name = namethis.kingdom = 'animal'this.color = ['yellow', 'white', 'brown', 'black']
}
Human.prototype.getName = function() {return this.name
}function Chinese(name,age) {Human.call(this,name)//call函数借助”Human“的构造器来获得通用属性this.age = agethis.color = 'yellow'
}//返回的对象__proto__属性为对象参数的原型
Chinese.prototype = Object.create(Human.prototype)//使用现有的对象来作为新创建对象的原型
//修复"Chinese"构造函数的原型链,即自身的"constructor"属性需要指向自身
Chinese.prototype.constructor = ChineseChinese.prototype.getAge = function() {return this.age
}
Object.defineProperty(obj, prop, descriptor)
Object.create
该函数创建一个新对象,使用现有的对象来提供新创建的对象的proto,核心步骤有:
- 创建一个临时函数
- 将该临时函数的原型指向对象参数
- 返回该临时对象的实例
Object.create法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
const _objectCreate = proto => {if(typeof proto !== 'object' || proto === null) returnconst fn = function() {}fn.prototype = protoreturn new fn()
}
Object.freeze
Object.freeze = writable: false + Object.seal = writable: false + Object.preventExtensions + configable: false
- Symbol 类型作为 key 值的情况,也要冻结
- 只冻结对象自有的属性(使用 for ... in 会把原型链上的可枚举属性遍历出来)。
- 注意不可扩展性(不能添加新属性,使用 Object.preventExtensions() 或 Object.seal() 实现,同时也相当于把原型链冻结)。
key:
- Object.getOwnPropertyNames/Symbol
- forEach
- Object.defineProperty:configurable,writable
- Object.preventExtensions(object)
const _objectFreeze = object => {if(typeof object !== 'object' || object === null) {throw new TypeError(`the ${object} is not a object`)}const keys = Object.getOwnPropertyNames(object);const symbols = Object.getOwnPropertySymbols(object);[...keys, ...symbols].forEach(key => {Object.defineProperty(object, key, {configurable: false,writable: false,})})Object.preventExtensions(object)
}
Object for of
// 创建一个构造函数
function MyObject() {// 为构造函数的实例对象添加两个属性this.prop1 = 'Value 1';this.prop2 = 'Value 2';
}// 在原型上添加一个自定义的迭代器方法
MyObject.prototype[Symbol.iterator] = function () {// 获取实例对象的所有属性名并存储在数组 keys 中const keys = Object.keys(this);let index = 0;// 返回一个迭代器对象,包含 next 方法return {next: () => {// 如果还有属性未遍历完if (index < keys.length) {// 获取当前属性名 key,并递增索引 indexconst key = keys[index++];// 返回包含当前属性名和对应值的对象,并设置 done 为 false 表示未遍历完return { value: [key, this[key]], done: false };} else {// 如果所有属性都已遍历完,返回 done 为 true 表示遍历结束return { done: true };}},};
};// 创建一个实例对象
const instance = new MyObject();// 使用 for...of 遍历实例对象的属性
for (const [key, value] of instance) {// 打印属性名和对应值console.log(key, value);
}
*instance of
- 获取首个对象参数的原型对象
- 获取Fn函数的原型对象
- 进入死循环,当两个参数的原型对象相等时返回true
- 当两个参数的原型对象不相等时获取首个对象参数原型的原型并且循环该步骤直到null时返回false
const _instanceof = (target, Fn) => {let proto = target.__proto__let prototype = Fn.prototypewhile(true) {if(proto === Fn.prototype) return trueif(proto === null) return falseproto = proto.__proto__}
}const _instanceof = (target, Fn) => {return Fn.prototype.isPrototypeOf(target);}
算法
合法的URL
URL结构一般包括协议、主机名、主机端口、路径、请求信息、哈希
- 域名不区分大小写:"www"子域名(可选)、二级域名、"com"顶级域名
- 只能包含字母(a-z、A-Z)、数字(0-9)和连字符(-)(但-不能再首尾)
https://www.bilibili.com/video/BV1F54y1N74E/?spm_id_from=333.337.search-card.all.click&vd_source=6fd32175adc98c97cd87300d3aed81ea
//开始: ^
//协议: http(s)?:\/\/
//域名: [a-zA-Z0-9]+-[a-zA-Z0-9]+|[a-zA-Z0-9]+
//顶级域名 如com cn,2-6位: [a-zA-Z]{2,6}
//端口 数字: (:\d+)?
//路径 任意字符 如 /login: (\/.+)?
//哈希 ? 和 # ,如?age=1: (\?.+)?(#.+)?
//结束: $
// https:// www.bilibili com /video/BV1F54y1N74E ?spm..
/^(http(s)?:\/\/)?(([a-zA-Z0-9]+-[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)+([a-zA-Z]{2,6})(:\d+)?(\/.+)?(\?.+)?(#.+)?$/.test(url)
千位分割:num.slice(render, len).match(/\d{3}/g).join(',')
const format = (n) => {let num = n.toString() // 拿到传进来的 number 数字 进行 toStringlet len = num.length // 在拿到字符串的长度// 当传进来的结果小于 3 也就是 千位还把结果返回出去 小于3 不足以分割if (len < 3) {return num} else {let render = len % 3 //传入 number 的长度 是否能被 3 整除if (render > 0) { // 说明不是3的整数倍return num.slice(0, render) + ',' + num.slice(render, len).match(/\d{3}/g).join(',')} else {return num.slice(0, len).match(/\d{3}/g).join(',')}}}let str = format(298000)console.log(str)