JS高级部分

全局变量

变量分类:

  1. 全局变量:定义在function外部的变量。特点:不销毁
  2. 局部变量:定义在function内部的变量。特点:用完即销毁
var username = '女'	// 全局

var play () {
    var username = '女'	// 局部
}

特殊:
var play () {
    username = '女'	// 全局
}

使用场景

全局:少用,一直常驻内存中不易被销毁,容易出现命名冲突,适合公用的变量

局部:函数执行完毕即被销毁,所以无法持久化

ready == DOMContententLoaded

作用域

函数作用域:函数内部使用范围

全局作用域:整个网页范围

面试题
function fn(a) {
	console.log(a)	// {函数体}	原因:函数的优先级大于var
	var a = 20
	function a() {}
	console.log(a)	// 20
}
面试题

let块级作用域

特点:

  1. 块级作用域
  2. 暂时性死区
  3. 当全局变量使用的时候不属于window对象
面试题
<script>
    for(var i=0;i<3;i++){
        setTimeout(function(){
            alert(i)		// 4,4,4
        },0)
     }
</script>
<!-- let 每次执行的时候会生成一个副本 -->
<script>
    for(let i=0;i<3;i++){
        setTimeout(function(){
            alert(i)		// 0,1,2
        },0)
     }
</script>

不能在初始化之前去访问let修饰的变量:暂时性死区

function run(){
console.log(x);	// 报错
let x = 10;
}

用let声明的变量,不属于window(用var声明的变量属于window)

const

命名:要求全部大写,多个单词之间下划线隔开

特点:

  1. 定义的时候就需要初始化他的值,且后续无法修改
  2. 如果用const定义一个对象,那么是可以修改内部属性的
  3. 当全局变量的时候,效果和了他一样
if(1 == 1){
    const ONE = 10;
}
console.log(ONE)
    const play = { name: "张三", age: 18 };
    // play = {}; // 报错:Assignment to constant variable.
    console.log(play.name); // 张三
    // 如果const定义的是一个对象,那么是可以修改它内部属性的
    play.name = "李四";
    console.log(play.name); // 李四

IIFE(立即执行函数)

语法:(function([形参]){
	// 函数体
	[return]
})([实参])

作用:

  1. 早期模块化解决方案,避免命名冲突
  2. 防止外部代码来访问内部的变量,提高安全性

*闭包

好处:能够读取其他函数内部变量的函数

坏处:用多了后,增加了内存的消耗,容易造成内存的泄露

特点:外函数包裹内函数,而且内函数的可以访问外部函数的局部变量,闭包环境是内部函数与外部函数的桥梁

<script>
	var fn = (function(){
        var num = 10;
        return function(){
            num++;
            console.log(num)
        }
    })()
    // 在外部执行fn,不论执行多少次,num变量一直存在,从未还原过
   	// 好处:num既有了全局变量的常驻内存不销毁的特点,并且num又不会和外部变量重名
    // num一直被内部函数(fn)使用,所以他不会被垃圾回收器收走
    fn(); // 11
    fn(); // 12
    // 如和让GC回收掉num
    // 没有地方用num,自然会被垃圾回收机制收走
    fn = null;
</script>

没有任何地方使用的变量,会被垃圾回收器收走(释放内存空间)

this指向问题

this指向window
 function 函数名(){}
 var 函数名 = function(){}

this指向当前对象
 var obj = {
  	函数名:function(){}
  }

this指向按钮
 btn.onclick = function(){}

​ 面试题

独立的创建一个函数,不论写在那,都属于window
<script>
	let Obj = {
        getNameFunc:function(){
            console.log(this)	// Obj
            return function(){
                console.log(this)	// window
            }
        }
    }
</script>

arguments(参数)

含义:当前函数的实参列表

arguments.callee

含义:当前函数

call()与apply()

相同点:都是调用函数,并且动态修改函数里的this值

不同点:

​ call(新this值,参数1,参数2····)

​ apply(新this值,[参数1,参数2····])

slice截取的时候,找的是 this
	let args = [7,8,9].slice();//还是7,8,9 从原数组截取的
    let args =[7,8,9].slice.call([1,2,3]);//1,2,3,从后面这个数组截取的

函数柯里化

一个函数只接收一个参数

function sum(){
	//调用slice方法,但是把slice里面的this改成arguments,然后产生一个新数组
	let args =[].slice.call(arguments);//1,3
	function exec(){
		args.push(...arguments);// args = [1,2,3,4]
                return exec;
            }
            exec.calc=function(){
                return args.reduce(function(total,current){
                    return total+current;
                })
            }
            return exec;
        }
        // console.log(sum(1,3)(2,4)(100)(1));
        var exec1 = sum(1,3);
        var exec2 = exec1(2,4)
        var exec3 = exec2(100)
        var exec4 = exec3(1);
        console.log(exec4.calc());
我的练习
<!-- 柯里化:一个函数只接收一个参数 -->
    <script>
      let arr = [];
      function play() {
        arr.push(...arguments);
        let qq = 0;
        for (let i = 0; i < arr.length; i++) {
          qq += arr[i];
        }
        console.log(qq);
        return play;
      }
      console.log(play(3)(4));
    </script>

    <script>
      function play() {
        let arr = [].slice.call(arguments);
        function gogo() {
          arr.push(...arguments);
          let qq = 0;
          for (let i = 0; i < arr.length; i++) {
            qq += arr[i];
          }
          console.log(qq);
          return gogo;
        }
        return gogo;
      }
      console.log(play(3)(4));
    </script>

    <script>
      function play() {
        let num = arguments[0];
        function gogo() {
          console.log(arguments[0]);
          console.log(num.test(arguments[0]));
          return gogo;
        }
        return gogo;
      }
      play(/^\d/)('c23');
    </script>

匿名函数

优点:匿名函数,仅在调用时,才临时创建函数对象和作用域链对象;调用完,立即释放,所以匿名函数比非匿名函数更节省内存空间

eval

执行上下文栈

  1. 全局执行上下文
  2. 函数执行上下文
  3. eval执行上下文【很少使用】 assign:分配

执行上下文调用栈【代码的执行流程】【先进后出】

  1. 全局上下文入栈
  2. 函数上下文入栈
  3. 函数上下文出栈
  4. 全局上下文出栈

递归

概念:函数内部调用自身

递归如果不设置终点,则会死掉

    <!-- 面试题一 【阶乘】 -->
	<script>
        function play(){
        	let num = arguments[0];	// 获取传入的数字
        	if(num==1){
        	return 1;
        }
        	return play(num-1)*num;
        }
		play(10) // 乘到十
	</script>

方法二
    <!-- 递归:自己调自己 -->
    <script>
      function play(num) {
        if (num == 1) {
          return 1;
        }
        // 等于是play()在输入时执行,后面的乘法在所在函数输出的时候执行
        return play(num - 1) * num;
      }
      console.log(play(10));
    </script>
    <!-- 面试题二 【快速排序】 -->
    <!-- 1.在数据集之中,选择一个元素作为"基准"
          2.所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。
          3.对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。 -->
    <script>
      let arr = [1, 5, 3, 9, 4, 8, 0, 2, 12, 7, 2, 9];
      function my_arr(arr) {
        // 如果只剩下一个或一个都不剩直接返回(防止空转导致内容溢出)
        if (arr.length <= 1) {
          return arr;
        }
        let first_arr = arr[0]; // 获取第一个作比较
        let left_arr = []; // 左边
        let right_arr = []; // 右边
        let center_arr = [first_arr]; // 中间
        for (let i = 1; i < arr.length; i++) {
          // 如果比你大,去左边
          if (first_arr > arr[i]) {
            left_arr.push(arr[i]);
          }
          // 如果比你小,去右边
          if (first_arr < arr[i]) {
            right_arr.push(arr[i]);
          }
          // 如果相等,放中间
          if (first_arr == arr[i]) {
            center_arr.push(arr[i]);
          }
        }
        // 左边的执行完毕,才能执行右边的
        return [].concat(my_arr(left_arr), center_arr, my_arr(right_arr));
      }
      console.log(my_arr(arr));
    </script>

浅拷贝(主要针对对象)

复制出来的对象做修改时,对原对象照成影响的,都叫浅拷贝。

var a = {
	name:'rose',
	age:18,
	work:['啦','拉拉','啦啦啦']
}
var b = a; // 复制的是引用的地址【浅拷贝】
b.name='jack'
console.log(a.name); // jack

// ... 展开符只能展开一层,但是a里面还有一个对象,就是work数组
var c = {...a};
c.name = '张三';
// c和a是不同的两个对象,但是c和a他们两共用一个work
c.work[0] = '嘻嘻';
console.log(a.work[0]);	// 嘻嘻

// 情况三:通过Object.assign()修改复制的对象的对象(类似于情况二)
let x = {};
let y = { name: "李四", age: 21, arr: [1, 2, 3] };
Object.assign(x, y);
x.arr.push(...[4, 5, 6]);
console.log(x);
console.log(y);

深拷贝

    <!-- 深拷贝 -->
    <script>
      // 方法一:通过JSON的方式(函数与未定义的元素会被搞丢)
      let obj = {
        name: "张三",
        love: ["丽丽", "美美", "笑笑"],
        work: {
          k1: "卖画",
          k2: "卖唱",
        },
        fun: function () {
          console.log("起飞");
        },
        und: undefined,
      };
      let z = JSON.stringify(obj);
      let obj2 = JSON.parse(z);
      console.log(obj2);

      // 方法二:通过递归的方式一层一层的剥
      let obj3 = {};
      function play(obj, obj3) {
        // for...in...(当中遍历对象i取的就是属性名,遍历数组取的就是下标)
        for (let i in obj) {
          if (typeof obj[i] == "object") {
            let p;
            if (obj[i].length) {
              p = [];
            } else {
              p = {};
            }
            obj3[i] = play(obj[i], p);
          } else {
            obj3[i] = obj[i];
          }
        }
        return obj3;
      }
      play(obj, obj3);
    </script>

instanceof:运算符

查找

  1. 循环数组,逐个比较

  2. 数组.indexOf(XXX)

  3. 二分查找法(折半查找法):前提是有序数组

    <script>
    	var arr = [...]; // 由小到大
        //获取他的中间的值
        let arr_start = 0; // 获得开始的坐标
        let arr_post = arr.length-1; // 获取结束的坐标
        let arr_midden = parseInt(arr_post/2);	// 取得中间的下标
        function play(arr,num){	// 输入形参
            while(arr[arr_midden] != num){
                if(arr[arr_midden] > num){ // 当中位数大于参数2时
                    arr_post=arr_midden-1
                }
                if(arr[arr_midden] < num){ // 当中位数小于参数2时
                    arr_start=arr_midden+1
                }
                arr_midden=parseInt((arr_post + arr_start)/2)
            }
            return arr_midden
        }
        play(arr,10)
    </script>
    

性能优化

  1. onscroll
函数防抖
<script>
    let ms = null;
	window.onscroll = play;	// 当全局的滚动条移动时,触发play属性。(思考:如果调用的不是play对象而是play对象的返回值,那么值就会在开始确认下来,并且再也不会变化。)
    function play(){
		clearTimeout(ms);	// 滚动条滚动的时候不停的触发关闭定时器事件
         ms = setTimeout(function(){
             console.log(document.documentElement.scrollTop || document.body.scrollTop)
         },1);	// 当滚动条在该位置路过200ms后触发,但是由于上方有关闭定时器,所以只有滚动条停止后才触发
    };
</script>
函数防抖-优化
<script>
	window.onscroll = play();
     function play(){
		clearTimeout(ms);	// 滚动条滚动的时候不停的触发关闭定时器事件
         ms = setTimeout(function(){
             console.log(document.documentElement.scrollTop || document.body.scrollTop)
         },1);	// 当滚动条在该位置路过200ms后触发,但是由于上方有关闭定时器,所以只有滚动条停止后才触发
    };
</script>

函数节流
<script>
      let old_date = Date.now(); // 获取程序运行时的时间
      window.onscroll = play;
      function play() {
        let new_date = Date.now(); // 执行这句程序时的时间
        if (new_date - old_date >= 400) {
          console.log(
            document.documentElement.scrollTop || document.body.scrollTop
          );
          old_date = Date.now(); // 更新程序运行时的时间
        }
      }
</script>

函数节流-优化
<script>
	window.onscroll = play();
    function play(){
    	let lod_date = Date.now()	// 获取程序运行时的时间
        return function(){
            let new_date = Date.now();	// 执行这句程序时的时间
            if((new_date - old_date) == 400){
                console.log(document.documentElement.scrollTop || document.body.scrollTop);
          		old_date = Date.now(); // 更新程序运行时的时间
            }
        }
    }
</script>

seo优化

websoced

性能优化

图表

vue新特性

响应式原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值