最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 一道面向对象的面试题(阿里)

    正文概述 掘金(今天的代码有点烫手)   2021-03-18   566

    变量提升(需彻底理解)

    在当前作用域下(在当前的执行上下文下),在所有JS代码执行之前,把所有带var和所有带function关键字的提前的声明(创建一个变量)或者定义(给一个变量赋值)。带var的值提前声明,带function的提前声明+定义。贴段代码帮助我们理解这个概念:

    var getName = function () {
       console.log(4);
    }
    
    function getName() {
       console.log(5);
    }
    
    getName(); //输出4
    

    为什么这里的getName()输出的是4呢❓下面我们分两个阶段分析?:

    1、代码执行之前: 处理带有var和function关键字的语句,流程如下:

    1. var的提前声明,则定义了一个叫getName的变量,但是不赋值,因为这是代码执行之前!
    2. function的提前声明+定义,则定义了一个叫getName的变量,但是发现getName变量已经被定义,那么只是将一个方法赋给了它。此时getName=fuc=>5

    2、代码执行时: 给getName复制,此时getName=fuc=>4

    理解完上面这个概念之后,我们再看下面这道题

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

    解析:

    明天再写吧.....头疼?

    和老友叙旧2个多小时,为了兄弟们的终身大事,我操碎了心?!这部分明天周六补上!

    周六早上起床真的太难了!!!

    咱们继续(拖延症得治!!)(ง •_•)ง:

    根据上面的变量提升的概念,我们知道在代码运行之前是变量提升

    1、所以第一步(变量提升): 寻找function和var关键字的代码

    // 第一个function Foo() { ... },function关键字,声明+定义
    Foo = AAAFFFOOO  // 这里的AAAFFFOOO假设是Function对象在堆内存中的引用地址
    

    当前内存图:

    一道面向对象的面试题(阿里)

    // 第一个var getName = ... ,var关键字只声明
    getName = undefined // 再次再强调一遍,var只声明,不定义,所以此时getName的值是undefined
    

    当前内存图:

    一道面向对象的面试题(阿里)

    // 第二个function getName() { ... },function关键字,声明+定义。但是发现getName变量已经在上一步被声明,所以只需要定义就行,即赋值即可
    getName = func=>5 // 这里是伪代码,func=>5表示输出5的那个方法在堆内存中的引用地址,后续将全部按照这种写法
    

    至此,变量提升完毕!我们可以看到的是,目前全局的getName指向的是func=>5

    当前内存图:

    一道面向对象的面试题(阿里) 2、接下来第二步(代码执行): 我们一句一句来看

    <第1句 />


    function Foo() {
       getName = function () {
           console.log(1);
       }
       return this;
    }
    

    变量提升阶段已完成,跳过,当前内存图如下:

    一道面向对象的面试题(阿里) <第2句 />


    Foo.getName = function () {
       console.log(2);
    }
    

    为Foo添加属性getName,该属性也是一个引用类型,当前内存图如下:

    一道面向对象的面试题(阿里) <第3句 />


    Foo.prototype.getName = function () {
        console.log(3);
    }
    

    为Foo的原型对象(prototype)添加属性getName,该属性也是一个引用类型,当前内存图如下:

    一道面向对象的面试题(阿里) <第4句 />


    var getName = function () {
        console.log(4);
    }
    

    注意!!!! 这一步是一个赋值操作,因为我们现在处于方法执行阶段,而非变量提升阶段,所以这里一定不能搞混了。当前的内存图为:

    一道面向对象的面试题(阿里) <第5句 />


    function getName() {
        console.log(5);
    }
    

    变量提升阶段已完成,跳过,内存图不变,同上一步。

    <第6句 />


    Foo.getName();
    

    根据方法内存图,很容易可以看到Foo.getName的引用指向的是输出为2的方法,因此该语句输出如下:

    2
    

    <第7句 />


    getName();
    

    根据方法内存图,很容易可以看到当前全局下的getName变量的引用指向的是输出为4的方法,因此该语句输出如下:

    4
    

    <第8句 />


    Foo().getName();
    

    根据JS的运算符执行优先级,先计算Foo(),Foo的方法执行执行栈(ECStack)如下图所示:

    一道面向对象的面试题(阿里)

    此时的内存图为:

    一道面向对象的面试题(阿里)

    根据方法的执行栈可以看出来,Foo()执行返回了this也就是windows对象,接下来计算的就是window.getName(),即全局的getName方法,根据当前的内存图可以看到目前全局的getName的引用指向是输出为1的方法,因此该语句输出如下:

    1
    

    <第9句 />


    getName();
    

    第八句代码执行分析,我们已知,目前全局的getName的引用指向是输出为1的方法,因为该语句输出如下:

    1
    

    <第10句 />


    new Foo.getName(); // 2
    

    根据JS的运算符执行优先级,当前优先级最高的是成员访问(… . …)运算符,即先执行Foo.getName,而Foo.getName指向的是输出为2的方法。接下来优先级最高的就是new(带参数列表)(new … ( … ))运算符了,因为上面代码可以理解拆分成下面这两步:

    var s = Foo.getName;
    var r = new s();
    

    new一个方法,对于方法内部,相当于把这个方法当成一个普通方法执行了,因此该语句输出如下:

    2
    

    <第11句 />


    new Foo().getName();
    

    运算符优先级如下

    1. new Foo() // 看成一个整体xxx
    2. xxx.getName()
    

    首先new Foo()创建了一个Foo的实例,然后xxx.getName()相当于访问Foo实例的getName(),先从其自身上面找有无getName()方法,发现没有,则去其原型链找,发现原型链上有getName,并且根据我们上面的内存图中可以看出引用指向的是输出为3的方法,因此该语句输出如下:

    3
    

    这里有个问题需要注意,上面的第二句代码Foo.getName = function(){ ... }是给Foo这个对象定义的属性方法,并非Foo实例的属性方法,这一点一定注意!! 给Foo实例定义方法我们一般是这么写的:

    var Foo = function(){
        this.getName=function(){ ... }
    }
    

    <第12句 />


    new new Foo().getName();
    

    运算符优先级如下

    1. new Foo() // 看成一个整体xxx
    2. xxx.getName // 看成一个整体zzz
    3. new zzz()
    

    由第11句可以看出来new Foo().getName既是挂在Foo实例的原型对象上的方法,引用指向了输出为3的方法,new一个方法,对于方法内部,相当于把这个方法当成一个普通方法执行了,因此该语句输出如下:

    3
    

    总结

    看似简单的一道面试题里面埋了多少的坑,考察了多少知识点,有堆栈内存,有面向对象,有运算符的优先级,有原型,有闭包等等,所以真的是面试造火箭,工作拧螺丝,但是这些知识掌握不透彻,螺丝能拧好吗?


    起源地下载网 » 一道面向对象的面试题(阿里)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元