JavaScript进阶: 1.1 this指针/闭包/作用域

知识体系

在这里插入图片描述

核心知识点

作用域链

上下文content

作用域链

  • 演示面试题

    // 面试题
    let a = 'global';
    function course() {
      let b = 'zhawa'
      session()
      function session() {
        let c = 'session';
        teacher()
        // console.log('d', d);
        function teacher() {
          // let d = 'yy'; // 块作用域
          console.log('d-yy', d); //变量提升-提升范围:当前作用域
          var d = 'yy';
          console.log('d', d); //作用域生效
          console.log('b', b);  
          console.log('a', a); //作用域向上查找
        }
      }
    }
    course();
    
  • 提升优先级的问题

    console.log(xxx);
    var xxx = 'xxxx'
    function xxx(){
      console.log('xxxx-1')
    }
    

    结论:

    变量先被提升,提升到前面,函数再被提升,提升到变量后面,打印后面覆盖前面

  • 函数是天然的隔离方案—模块化

    console.log(t) //仍可以访问t
    if(true) {
      // 没形成作用域
      var t = 33;
      // 要形成,用let,const形成块级作用域
    }
    

    作用域链结论:

    1.对于作用域链,我们可以直接通过创建态去定位链条中的某一环 ----上下文

    2.手动取消链条环或者作用域的时候,可以通过块级作用域做性能优化----性能优化

上下文content

this是在执行时动态读取上下文所决定的

  • 考察点: 各使用态中的指针指向

    • 函数直接调用-this指向window

      function foo () {
        console.log('函数内部的this:', this);
      }
      foo();
      
    • 隐式绑定

      // 隐式绑定
      function fn() {
        console.log('隐式绑定', this)
      }
      
      var obj = {
        a: 1,
        fn
      }
      
      obj.fn = fn
      obj.fn() //输出this指向obj
      
      // 面试题1
      const foo = {
        bar: 10,
        fn: function() {
          console.log(this.bar) //undefined
          console.log(this) //window
        }
      }
      
      let fn1 = foo.fn //取出
      fn1()
      
      // 面试题2
      const o1 = {
        text: 'o1',
        fn: function() {
          // 直接使用上下文 - 传统分活
          // console.log('o1-this', this)
          return this.text
        }
      }
      
      const o2 = {
        text : 'o2',
        fn: function() {
          // 求助领导-部门协作
          return o1.fn()
        }
      }
      
      const o3 = {
        text: 'o3',
        fn: function() {
          // 直接借人
          let fn = o1.fn
          return fn()
        }
      }
      
      console.log('o1', o1.fn()) //o1
      console.log('o2', o2.fn()) //o1
      console.log('o3', o3.fn()) //undefined
      

      结论:

      1.在执行函数的时候,执行时调用的上一方—上下文

      2.公共函数 | 全局调用指向window

  • 显示绑定(bind | apply | call)

    • 知识点补充

      • bind

        • 绑定函数与目标函数

          const obj = {
            x: 24,
           //目标函数    
            getX: function(){
              return this.x;
            }
          }
          //绑定函数   
          const _getX = obj.getX;
          console.log(_getX());
          
          const _newGetX = obj.getX.bind(obj);
          console.log(_newGetX());
          // 传参
          const boundFun = fn.bind(thisArg, arg1, arg2)
          const boundFun = (...resetParmas) => fn.call(thisArg, arg1, arg2, ...resetParmas);
          
        • 多重绑定

          // 多重绑定-this值、参数顺序
          // 严格模式,防止this被封装到包装对象中
          "use strict"; 
          function log(...args) {
            console.log(this, ...args);
          }
          
          const boundLog = log.bind('new value', 1, 2);
          const newBoundLog = boundLog.bind('new new value', 3, 4,5);
          newBoundLog(7, 8);
          // new value 1 2 3 4 5 7 8
          
        • 绑定函数构造

          // 绑定函数构造---绑定函数传的this无效,new关键字构造绑定函数,this指向为目标函数
          
          class Base {
            constructor(...args) {
              console.log(new.target === Base);
              console.log(args);
            }
          }
          
          const BoundBase = Base.bind(null, 1, 2,3, 4);
          new BoundBase(); 
          
          /* true
          [ 1, 2, 3, 4 ]
           */
          // 如果目标函数是可构造的,绑定函数也可以使用 new 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数,而提供的 this 值会被忽略(因为构造函数会准备自己的 this,如 Reflect.construct 的参数所示)。如果直接构造绑定函数,new.target 将指向目标函数(也就是说,绑定函数对 new.target 是透明的)。
          
          class Base{}
          const boundBase = Base.bind(null, 1,2);
          console.log(new Base instanceof boundBase);
          console.log(boundBase.length);
          console.log(boundBase.name);
          /* 绑定函数还会继承目标函数的原型链。然而,它不会继承目标函数的其他自有属性(例如,如果目标函数是一个类,则不会继承其静态属性)。 */
          
        • 将方法转化为实用函数

          /* 以 Array.prototype.slice() 为例,你可以使用它将类数组对象转换为真正的数组。 */
          const slice = Array.prototype.slice;
          slice.call(arguments);
          
          /* 请注意,你不能保存 slice.call 并将其作为普通函数调用,因为 call() 方法还会读取其应该调用的函数作为其 this 值。在这种情况下,你可以使用 bind() 来绑定 call() 的 this 值。 */
          const unboundSlice = Array.prototype.slice;
          const slice = Function.prototype.call.bind(unboundSlice);
          slice(arguments);
          
      • apply

        • 传参与参数展开

          //调用
          const numbers = [5, 6, 2, 3,7];
          const max = Math.max.apply(null, numbers);
          const min = Math.min.apply(null, numbers);
          console.log('max, min', max, min);
          //参数展开
          /* 一般而言,fn.apply(null, args) 等同于使用参数展开语法的 fn(...args),只是在前者的情况下,args 期望是类数组对象,而在后者的情况下,args 期望是可迭代对象。 */
          
          const array = ["a", "b"];
          const elements = [0, 1,2];
          array.push.apply(array, elements);
          console.info(array);
          
          array.push(...elements);
          console.log(array);
          
          //批量添加数组元素
          const array = ["a", "b"];
          const elements = [0, 1,2];
          array.push.apply(array, elements);
          console.info(array);
          
          array.push(...elements);
          console.log(array);
          
        • 与call的区别

          与 call() 几乎完全相同,只是函数参数在 call() 中逐个作为列表传递,而在 apply() 中它们会组合在一个对象中,通常是一个数组——例如,func.call(this, "eat", "bananas") 与 func.apply(this, ["eat", "bananas"])。
          
        • 使用 apply() 和内置函数

          /* 使用 apply() 和内置函数 */
          
          const numbers = [5, 6, 2, 3,7];
          let max = Math.max.apply(null, numbers);
          let min = Math.min.apply(null, numbers);
          console.log('max, min:', max, min);
          
          // 与基于简单循环的算法相比
          max = -Infinity;
          min = +Infinity;
          
          for(let i=0; i<numbers.length; i++) {
            if(numbers[i] > max) {
              max = numbers[i];
            }
            if(numbers[i] < min) {
              min = numbers[i];
            }
          }
          console.log('max, min:', max, min);
          
          /* 但要注意:通过使用 apply()(或展开语法)来处理任意长的参数列表,你可能会超过 JavaScript 引擎的参数长度限制。
          如果你的值数组可能会增长到数万个,可以使用混合策略:将数组的片段分批通过 apply 调用函数 */
          function minOfArray (arr) {
            let min = +Infinity;
              const QUANTUM = 32768;
              for(let i=0; i<arr.length; i+= QUANTUM) {
                const submin = Math.min.apply(null, arr.slice(i, Math.min(i+QUANTUM, arr.length)));
                min = Math.min(submin, min);
              }
            return min;
          }
          const min = minOfArray([5, 6, 2, 4, 7]);
          console.log(min);
          
      • call

        使用同apply,只有参数形式不同。

    • apply\bind\call方法区别

      call <=> apply, 传参不同, 依次传入,数组(类数组)传入

      bind返回值不同,返回函数

    • 面试: 手写apply和bind

      /* 手写apply和bind */
      // 1.需求===>手写bind--->bind的位置 ---> Function.prototype ==>原型
      Function.prototype.newBind = function() {
        // 2.bind原理--改变this的指向
        const _this = this;
        const args = Array.prototype.slice.call(arguments);
        const newThis = args.shift();
        // 返回值不执行 ===> 返回函数
        return funciton () {
          // 执行核心
          return _this.newApply(newThis, args);
        }
      }
      // 2.内层实现
      Function.prototype.newApply = function(context) {
        // 参数兜底
        context = context || window;
        // 临时挂载执行环境
        context._this = this;
        let result = arguments[1] 
          ? context.fn(arguments[1])
          : context.fn();
        delete context._this;
        return result;
      }
      
    • 闭包:突破作用域

      //简单演示闭包的含义
      function mail() {
          let content = '信';
          // content作用域为mail函数内,突破此作用域,全局可访问如下操作
          return function() {
              return content;
          }
      }
      mail()();
      
  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值