美文网首页
经典面试题

经典面试题

作者: Sunshine_0676 | 来源:发表于2020-05-12 16:14 被阅读0次

    1.js中的闭包是什么,有什么作用

    [MDN解释]:函数和对其周围状态(词法环境)的引用捆绑在一起构成闭包。也就是说,闭包可以让你从内部函数访问外部函数作用域,在JS中,每当函数被创建,就会在函数生成时生成闭包。

    答:【函数】和【函数内部能访问到的变量】(也叫环境)的总和,就是一个闭包

    闭包不是需要函数套函数,然后return一个函数吗?为什么要函数套函数呢?
    答:是因为需要局部变量,所以才要把变量放在一个函数里,如果不放在一个函数里,就是一个全局变量了,就达不到使用闭包的目的---隐藏变量,所以函数套函数只是为了造出一个局部变量,跟闭包无关

    为什么要return 函数呢?
    答:因为如果不return,你就无法使用闭包。把return 函数 改成window.函数也是一样的,只要让外面可以访问到这个函数就行了。所以return 函数只是为了函数能被使用,也跟闭包无关。

    闭包的作用
    答:闭包常常用来[间接访问一个变量]。换句话说,[隐藏一个变量]

    假设我们在做一个游戏,在写其中关于「还剩几条命」的代码。
    如果不用闭包,你可以直接用一个全局变量:
    window.lives = 30 // 还有三十条命
    这样看起来很不妥。万一不小心把这个值改成 -1 了怎么办。所以我们不能让别人「直接访问」这个变量。怎么办呢?

    用局部变量。

    但是用局部变量别人又访问不到,怎么办呢?

    暴露一个访问器(函数),让别人可以「间接访问」。

    代码如下:

    !function(){
      var lives = 50
      window.奖励一条命 = function(){
        lives += 1
      }
      window.死一条命 = function(){
        lives -= 1
      }
    }()
    

    那么在其他的 JS 文件,就可以使用 window.奖励一条命() 来涨命,使用 window.死一条命() 来让角色掉一条命。

    闭包是 JS 函数作用域的副产品。

    换句话说,正是由于 JS 的函数内部可以使用函数外部的变量,所以这段代码正好符合了闭包的定义。而不是 JS 故意要使用闭包。

    很多编程语言也支持闭包,另外有一些语言则不支持闭包。

    只要你懂了 JS 的作用域,你自然而然就懂了闭包,即使你不知道那就是闭包!

    2.this关键字

    this是JS的关键字,指函数执行时的上下文,跟函数定义时的上下文无关。随着函数使用场合的不同,this的值也会发生变化。但有一个总的原则,就是this指代的是调用函数的那个对象

    全局上下文

    在全局上下文中,也就是在任何函数体外部,this指代全局对象

    //在浏览器中,this指代全局对象 window
    console.log(this === window)  // true
    
    函数上下文

    在函数上下文中,也就是在任何函数体内部,this指代调用函数的那个对象
    函数调用中的this

    function f1() {
      return this
    }
    console.log(f1() === window) // true
    

    如上代码所示,直接定义一个函数f1,相当于为window对象定义了一个属性。直接执行函数f1(),相当于执行window.f1()。所以函数f1中的this指代调用函数的那个对象,也就是window对象。

    function f2(){
        "use strict"; // 这里是严格模式
        return this;
    }
    console.log(f2() === undefined); // true
    

    如上代码所示,在严格模式下,禁止this关键字指代全局对象,this的值将维持undefined状态。

    对象方法中的this
    var o = {
        name: "stone",
        f: function() {
            return this.name;
        }
    };
    console.log(o.f()); // "stone"
    

    如上代码所示,对象o中包含一个属性name和一个方法f。当我们执行o.f()时,方法f中的this指代调用函数的那个对象,也就是对象o,所以this.name也就是o.name。
    注意,在何处定义函数完全不会影响到this的行为,我们也可以首先定义函数,然后再将其附属到o.f。这样做this的行为也一致。

    var fun = function() {
      return this.name
    }
    var o = {name: 'mm'}
    o.f = fun
    console.log(o.f()) // mm
    

    类似的,this的绑定只受最靠近的成员引用的影响,在下面的这个例子中,我们把一个方法 g() 当作对象 o.b 的函数调用。在这次执行期间,函数中的 this 将指向 o.b。事实上,这与对象本身的成员没有多大关系,最靠近的引用才是最重要的。

    o.b = {
        name: "mm"
        g: fun,
    };
    
    console.log(o.b.g()); // "mm"
    
    eval()方法中的this

    eval()方法可以将字符串转换为JS代码,使用eval方法时,this的指向哪里呢?答案很简单,看谁在调用eval方法,调用者的执行环境中的this就被eval方法继承下来了

    // 全局上下文
    function f1() {
      return eval('this')
    }
    console.log(f1() === window) //true
    
    //函数上下文
    var o = {
      name: 'stone',
      f: function() {
        return eval('this.name')
      }
    }
    
    call和apply方法中的this

    call和apply是函数对象的方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指代的就是这两个方法的第一个参数。

    var x = 0
    function f() {
      console.log(this.x)
    }
    var o = {}
    o.x = 1
    o.m = f
    o.m.apply() // 0
    

    call和apply的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。如果把最后一行改为

    o.m.apply(o) //1
    

    运行结果就变成了1,证明了这时this指代的是对象o

    bind方法中的this

    ES5引入了Function.prototype.bind.调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到bind的第一个参数,无论这个函数是如何被调用的。

    function f() {
      return this.a
    }
    var g = f.bind({
      a: 'stone'
    })
    console.log(g()) // stone
    var o = {
      a: 28,
      f: f, 
      g: g
    }
    console.log(o.f(), o.g()) // 28, stone
    
    
    DOM事件处理函数中的this

    一般来讲,当函数使用addEventListener,被用作事件处理函数时,它的this指向触发事件的元素

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
        <button id="btn" type="button">click</button>
        <script>
            var btn = document.getElementById("btn");
            btn.addEventListener("click", function(){
                this.style.backgroundColor = "#A5D9F3";
            }, false);
        </script>
    </body>
    </html>
    
    

    但在ID浏览器中,当函数使用attachEvent,被当作事件处理函数时,它的this却指向window

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
        <button id="btn" type="button">click</button>
        <script>
            var btn = document.getElementById("btn");
            btn.attachEvent("onclick", function(){
                console.log(this === window);  // true
            });
        </script>
    </body>
    </html>
    
    
    内联事件处理函数中的this

    当代码被内敛处理函数调用时,它的this指向监听器所在的DOM元素。

    <button onclick="alert(this.tagName.toLowerCase());">
      Show this
    </button>
    

    上面alert会显示button,注意只有外层代码中的this是这样设置的。如果this被包含在匿名函数中,则友事另一种情况了

    <button onclick="alert((function(){return this})());">
      Show inner this
    </button>
    

    在这种情况下,this被包含在匿名函数中,相当于处于全局上下文中,所以它指向window对象。

    练习题

    // 挑战一
    function func1() {
        function func2() {
            console.log(this)
        }
        return func2;
    }
    func1()();  // ???
    
    // 挑战二
    scope = "stone";
    
    function Func() {
        var scope = "sophie";
    
        function inner() {
            console.log(scope);
        }
        return inner;
    }
    
    var ret = Func();
    ret();    // ???
    
    
    // 挑战三
    scope = "stone";
    
    function Func() {
        var scope = "sophie";
    
        function inner() {
            console.log(scope);
        }
        scope = "tommy";
        return inner;
    }
    
    var ret = Func();
    ret();    // ???
    
    
    // 挑战四
    scope = "stone";
    
    function Bar() {
        console.log(scope);
    }
    
    function Func() {
        var scope = "sophie";
        return Bar;
    }
    
    var ret = Func();
    ret();    // ???
    
    
    // 挑战五
    var name = "The Window";  
    var object = {    
        name: "My Object",
        getNameFunc: function() {      
            return function() {        
                return this.name;      
            };    
        }  
    };  
    console.log(object.getNameFunc()());    // ???
    
    
    // 挑战六
    var name = "The Window";  
    var object = {    
        name: "My Object",
        getNameFunc: function() {      
            var that = this;      
            return function() {        
                return that.name;      
            };    
        }  
    };  
    console.log(object.getNameFunc()());    // ???
    
    

    简言之:
    a.如果是一般函数,this指向全局对象window;

    b.在严格模式下"use strict",为undefined.

    c.对象的方法里调用,this指向调用该方法的对象.

    d.构造函数里的this,指向创建出来的实例.

    3.原型及原型链

    原型链是一种机制,指的是JS每个对象都有一个内置的porto属性指向创建它的构造函数的prototype(原型)属性。原型链的作用是为了实现对象的继承。

    原型链

    相关文章

      网友评论

          本文标题:经典面试题

          本文链接:https://www.haomeiwen.com/subject/ojdpnhtx.html