this指针、闭包、作用域

81 阅读2分钟

作用域链

  • 作用域链:作用域链是ECMAScript引擎在执行代码时创建的,用来查找变量和函数的。
面试题
   let a = 10;
   funtion fn1(){
      console.log(a);
      let b=20;
      fn2();
      // 函数提升
      function fn2(){
         console.log(b);
         let c=30;
         fn3();
         function fn3(){
            // 变量提升
            console.log(d);
            var d=40;
            // 作用域向上查找
            console.log(c);
         }
      }
   }
   fn1();
   /************* 
    * 提升优先级:函数提升>变量提升
    * 提升维度:变量优先
    * 执行维度:函数先打印
    */
   // *************
   // 前置结论:函数是天然隔离的方案
   // 块级作用域
   if(true) {
      let e = 1;
      var f = 2;
      console.log('e',e);
      console.log('f',f);
   }
   console.log('e',e);
   console.log('f',f);
  • 对于作用域链我们直接通过创建态来定位作用域链条的某一环
  • 手动取消链条甚至全局作用域,可以利用块级作用域做性能优化

this上下文 context

  • this上下文:this上下文是函数执行时,this上下文指向的。
函数直接调用 -- this指向window
   function fn1(){
      console.log(this);
   }
   fn1();
隐式绑定 -- this指向调用堆栈的上一级
   function fn2(){
      console.log(this.a);
   }
   let obj = {
      a:1,
      fn2
   }
   obj.fn2();
面试题
   const obj = {
      a:1,
      fn1: function(){
         console.log(this.a);
         console.log(this);
      }
   }
   let fn2 = obj.fn1;
   fn2(); // this指向window

   // 如何改变this指向
   const obj1 = {
      a:1, 
      fn: function(){
         // console.log('this指向obj1',this);
         return this.a;
      }
   }
   const obj2 = { 
      a:2, 
      fn: function(){ 
         return obj1.fn();
      } 
   };
   const obj3 = {
      a:3, 
      fn: function(){
         let fn = obj1.fn;
         return fn();
      }
   }
   console.log('obj1',obj1.fn());
   console.log('obj2',obj2.fn());
   console.log('obj3',obj3.fn());
  • 在执行函数时,函数背上一级调用,删上下文指向上一级
  • 直接变成公共函数时this指向window
将obj2.fn()结果指向obj2
   // 直接使用call、apply、bind,改变this指向
   obj2.fn.call(obj2);
   // obj2.fn.apply(obj2);
   // obj2.fn.bind(obj2)();
   // 不改变this指向
   const obj1 = {
      a:1, 
      fn: function(){
         return this.a;
      }
   }
   const obj2 = {
      a:2,
      fn: obj1.fn
   }
   // 执行函数时 this指向最后调用的对象
显式绑定(bind|call|apply)
   function fn1(){
      console.log(this);
   }
   fn1();
   fn1.call({a:1});
   fn1.apply({a:2});
   fn1.bind({a:3})();
call、apply、bind区别
  1. call、apply、bind都是改变this指向,但是call、apply改变this指向后,函数执行完会返回函数本身,bind改变this指向后,函数执行完不会返回函数本身
  2. call、apply 传入参数不同,call多个参数一次传入,apply是传入数组
  3. 面试:手写apply & bind
   // 需求:手写 bind => bind 位置 => Function.prototype => 原型
   Function.prototype.newBind = function() {
      // bind 改变原理
      let self = this;
      // let args = [...arguments].slice(1);
      let args = Array.prototype.slice.call(arguments);
      let newThis = args.shift();
      function() {
         return self.newBpply(newThis, args);
      }
   }

   Function.prototype.newApply = function(context) {
      if(typeof this !== 'function') {
         throw new Error('type error');
      }
      // 参数兜底
      context = context || window;
      // 临时挂载执行函数
      context.fn = this;
      let result = arguments[1] ? context.fn(...arguments[1]) : context.fn();
      delete context.fn;
      return result;
   }
new
   class Person {
      constructor(name, age) {
         this.name = name;
         this.age = age;
      }
      test() {
         console.log(this.name);
      }
      asynctest() {
         setTimeout(() => {
            console.log(this.name);
         },100)
      }
   }

   const person = new Person('zhangsan', 18);
   person.test();
   person.asynctest();
闭包 (如何突破作用域)
   function test() {
      let a = 1;
      return function() {
         console.log(a);
         a++;
         return a; 
      }
   }
   const fn = test();