一道超经典js面试题Foo.getName()的故事

目录
  • 一、解析:
    • 1.Foo.getName()
  • 二、解析:
    • 2.getName()
  • 三、解析:
    • 3.Foo().getName()
  • 四、解析:
    • 4.getName()
  • 五、解析:
    • 5.new Foo.getName()
  • 六、解析:
    • 6.new Foo().getName()
  • 七、解析:
    • 7.new new Foo().getName()
  • 总结

下面是一道超经典的JS面试题。

蕴含了静态属性与实例属性,变量提升,this指向,new一个函数的过程

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();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName(); 

输出一下结果

Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3

一、解析:

1.Foo.getName()

我们先看此题的上半部分做了什么,首先定义了一个叫Foo的函数,之后为Foo创建了一个叫getName的静态属性存储了一个匿名函数,之后为Foo的原型对象新创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数,最后再声明一个叫getName函数。

第一问的 Foo.getName 自然是访问Foo函数上存储的静态属性,自然是2

二、解析:

2.getName()

为何输出是4,这里考的是变量提升与函数声明提升。我们知道使用var声明变量会存在变量提升的情况,比如下面的例子中,即使在声明前使用变量a也不会报错,举例:

console.log(a)// undefined
var a = 1;
console.log(a)// 1

因为声明提前会让声明提升到代码的最上层,而赋值操作停留在原地,所以上面代码等同于:

var a
console.log(a)// undefined
a = 1;
console.log(a)// 1

而函数声明(注意是函数声明,不是函数表达式或者构造函数创建函数)也会存在声明提前的情况,即我们可以在函数声明前调用函数:

fn() // 1
function fn() {
    console.log(1);
};
fn() // 1

//因为函数声明提前,导致函数声明也会被提到代码顶端,所以等同于
function fn() {
    console.log(1);
};
fn() // 1
fn() // 1

那这样就存在一个问题了,变量声明会提升,函数声明也会提升,谁提升的更高呢?在你不知道的JavaScript中明确指出,函数声明会被优先提升,也就是说都是提升,但是函数比变量提升更高,所以题目中的两个函数顺序可以改写成:

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

var getName;

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

这样就解释了为什么是输出4。

三、解析:

3.Foo().getName()

其实可以看出来,我们在执行Foo()函数的时候getName这个变量提升到外部的全局作用域中了,因为在js中,如果对于一个变量没用用var 或者 let等声明的话,他就默认是全局属性,就是window对象的一个属性。所以在这里我们的全局的getName又被改了

因为我们Foo()执行的时候返回了this而这里的this就是window对象 我们需要知道的是在浏览器中所有全局的声明都是window对象的属性和方法,所以这里我们调用this.getName()就会返回1了。

四、解析:

4.getName()

这里输出1已经毫无悬念,上一分析中,getName的值在Foo执行时被修改了,所以再调用getName一样等同于window.getName(),同样是输出1。

五、解析:

5.new Foo.getName()

首先还是先看运算符优先级吧,我自个看完的结果是【new Foo() > Foo() > new Foo】,先运算方式2的Foo.getName() 结果为“2”,再new一个Foo实例对象,因此这里new的过程就相当于单纯把Foo.getName执行了一遍输出2。

六、解析:

6.new Foo().getName()

这里考了new基本概念,首先这个调用分为两步,第一步new Foo()得到一个实例,第二步调用实例的getName方法。

我们知道new一个构造函数的过程大致为,以构造函数原型创建一个对象(继承原型链),调用构造函数并将this指向这个新建的对象,好让对象继承构造函数中的构造器属性,如果构造函数没有手动返回一个对象,则返回这个新建的对象。

所以在执行new Foo()时,先以Foo原型创建了一个对象,由于Foo.prototype上事先设置了一个getName方法(输出3的那个),所以这个对象可通过原型访问到这个方法,其次由于Foo内部也没提供什么构造器属性,最终返回了一个this(这个this指向实例),因此这里的this还是等同于我们前面概念提到的以Foo原型创建的对象,可以尝试输出这个实例,除了原型上有一个getName方法就没有其它任何属性,因此这里输出3。

七、解析:

7.new new Foo().getName()

相当于new(new Foo().getName())

先执行new Foo().getName()由6部知道了输出3,再创建Foo.prototype.getName()的实例返回。结果为3

总结

到此这篇关于js面试题Foo.getName()的文章就介绍到这了,更多相关js面试题Foo.getName()内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • javascript最新2020经典面试题

    let 和 var (a): let 声明的变量只在 let 命令所在的代码块内有效 (b): let 是在代码块内有效,var 是在全局范围内有效: (c): let 不存在变量提升,var 会变量提升: for (var i = 0; i < 10; i++) { setTimeout(function(){ console.log(i); }) } // 输出十个 10 for (let j = 0; j < 10; j++) { setTimeout(function(){ conso

  • 详解JS中的this、apply、call、bind(经典面试题)

    这又是一个面试经典问题~/(ㄒoㄒ)/~~也是 ES5中众多坑中的一个,在 ES6 中可能会极大避免 this 产生的错误,但是为了一些老代码的维护,最好还是了解一下 this 的指向和 call.apply.bind 三者的区别. this 的指向 在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象,来,跟着我朗读三遍:this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象.记住这句话

  • 5个JavaScript经典面试题

    1:Scope作用范围 复制代码 代码如下: (function() {     var a = b = 5;  })();  console.log(b); 什么会被打印在控制台上? 回答 上面的代码会打印 5. 这个问题的诀窍是,这里有两个变量声明,但 a 使用关键字var声明的.代表它是一个函数的局部变量.与此相反,b 变成了全局变量. 这个问题的另一个诀窍是,它没有使用严格模式 ('use strict';).如果启用了严格模式,代码就会引发ReferenceError的错误:B没有定义

  • Javascript前端经典的面试题及答案

    前言 如果面试题按类型来分,主要涉及到"技术"与"非技术"两大类,技术类别下涉及到的子类别有: 移动 & PC端布局类 JavaScript 核心基础类 衍生框架类 项目应用类 这四大类别的面试题若按出现频率来划分,则面试时 100% 会问到的题型有:"移动端&PC端布局类.JavaScript 核心基础类".本次选择这两类中难度更高一些的 "JavaScript 核心基础类" 面试题,进行了分析和解答,供面试

  • 一道超经典js面试题Foo.getName()的故事

    目录 一.解析: 1.Foo.getName() 二.解析: 2.getName() 三.解析: 3.Foo().getName() 四.解析: 4.getName() 五.解析: 5.new Foo.getName() 六.解析: 6.new Foo().getName() 七.解析: 7.new new Foo().getName() 总结 下面是一道超经典的JS面试题. 蕴含了静态属性与实例属性,变量提升,this指向,new一个函数的过程 function Foo() { getName

  • JS经典正则表达式笔试题汇总

    本文实例总结了JS经典正则表达式笔试题.分享给大家供大家参考,具体如下: 一.复习字符串的传统操作 如何获取一个字符串中的数字字符,并按数组形式输出,如 dgfhfgh254bhku289fgdhdy675gfh 输出[254,289,675] 分析:循环用charAt()的方法获取到每一个子字符串,判断他是不是在0~9之间,是就把他扔到准备好的数组里 var str="dgfhfgh254bhku289fgdhdy675gfh"; findNum(str); function fin

  • js面试题继承的方法及优缺点解答

    目录 说一说js继承的方法和优缺点? 一.原型链继承 二.借用构造函数(经典继承) 三.组合继承 四.原型式继承 五.寄生式继承 六.寄生组合式继承 说一说js继承的方法和优缺点? 要点: 原型链继承.借用构造函数继承.组合继承.原型式继承.寄生式继承.寄生组合式继承.ES6 Class 答: 一.原型链继承 缺点: 1.引用类型的属性被所有实例共享 2.在创建 Child 的实例时,不能向 Parent 传参 //原型链继承 function Parent() { this.parentPro

  • Spring,hibernate,struts经典面试笔试题(含答案)

    本文讲述了Spring,hibernate,struts经典面试笔试题及其参考答案.分享给大家供大家参考,具体如下: 1.Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory 3.打开Sesssion 4.创建事务Transation 5.持久化操作 6.提交事务 7.关闭Session 8.关闭SesstionFactory 为什么要用: ① . 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代

  • JS面试题---关于算法台阶的问题

    有100格台阶,可以跨1步可以跨2步,那么一个有多少种走法: 今天电话面试.遇到一道算法问题,然后瞬间一脸懵逼: 然后机智的我,自作聪明的想到如果一个人每次都走1步,那么最多100步,每次走2步最少50步:然后明显跑题了...还好对方及时把我打断了...不然我估计要对着这玩意一直死脑经...一路走到黑.. 然后回到家了.拿着偶的mac,然后静静的思考,终于写出来了 var Stairs = new step(); function step(){ this.n1=1; this.n2=2; th

  • JS面试题大坑之隐式类型转换实例代码

    1.1-隐式转换介绍 在js中,当运算符在运算时,如果两边数据不统一,CPU就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成一样的数据类型再计算 这种无需程序员手动转换,而由编译器自动转换的方式就称为隐式转换 例如1 > "0"这行代码在js中并不会报错,编译器在运算符时会先把右边的"0"转成数字0`然后在比较大小 1.2-隐式转换规则 转成string类型: +(字符串连接符) 2..转成number类型:++/--(自增自减运算符

  • JS面试题中深拷贝的实现讲解

    在面试中你是否遇到过如下场景: Q:小朋友,你是否了解如何拷贝一个对象? R:此时,机智的你可能会想到 Object.assign({}, obj); Q:那如何深拷贝一个对象呢? R:机智的你 JSON.parse(JSON.stringify(obj)); Q:使用stringify这种方式有何弊端? 性能问题,stringify再解析其实需要耗费较多时间,特别是数据量大的时候. 一些类型无法拷贝,例如函数(不输出),正则(输出空对象),时间对象(输出时间字符串),Undefiend(不输出

  • js面试题之异步问题的深入理解

    js中的宏任务与微任务 在面试过程中,基本面试官都会问你一些promise的问题,promise是es6的新内容,主要是用来优化异步的问题.笔试中经常会让你写一些promise和setTimeout的执行结果,这你就必须知道宏任务和微任务的概念了! 为什么要使用promise 如果你经历过以前的jquery开发项目,你会遇到以下问题:回调地狱 $.ajax({ ... success: function() { ... $.ajax({ ... success: function() { } }

  • JS面试题之forEach能否跳出循环详解

    当年懵懂无知的我被问到这个问题时,脑袋一片空白,当然也没答对,一直以来我对forEach都有一种错误的理解,由于它比原始的for循环简洁许多,导致我一度认为那是为了方便书写所创造出来的语法糖,在业务中也经常使用,但从没考虑过这种方式存在的问题. forEach使用说明 参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach?v=example arr.for

  • C语言经典指针笔试题详解

    目录 题目一(有关传值调用与非法访问) 题目二 (返回栈空间地址的问题 ) 题目三 (区别传值调用的传址调用) 题目四 (free释放的时机)

随机推荐