当前位置:网站首页>this+闭包+作用域 面试题

this+闭包+作用域 面试题

2022-07-05 02:41:00 _语墨

作用域链

let a = 'global';
    console.log(a);

    function course() {
    
        let b = 'zhaowa';
        console.log(b);

        session();
        function session() {
    
            let c = 'this';
            console.log(c);

            teacher();
            function teacher() {
    
                let d = 'yy';
                console.log(d);

                console.log('test1', b);
            }
        }
    }
    console.log('test2', b);
    course();

报错:b is not defined

  if(true) {
    
        let e = 111;
        console.log(e);
    }
    console.log('test3', e)

报错: e is not defined

  • 对于作用域链我们直接通过创建态来定位作用域链
  • 手动取消全局,使用块级作用域

this - 上下文context

this是在执行时动态读取上下文决定的,而不是创建时

函数直接调用中 this指向的是window(函数表达式、匿名函数、嵌套函数)

function foo(){
    
console.log(this)   //指向window
}

隐式绑定

1.this的指向是调用堆栈的上一级 => 对象、数组等引用关系逻辑

   function fn() {
    
        console.log('隐式绑定', this.a);
    }
    const obj = {
    
        a: 1,
        fn
    }

    obj.fn = fn;
    obj.fn();

面试题:

  const foo = {
    
        bar: 10,
        fn: function() {
    
            console.log(this.bar);
            console.log(this);
        }
    }

    let fn1 = foo.fn;

    fn1();  //this 指向windows

    // 追问1, 如何改变指向
    const o1 = {
    
        text: 'o1',
        fn: function() {
    
            // 直接使用上下文 - 传统分活
            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('o1fn', o1.fn());
    console.log('o2fn', o2.fn());
    console.log('o3fn', o3.fn());

	o1fn o1
    o2fn o1
    o3fn undefined   //指向windows
  • 在执行函数时,函数被上一级调用,上下文指向上一级
  • or直接变成公共函数,指向window
    追问: 现在我要 console.log(‘o2fn’, o2.fn()); 的结果是o2
    // 1. 人为干涉,改变this - bind/call/apply
    // 2. 不许改变this
    const o1 = {
    
        text: 'o1',
        fn: function() {
    
            return this.text;
        }
    }

    const o2 = {
    
        text: 'o2',
        fn: o1.fn
    }

    console.log('o2fn', o2.fn());
    // this指向最后调用他的对象,把在fn执行时,o1.fn抢过来挂载在自己o2fn上即可

2.new - this指向的是new之后得到的实例

  class Course {
    
        constructor(name) {
    
            this.name = name;
            console.log('构造函数中的this:', this);
        }

        test() {
    
            console.log('类方法中的this:', this);
        }
    }

    const course = new Course('this');
    course.test();
    //指向实例course

追问: 类中异步方法,this有区别吗

 class Course {
    
        constructor(name) {
    
            this.name = name;
            console.log('构造函数中的this:', this);
        }

        test() {
    
            console.log('类方法中的this:', this);
        }
        asyncTest() {
    
            console.log('异步方法外:', this);
            setTimeout(function() {
    
                console.log('异步方法内:', this);
            }, 100)
        }
    }

    const course = new Course('this');
    course.test();
    course.asyncTest();

在这里插入图片描述

  • 执行settimeout时,匿名方法执行时,效果和全局执行效果相同
  • 追问 如何解决: 使用箭头函数

显式绑定(bind|apply|call)

  function foo() {
    
        console.log('函数内部this', this);
    }
    foo();

    // 使用
    foo.call({
    a: 1});
    foo.apply({
    a: 1});

    const bindFoo = foo.bind({
    a: 1});
    bindFoo();

追问:call、apply、bind的区别

  • call 、apply 传参不同 依次传入/数组传入
  • bind直接返回函数,需要再次执行 传参和call一样

bind 的原理 / 手写bind

原理或手写类题目,解题思路:
1.说明原理,写下注释
2.根据注释,补齐源码

 function sum(a, b, c) {
    
        console.log('sum', this);
        return a + b + c;
    }
    // 1. 需求:手写bind => bind位置(挂在那里) => Function.prototype
    Function.prototype.newBind = function() {
    
        // 2. bind是什么? 
        const _this = this;
        const args = Array.prototype.slice.call(arguments);
        // args特点,第一项是新的this,第二项~最后一项函数传参
        const newThis = args.shift();

        // a. 返回一个函数
        return function() {
    
            // b. 返回原函数执行结果 c. 传参不变
            return _this.apply(newThis, args);
        }
    }


    //手写apply
    Function.prototype.newApply = function(context) {
    
        // 边缘检测
        // 函数检测
        if (typeof this !== 'function') {
    
            throw new TypeError('Error');
        }
        // 参数检测
        context = context || window;

        // 挂载执行函数
        context.fn = this;

        // 执行执行函数
        let result = arguments[1]
            ? context.fn(...arguments[1])
            : context.fn();

        // 销毁临时挂载
        delete context.fn;
        return result;
    }

如何突破作用域的束缚?

闭包

闭包:一个函数和他周围状态的引用捆绑在一起的组合

  function mail() {
    
        let content = '信';
        return function() {
    
            console.log(content);
        }
    }
    const envelop = mail();
    envelop();
  • 函数外部获取到了函数作用域内的变量值
    函数作为参数的时候
   // 单一职责
    let content;
    // 通用存储
    function envelop(fn) {
    
        content = 1;

        fn();
    }

    // 业务逻辑
    function mail() {
    
        console.log(content);
    }

    envelop(mail);
    

在这里插入图片描述
函数嵌套

  let counter = 0;

    function outerFn() {
    
        function innerFn() {
    
            counter++;
            console.log(counter);
            // ...
        }
        return innerFn;
    }
    outerFn()();

在这里插入图片描述
事件处理(异步执行)的闭包

 let lis = document.getElementsByTagName('li');

    for(var i = 0; i < lis.length; i++) {
    
        (function(i) {
    
            lis[i].onclick = function() {
    
                console.log(i);
            }
        })(i);
    }

解决:
1.改为立即执行函数,传参形式。
2.将var 改为let
追问:
立即执行嵌套

 (function immediateA(a) {
    
        return (function immediateB(b) {
    
            console.log(a); // 0
        })(1);
    })(0);

立即执行函数遇上块级作用域

  let count = 0;

    (function immediate() {
    
        if(count === 0) {
    
            let count = 1;

            console.log(count);
        }
        console.log(count);
    })();

在这里插入图片描述
拆分执行 - 关注

  function createIncrement() {
    
        let count = 0;
        
        function increment() {
    
            count++;
        }

        let message = `count is ${
      count}`;

        function log() {
    
            console.log(message);
        }

        return [increment, log];
    }
    const [increment, log] = createIncrement();

    increment();
    increment();
    increment();
    log();

实现私有变量

   function createStack() {
    
        return {
    
            items: [],
            push(item) {
    
                this.item.push(item);
            }
        }
    }

    const stack = {
    
        items: [],
        push: function() {
    }
    }

    function createStack() {
    
        const items = [];
        return {
    
            push(item) {
    
                items.push(item);
            }
        }
    }
    // Vuex store
原网站

版权声明
本文为[_语墨]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_44247866/article/details/125541639