目录

高级函数
回调函数
概念:将函数作为参数传递给另一个函数调用的函数(等某个动作完成后调用的函数),是解决异步操作的有效途径
字面上的理解,回调函数就是传递一个参数化的函数,就是将这个函数作为一个参数传到另一个主函数里面,当那一个主函数执行完之后,再执行传进去的作为参数的函数。走这个过程的参数化的函数 就叫做回调函数。换个说法也就是被作为参数传递到另一个函数(主函数)的那个函数就叫做 回调函数。
栈区
主栈任务队列 和 等待任务队列
-
同步:javaScript中先执行主栈任务队列中的代码 (除了以下几个异步的代码都放到主栈任务队列执行)
-
异步:等主栈任务队列中的代码执行完毕,再去执行等待任务队列中的代码
异步:事件,定时器, (回调函数),Ajax请求
回调函数理解
function title(value){//这是回调函数!!!!
alert(value);
}
function main(title, value){//这个主函数:在参数列表中,title作为一个参数传递进来,也就是上文说的 参数化函数;然后value这个值正是title()函数中所需要的。
alert("我是主函数");
title(value);//结果为:'我是回调函数'。——————然后在这行这个title(),它就是回调函数咯。
}
main(title,"我是回调函数");//title参数加上()后,就会变回一个函数,并会被执行一次。
//PS:看清楚,调用的是main()函数,意味着先执行main(),这时已经执行了主函数,title()被main()在函数体中执行了一次,因此title()是回调函数。
<button onClick=test()>click me</button>
function a(callback) {
alert("我是parent函数a!");
alert("调用回调函数");
callback();
}
function b() {
alert("我是回调函数b");
}
function c() {
alert("我是回调函数c");
}
function test() {
a(b);
a(c);
}
自执行函数
-
自执行函数 自己调用自己 立即执行
-
标准语法:;(function(){做的事情})();
-
;->可以有可以没有,防止前边有未执行完毕的代码
-
;(function(){
console.log("哈哈");
})();
~function(){
console.log("111");
}();
!function(){
console.log("222");
}();
+function(){
console.log("333");
}();
-function(){
console.log("444");
}();
函数闭包
-
闭包的概念:一个函数嵌套另一个函数,子函数可以访问当前这个函数的局部变量,在本质上,闭包是将函数内部和函数外部连接起来的桥梁
-
闭包的优点和缺点:优点缓存数据,延长作用域链,优点即缺点,使用闭包需谨慎,可能导致内存泄漏
-
闭包的本质是形成了一个不销毁的私有作用域
全局作用域下的变量或函数只有浏览器关闭才会销毁
函数执行形成私有作用域,执行完毕自动销毁
// 闭包
function test() {
var n = 1;
function test1() {
n++;
console.log(n);
}
return test1;
}
var resFn = test();
resFn();//2
resFn();//3
resFn();//4
resFn();//5
闭包模拟随机数
function test() {
var random = Math.random();
return function () {
console.log(random);
};
}
var resFn = test();
resFn();
resFn();
resFn();
闭包模拟点赞
var spans = document.querySelectorAll("span");
// 利用自定义属性
// for(var i = 0;i<spans.length;i++){
// spans[i].index = 0;
// spans[i].onclick = function(){
// this.index = this.index + 1;
// this.innerHTML = "赞:"+this.index;
// }
// }
// 闭包
// zan这个函数形成了三个不销毁的私有作用域
for (var i = 0; i < spans.length; i++) {
// 赋值的是zan这个函数执行完成的返回值
spans[i].onclick = zan();
}
function zan() {
var n = 0;
return function () {
n++;
this.innerHTML = "赞:" + n;
}
}
闭包模拟私有属性
// 闭包模拟私有属性
function myUtils() {
var a = 100;
function test1() {
console.log(a);
}
function test2() {
console.log("haha");
}
function test3() {
console.log("hehe");
}
return {
fn1:test1,
fn2:test2,
fn3:test3
}
}
var utils = myUtils();
console.log(utils);//{fn1: ƒ, fn2: ƒ, fn3: ƒ}
utils.fn1();//100
utils.fn2();//haha
utils.fn3();//hehe
递归函数
递归就是自己调用自己
-
递去:自己调用自己的过程在递去的过程中形成的私有作用域不销毁
-
归来:在归来过程中将值返回,然后这个私有作用域才销毁
function sum(n) {
if (n == 1) { //递归结束的条件 没有结束条件报错 RangeError: Maximum call stack size exceeded
return 1;
}
return n + sum(n - 1);
//3 + sum(2)
//3 + 2 + sum(1);
//3 + 2 + 1 =>6
}
var res = sum(3);
console.log(res);
递归求 斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列 1、1、2、3、5、8、13、21、34、 在数学上,斐波那契数列以如下被以递推的方法定义: F(n)=F(n - 1)+F(n - 2)
// 求第n项的值
function fn(n){
if(n == 1 || n == 2){
return 1;
}
return fn(n-1) + fn(n-2);
}
var res = fn(6);
/*
fn(6);
fn(5) + fn(4)
fn(4) + fn(3) + fn(3) + fn(2)
fn(3) + fn(2) + fn(2) + fn(1) + fn(2) + fn(1) + fn(2)
fn(2) + fn(1) + fn(2) + fn(2) + fn(1) + fn(2) + fn(1) + fn(2)
=> 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 => 8
*/
console.log(res);
快速排序
var arr1 = [12, 13, 23, 14, 20, 26, 34, 13, 16];
// 快速排序
// 思想:取出当前数组中的中间项和其它的每一项进行比较,比中间项小的放到左边,大的放到右边(左边和右边是指一个数组)
function quickSort(arr) {
if(arr.length <=1){
return arr;
}
// 获取中间索引
var pointIndex = Math.floor(arr.length / 2);
// 获取中间项
var pointVal = arr.splice(pointIndex, 1)[0];
// 和其它的每一项进行比较,比中间项小的放到左边,大的放到右边(左边和右边是指一个数组)
var left = [],right = [];
for(var i = 0;i<arr.length;i++){
var curVal = arr[i];
curVal < pointVal ? left.push(curVal):right.push(curVal);
}
return quickSort(left).concat(pointVal,quickSort(right));
}
var res = quickSort(arr1);
console.log(res);
//快速排序原数组会少一项
console.log(arr1);
防抖和节流
在开发过程中我们经常绑定高频发事件,例如resize,scroll,mousemove,..我们可能不想让对应的事件处理函数实时触发,这个我们就要用到防抖和节流,防抖和节流都是用来做性能优化的。
防抖
在一个事件发生一定时间之后,才执行特定动作(这个动作完成,一定时间后才执行一次)。
-
搜索框搜索输入。只需用户最后一次输入完,再发送请求;
-
浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。
-
...
function debounce(fun, awit) {
var timer;
return function () {
if (timer) { //看当前是否有定时器
// 清楚定时器
clearTimeout(timer);
}
timer = setTimeout(fun, awit);
}
}
window.onresize = debounce(getWin, 3000);
// 获取窗口大小的函数
function getWin() {
var winW = document.documentElement.clientWidth;
var winH = document.documentElement.clientHeight;
console.log(winW, winH);
}
节流
在一定时间之内,限制一个动作只执行一次 。 (过安检,30s,检查一个人)
-
监听滚动条位置;
-
防止高频点击提交,防止表单重复提交;
-
...
window.onscroll = throttle(getScroll, 3000);
// 闭包
function throttle(fun, awit) {
var timer;
return function () {
// 判断
if (!timer) { //判断没有定时器就设置一个
timer = setTimeout(function () {
getScroll();
timer = null;
}, 3000);
}
}
}
// 获取滚动距离
function getScroll() {
var scrollT = document.documentElement.scrollTop || document.body.scrollTop;
console.log(scrollT);
}
防抖和节流的区别
-
共同点:防抖和节流都要使用延迟定时器去完成,有利于性能优化
-
不同点:防抖是这个动作(事件触发)完成,一定时间后才执行一次,节流,是多长事件就去做一次这个事件
call和apply
call和apply都是用来改变this指向的
-
函数.call(_this,实参1,实参2,...);
-
作用:首先将这个函数执行,然后把里边的thi变为第一个参数
-
_this:将这个函数中this变为谁第一个参数就传递谁
-
实参:就是传递函数的实参
-
-
函数.apply(_this,[实参1,实参2,...]);
-
作用:首先将这个函数执行,然后把里边的thi变为第一个参数
-
_this:将这个函数中this变为谁第一个参数就传递谁
-
[实参1,实参2,...] 就是传递函数的实参
-
function test(a, b) {
console.log(this);
console.log(a, b);
}
test.call([10, 20, 30], 10000, 20000);
test.call({ name: "哈哈" });
test.call(1);
test.call("呵呵", "hello", "wolrld");
test.apply(true);
test.apply(100, [100, 200]);
应用
/*
求数组中最大值
*/
var arr = [20,158,100,21,200,88,66];
// var maxVal = arr[0];
// for(var i = 0;i<arr.length;i++){
// if(maxVal < arr[i]){
// maxVal = arr[i];
// }
// }
// console.log(maxVal);
var res = Math.max.apply(Math,arr);
console.log(res);
DOM回流
分类
DOM回流可以分为重排 和 重绘
重排 和 重绘:页面重新渲染 ,重排->页面整体重新渲染 重绘->局部渲染
重排:HTML结构的width,height,位置,display:none,结构的操作(添加,删除,替换,...)都会引起重排
重绘:背景颜色,文本颜色样式发生改变是局部渲染
DOM回流只能尽量减少,不能避免
DOM回流的数据绑定方式
字符串拼接
优点:引发一次DOM回流
缺点:innerHTML在进行字符串解析的时候,如果说字符串过长,解释时间导致过长,可能 出现页面空白或卡顿
// 获取元素
var ul = document.querySelector("ul");
var str = '';
// 字符串拼接
for(var i = 0;i<arr.length;i++){
str += '<li>'+arr[i].name+'</li>';
}
console.log(str);
ul.innerHTML = str;
动态添加
优点:不会出现页面空白或卡顿由于字符串解析造成的这个问题
缺点:发生多次DOM回流
// 获取元素
var ul = document.querySelector("ul");
for(var i = 0;i<arr.length;i++){
// 先创建li
var li = document.createElement("li");
li.innerHTML = arr[i].name;
// 添加到ul中
ul.appendChild(li);
}
利用文档碎片
// 综合的方案
var ul = document.querySelector("ul");
// 文档碎片 相当于一个临时的容器
var frg = document.createDocumentFragment();
console.log(frg);
for (var i = 0; i < arr.length; i++) {
// 先创建li
var li = document.createElement("li");
li.innerHTML = arr[i].name;
// 添加到ul中
frg.appendChild(li);
}
ul.appendChild(frg);
// 手动释放
frg = null;
console.log("getComputedStyle" in window);
// IE8及以下 结果是false
// var width = window.getComputedStyle(document.body).width;
// console.log(width);