昆明 网站 制作,网站开发的研究现状,网络推广培训平台,做网站的人叫什么软件1、bind函数的实现过程
// 简化实现#xff0c;完整版实现中的第 2 步
Function.prototype.bind function (context) {var self this;// 第 1 个参数是指定的 this#xff0c;截取保存第 1 个之后的参数// arr.slice(begin); 即 [begin, end]var args Array.prototype.sl…1、bind函数的实现过程
// 简化实现完整版实现中的第 2 步
Function.prototype.bind function (context) {var self this;// 第 1 个参数是指定的 this截取保存第 1 个之后的参数// arr.slice(begin); 即 [begin, end]var args Array.prototype.slice.call(arguments, 1); return function () {// 此时的 arguments 是指 bind 返回的函数调用时接收的参数// 即 return function 的参数和上面那个不同// 类数组转成数组var bindArgs Array.prototype.slice.call(arguments);// 执行函数return self.apply( context, args.concat(bindArgs) );}
} 2、高阶函数、函数柯里化
简单来说高阶函数是一个接受函数作为参数传递或者将函数作为返回值输出的函数。
一个经常用到的柯里化函数 var curry function (fn) {var args [].slice.call(arguments, 1);return function() {var newArgs args.concat([].slice.call(arguments));return fn.apply(this, newArgs);};
}; 3、节流
函数节流指的是在一定的时间间隔内比如说3秒只执行一次在这三秒内无视后来产生的函数调用请求也不会延长时间间隔。3秒间隔结束后第一遇到新的函数调用会触发执行然后在这新的3秒内依旧无视后来产生的函数调用请求以此类推。
应用场景非常适用于函数被频繁调用的场景例如window.onresize事件mousemove事件上传进度等等。实现方案一用时间戳来实现
// fn 是需要执行的函数
// wait 是时间间隔
const throttle (fn, wait 50) {// 上一次执行 fn 的时间let previous 0// 将 throttle 处理结果当作函数返回return function(...args) {// 获取当前时间转换成时间戳单位毫秒let now new Date()// 将当前时间和上一次执行函数的时间进行对比// 大于等待时间就把 previous 设置为当前时间并执行函数 fnif (now - previous wait) {previous nowfn.apply(this, args)}}
}// DEMO
// 执行 throttle 函数返回新函数
const betterFn throttle(() console.log(fn 函数执行了), 1000)
// 每 10 毫秒执行一次 betterFn 函数但是只有时间差大于 1000 时才会执行 fn
setInterval(betterFn, 10)
实现方案二用定时器来实现 //原始函数function add(a:any, b: any) {console.log(a b, ****本身调用, new Date());return a b;};//节流函数const throttle (fn: any, wait 50) {let timer: any null;return (...args: any) {if (!timer) {timer setTimeout(() {fn.apply(this, [...args])timer null;}, wait)}}}// betterFn就是节流之后返回的新函数const betterFn throttle(add, 2000);setInterval(() {// 如果在此处直接调用add会出新add频繁触发的情况加上throttle之后明显情况好多了betterFn(1, 1)}, 200)4、防抖
防抖函数debounce指的是某个 函数在某段时间内无论触发了多少次回调都只执行最后一次。假如我们设置了一个等待时间3秒的函数在这3秒内如果遇到函数调用请求就重新计时3秒直至新的3秒内没有函数请求此时执行函数不然就以此类推重新计时。 //防抖const debounce (fn: any, wait 50) {let timer: any null;return (...args: any) {if (timer) {clearTimeout(timer)}timer setTimeout(() {fn.apply(this, args);}, wait);};};// betterFn就是防抖之后返回的新函数const betterFn debounce(() console.log(****执行了, new Date()), 2000);document.addEventListener(resize, betterFn)5、null和undefined的本质区别是什么
(1)从类型来说
null的数据类型是objecttypeof null object会被隐式转换成0不容易发现错误undefined是一个基本数据类型转换成数值为NaN
(2)从变量赋值来说
给一个全局变量赋值为null相当于将这个变量的指针对象以及值清空如果给对象的属性赋值为null或者局部变量赋值为null相当于给这个属性分配了一块空的内存然后值为nullJS会回收全局变量为null的对象给一个全局变量赋值为undefined相当于将这个对象的值清空但是这个对象依旧存在如果给对象的属性赋值为undefined说明这个值为空
总结
null已经声明赋值为空引用数据类型数值转换成0。
undefined已经声明未赋值基本数据类型数值转换成NaN。 6、ES6语法中的const声明一个只读的常量那为什么可以修改const的值
const foo {};// 为 foo 添加一个属性可以成功
foo.prop 123;
foo.prop // 123// 将 foo 指向另一个对象就会报错
foo { name: 123 }; // TypeError: foo is read-only
解答
const实际上保证的并不是变量的值不得改动而是变量指向的那个内存地址所保存的数据不得改动。
对于简单类型的数据numberstringboolean值就是保存在变量指向的那个内存地址因此等同于常量。
但是对于复合数据类型的数据主要是对象和数组变量指向的内存地址保存的只是一个指向实际数据的指针const只能保证这个指针是固定的即总是指向另一个固定的地址至于它指向的数据结构是不是可变的就完全不能控制了。
因此将一个对象声明为常量必须非常小心。 7、JS数据类型
(1)分为两大类基本数据类型和引用数据类型
基本数据类型NumberStringBooleanNullUndefinedSymbolBigInt引用数据类型ObjectjsonArrayfunction
(2)两种数据类型存放机制
基本数据类型体积小放在栈内存里面引用数据类型体积大放在堆内存里面引用数据类型会有一个指针放在栈内存里面用来定位堆里面存放的引用类型的数据
(3)如何判断这两种数据类型
typeof 用来查找基本数据类型引用类型除了function外其他的都为objectinstanceof 用来查找引用数据类型原理instanceof在查找的时候会遍历左边变量的原型链直到找到右边变量的prototype找得到就返回true找不到就返回falseObject.prototype.toString().call() 所有数据类型原型链和原型有关首先toString()这个方法本来应该返回string的字符串形式但是大多数情况会返回[object,****]这种形式因为js重写了某些数据类型的toString方法 const var1 typeof 123;const var2 typeof 哈哈哈;const var3 typeof true;console.log(var1); // numberconsole.log(var2); // stringconsole.log(var3); // booleanconst date1 new Date()const var4 date1 instanceof Date;const var5 date1 instanceof Array;console.log(var4); // trueconsole.log(var5); // falseconst arr1 [1, 2];const obj1 { name: 张三 };const fn1 () {};const str1 I am a string;const var6 Object.prototype.toString.call(arr1);const var7 Object.prototype.toString.call(obj1);const var8 Object.prototype.toString.call(fn1);const var9 Object.prototype.toString.call(str1);console.log(var6); // [object Array]console.log(var7); // [object Object]console.log(var8); // [object Function]console.log(var9); // [object String](4)数据类型如何转换
转换为字符串toString()/String()转换为数字类型Number()/ParseInt()/ParseFloat()转换为布尔值Boolean()/在实际的开发中也会调用双感叹号强制转换成布尔类型隐式转换js是一门弱类型语言运行期间会根据运行环境自动类型转换 const var1 String(123);const var2 !!哈哈哈;const var3 Number(true);const var4 人民 899;console.log(var1); // 123console.log(var2); // trueconsole.log(var3); // 1console.log(var4); // 人民8998、JS的事件循环(Event Loop)
(1)首先得解释一下单线程
js是单线程语言用途决定了它必须是单线程语言单线程指同一时间只能做一件事。
(2)然后解释一下代码执行流程
同步任务执行完毕再去执行异步任务异步任务分宏任务和微任务如此循环往复就构成了事件循环
(3)解释一下事件循环
常见的宏任务setTimeoutsetIntervalI/O操作UI渲染等等
常见的微任务Promise.then(回调)MutationObserver等等
执行顺序
先执行同步任务、再执行微任务、最后执行宏任务(宏任务里面还有微任务先执行微任务)同步任务、微任务、宏任务同步任务、微任务、宏任务......如此循环往复.....
记住要执行宏任务的前提是清空了所有微任务 function test () {console.log(start)setTimeout(() {console.log(children2)Promise.resolve().then(() {console.log(children2-1)})}, 0)setTimeout(() {console.log(children3)Promise.resolve().then(() {console.log(children3-1)})}, 0)Promise.resolve().then(() {console.log(children1)})console.log(end)}test()/*
以上代码在浏览器的执行顺序是
* start
* end
* children1
* children2
* children2-1
* children3
* children3-1
* */9、讲一下前端的深拷贝和浅拷贝
因为js的数据类型分两大类基本数据类型和引用数据类型在进行变量赋值的时候分为下面两部分
基本数据类型赋值赋值之后两个变量互不影响引用数据类型赋址两个变量具有相同的引用指向同一个对象相互之间有影响
其实简单来说浅拷贝就是把变量A的指针赋值给了变量B变量A和变量B本质上指向的是同一块内存而深拷贝是在内存中重新开辟出来一块新的空间给变量B。
看代码比较直观如下所示 let a Jesus Loves You;let b a;console.log(b);// Jesus Loves Youa God Loves Youconsole.log(a);// God Loves Youconsole.log(b);// Jesus Loves You/*从上面的变动中可以看出a的改动并没有影响到b因为a为基本数据类型*/在来看另外一组代码如下所示 let a {name: Holy Bible,book: {title: God Loves Everyone,price: 45,},}let b a;console.log(JSON.stringify(b));// {name:Holy Bible,book:{title:God Loves Everyone,price:45}}a.name Other Book;a.book.price 55;console.log(JSON.stringify(a));// {name:Other Book,book:{title:God Loves Everyone,price:55}}console.log(JSON.stringify(b));// {name:Other Book,book:{title:God Loves Everyone,price:55}}/*从上面的变动中可以看出a的改动影响到了b因为a为引用数据类型*/(1)常用的浅拷贝方式
Object.assign()展开语法SpreadArray.prototype.slice()
(2)常用的深拷贝方式
JSON.parse(JSON.stringify(object))
这个深拷贝的方式有bug不建议在日常的开发中使用会有以下几个问题
会忽略undefined会忽略symbol不能序列化函数不能解决循环引用的对象不能正确处理new Date()不能处理正则 10、讲一下JS的原型链