zoukankan      html  css  js  c++  java
  • 从面试题谈谈js的闭包,原型

    最近群里有小伙伴分享了两道面试题,这里我谈谈自己的理解,废话不多说,上第一题:

    var n = 10;
    var obj = {
        n:20,
        fn:(function(){
            this.n += 2;
            n *= 3;
            return function(){
                this.n *= 2;
                n += 1;
                console.log(n)
            }
        })(n)
    }
    
    var fn = obj.fn;
    fn(); 
    obj.fn() 
    console.log(n,obj.n)  

    这个题目中,定义的obj对象的fn属性是个自执行的函数,任何自执行的函数,其内部this指向全局变量window,所以定义对象obj时,全局变量n由于自执行函数的影响,加上自执行函数内部没有定义n,所以会执行window.n*=2,接着执行window.n+=3,此时全局变量n已经被修改为36;

    接下来

    var fn = obj.fn;
    fn(); 

    这两步其实就是在全局空间定义一个变量fn,赋值为obj的fn属性,也就是那个自执行函数,然后再执行它本身,此时由于自执行函数返回的函数(闭包)中没有定义n,会向上一级作用域查找,就是全局变量n,之前说过,已经被修改成36,所以在执行时

    this.n *= 2;  //36*2=72
    n += 1;    //72+1=73
    console.log(n)

    fn()执行的结果就是打印73;

    下面obj.fn(),fn()作为obj的方法执行,其this值指向obj本身,此时自执行函数返回的函数(闭包)中,this.n就修改了obj.n,使其值变为40,因为返回的函数没有定义n,会向上一级作用域查找,就是全局变量n,所以n+1=73+1=74,所以打印结果为74;

    再接着打印全局变量n,值为74,obj.n上一步说了,是40。

    ok,继续说下一道题目:

    function Foo(){
        getName=function(){alert(1);}
        return this;
    }
                
    Foo.getName=function(){alert(2);}
    Foo.prototype.getName=function(){alert(3);}
    var getName=function(){alert(4);}
    function getName(){alert(5);}
    
    
    Foo.getName(); 
    getName(); 
    Foo().getName(); 
    getName(); 
    new Foo.getName();
    new Foo().getName(); 
    new new Foo().getName();   

    首先看这个题目开始做了些什么事情:首先定义了一个Foo函数,之后为Foo创建了一个名为getName的静态属性,并存储了一个匿名函数,之后为Foo的原型对象创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数,最后再声明一个叫getName函数。

    1.Foo.getName()

    Foo.getName 访问Foo函数上存储的静态属性,即为2。

    2.getName()

    首先要知道的是,所有声明变量或声明函数都会被提升到当前函数的顶部,而函数表达式不会提前,所以题目的代码实际是下面这个样子

    function Foo() {
     getName = function () { alert (1); };
     return this;
    }
    var getName;//提升变量声明
    function getName() { alert (5);}//提升函数声明,覆盖var的声明
     
    Foo.getName = function () { alert (2);};
    Foo.prototype.getName = function () { alert (3);};
    getName = function () { alert (4);};//赋值再次覆盖function getName声明
     
    getName();//最终结果输出4

    3.Foo().getName()

    Foo().getName(); 先执行Foo函数,然后调用Foo函数的返回值this的getName属性函数。Foo函数的第一句 getName = function () { alert (1); }; 是一句函数赋值语句,它没有var声明,所以先向当前Foo函数作用域内寻找getName变量声明,没有找到。继续向当前函数作用域上层,即外层作用域内寻找是否含有getName变量,找到了,也就是第二问中的alert(4)函数,将此变量的值赋值为 function(){alert(1)}。Foo函数返回的this是window对象,相当于执行 window.getName() ,而window中的getName已经被修改为alert(1),所以最终会输出1

    4.getName()

    直接调用getName函数,相当于执行 window.getName() ,因为这个变量已经被第三问Foo函数执行时修改了,所以结果与第三问相同,为1

    5.

    new Foo.getName();
    new Foo().getName(); 
    new new Foo().getName();   

    关于以上三个含有new操作符的式子,一起说一下,这里主要是js中运算符优先级的问题,其实执行顺序依次如下

    new (Foo.getName)();  
    
    //点号优先级高于new,将getName函数作为了构造函数来执行,遂弹出2
    
    (new Foo()).getName(); 
    
    //括号优先级高于new,先执行Foo函数,此时返回的是this,而this在构造函数中本来就代表当前实例化对象,所以最终Foo函数返回实例化对象。
    之后调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,遂到当前对象的原型对象(prototype)中寻找getName
    ,找到了,是3
    new ((new Foo()).getName)(); //先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new。 最终结果为3

    以上就是两道关于原型,作用域闭包,变量提升,以及运算符优先级的问题

  • 相关阅读:
    docker搭建ros-indigo-arm交叉编译环境
    ROS系统C++代码测试之gtest
    ROS系统python代码测试之rostest
    ROS(Robot Operating System)常用环境变量介绍
    【持续更新】 | OpenCV 学习笔记
    Mac + PyCharm 安装 Opencv3 + python2.7
    如何用Caffe训练自己的网络-探索与试验
    九度OJ-第5章-图论
    看周志华教授的一番话有感
    姿势估计实验-Realtime_Multi-Person_Pose_Estimation-CMU
  • 原文地址:https://www.cnblogs.com/Cathamerst/p/7214565.html
Copyright © 2011-2022 走看看