function Foo() {

getName = function () { alert (1); };

return this;

}

Foo.getName = function () { alert (2);};

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

C:var getName = function () { alert (4);};

D:function getName() { alert (5);}

//请写出以下输出结果:

Foo.getName();         //访问静态属性

getName();               //变量提升(C先变量提升,D接着覆盖,最后C赋值再次覆盖,输出4 )

Foo().getName();     //函数作用域,运算符优先级。getName变量当前作用域找不着,接着往上找,结果找着C,将其覆盖,结束。点.高于括号。  这里前面有点懵,找到全局的为啥用Foo()放回的函数getName值覆盖呢?

getName();              //this指向window.

new Foo.getName();         //运算符优先级,先.,然后返回Foo函数里面的方法getName并做构造函数对象实例化。

new Foo().getName();      //运算符优先级

new new Foo().getName();        //运算符优先级

答案是:

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();//2

getName();//4

Foo().getName();//1

getName();//1

new Foo.getName();//2

new Foo().getName();//3

new new Foo().getName();//3

此题是我综合之前的开发经验以及遇到的JS各种坑汇集而成。此题涉及的知识点众多,包括变量定义提升、this指针指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级等等。

此题包含7小问,分别说下。

第一问

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

第一问的 Foo.getName 自然是访问Foo函数上存储的静态属性,自然是2,没什么可说的。

第二问

第二问,直接调用 getName 函数。既然是直接调用那么就是访问当前上文作用域内的叫getName的函数,所以跟1 2 3都没什么关系。此题有无数面试者回答为5。此处有两个坑,一是变量声明提升,二是函数表达式。

变量声明提升

即所有声明变量或声明函数都会被提升到当前函数的顶部。

例如下代码:

console.log('x' in window);//true

var x;

x = 0;

代码执行时js引擎会将声明语句提升至代码最上方,变为:

var x;

console.log('x' in window);//true

x = 0;

函数表达式

var getName 与 function getName 都是声明语句,区别在于 var getName 是函数表达式,而 function getName 是函数声明。关于JS中的各种函数创建方式可以看 大部分人都会做错的经典JS闭包面试题 这篇文章有详细说明。

函数表达式最大的问题,在于js会将此代码拆分为两行代码分别执行。

例如下代码:

console.log(x);//输出:function x(){}

var x=1;

function x(){}

实际执行的代码为,先将 var x=1 拆分为 var x; 和 x = 1; 两行,再将 var x; 和 function x(){} 两行提升至最上方变成:

var x;

function x(){}

console.log(x);

x=1;

所以最终函数声明的x覆盖了变量声明的x,log输出为x函数。

同理,原题中代码最终执行时的是:

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

第三问

第三问的 Foo().getName(); 先执行了Foo函数,然后调用Foo函数的返回值对象的getName属性函数。

Foo函数的第一句  getName = function () { alert (1); };  是一句函数赋值语句,注意它没有var声明,所以先向当前Foo函数作用域内寻找getName变量,没有。再向当前函数作用域上层,即外层作用域内寻找是否含有getName变量,找到了,也就是第二问中的alert(4)函数,将此变量的值赋值为 function(){alert(1)}。

此处实际上是将外层作用域内的getName函数修改了。

注意:此处若依然没有找到会一直向上查找到window对象,若window对象中也没有getName属性,就在window对象中创建一个getName变量。

之后Foo函数的返回值是this,而JS的this问题博客园中已经有非常多的文章介绍,这里不再多说。

简单的讲,this的指向是由所在函数的调用方式决定的。而此处的直接调用方式,this指向window对象。

遂Foo函数返回的是window对象,相当于执行 window.getName() ,而window中的getName已经被修改为alert(1),所以最终会输出1

此处考察了两个知识点,一个是变量作用域问题,一个是this指向问题。

第四问

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

第五问 new Foo.getName(); ,此处考察的是js的运算符优先级问题。

js运算符优先级:

参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

通过查上表可以得知点(.)的优先级高于new操作,遂相当于是:

new (Foo.getName)();

所以实际上将getName函数作为了构造函数来执行,遂弹出2。

第六问

第六问 new Foo().getName() ,首先看运算符优先级括号高于new,实际执行为

(new Foo()).getName()

遂先执行Foo函数,而Foo此时作为构造函数却有返回值,所以这里需要说明下js中的构造函数返回值问题。

构造函数的返回值

在传统语言中,构造函数不应该有返回值,实际执行的返回值就是此构造函数的实例化对象。

而在js中构造函数可以有返回值也可以没有。

1、没有返回值则按照其他语言一样返回实例化对象。

2、若有返回值则检查其返回值是否为引用类型。如果是非引用类型,如基本类型(string,number,boolean,null,undefined)则与无返回值相同,实际返回其实例化对象。

3、若返回值是引用类型,则实际返回值为这个引用类型。

原题中,返回的是this,而this在构造函数中本来就代表当前实例化对象,遂最终Foo函数返回实例化对象。

之后调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,遂到当前对象的原型对象(prototype)中寻找getName,找到了。

遂最终输出3。

第七问

第七问, new new Foo().getName(); 同样是运算符优先级问题。

最终实际执行为:

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

先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new。

遂最终结果为3

注意:new foo()带参数比new foo无参数优先级高。

一道有趣的JS问题的更多相关文章

  1. javascript基础修炼(13)——记一道有趣的JS脑洞练习题

    目录 一. 题目 二. 解法风暴 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址 ...

  2. javascript基础修炼(13)——记一道有趣的JS脑洞练习题【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  3. 一道有趣的for循环题

    一道有趣的for循环题 今天在复习js基础知识时发现了一个for循环的题,第一眼看到直接懵逼了,没想到for循环竟然还可以这样玩?涨姿势了. 题目是这样的 for(i=0, j=0; i<10, ...

  4. 一道变态的js题

    一道腾讯js面试题 题目如下: f = function() {return true;}; g = function() {return false;}; (function() { if (g() ...

  5. 【前端词典】4 个实用有趣的 JS 特性

    前言 最近在学习的过程中发现了我之前未曾了解过的一些特性,发现有些很有趣并且在处理一些问题的时候可以给我一个新的思路. 这里我将这些特性介绍给大家. 4 个有趣的 JS 特性 利用 a 标签解析 UR ...

  6. 一个有趣的js隐式转换的问题

    一个有趣的js隐式转换的问题 在chrome的控制台中打印一下表达式 [] + {} //结果为 [object object] 然后调整顺序打印 {} + [] //结果为 0 然后将两个表达式组合 ...

  7. codeforces 1451D,一道有趣的博弈论问题

    大家好,欢迎来到codeforces专题. 今天选择的问题是Contest 1451场的D题,这是一道有趣简单的伪博弈论问题,全场通过的人有3203人.难度不太高,依旧以思维为主,坑不多,非常友好. ...

  8. 有趣的JS存储 连等问题

    五个月不见了,你是不是和我一样又帅了,今天我们先来看一道经典的关于JS存储的题目,来一场紧张又刺激的脑内吃鸡大战吧: var a = {n:1}; a.x = a = {n:2}; console.l ...

  9. 一道经典的js面试题

    # 声明:学习编程语言最好的方式就是通过实例学习 ## 下面是我在博客上看到的一道js面试题,可以说非常经典,下面会以最简单的方式让你理解题目:```bashfunction Foo() { getN ...

随机推荐

  1. 原生JavaScript中动画与特效的实现原理

    现如今,许多页面上均有一些动画效果.适当的动画效果可以在一定程度上提高页面的美观度,具有提示效果的动画可以增强页面的易用性. 实现页面动画的途径一般有两种. 一种是通过操作JavaScript间接操作 ...

  2. Spark环境搭建(二)-----------HDFS shell 常用操作

    配置好HDFS,也学习了点HDFS的简单操作,跟Linux命令相似 1)  配置Hadoop的环境变量,类似Java的配置 在 ~/.bash_profile 中加入 export HADOOP_HO ...

  3. 分类器、logistic回归

    相关性 1.相关性是一种测度,用来表示两个变量在同一方向上发生变化的程度,如果x和y在变化方向上相同,那么这两个变量就是正相关:如果变化方向相反,就是负相关:如果变量之间没有关系,那么相关性就是0. ...

  4. redis日志格式

    在redis.conf中,在大概65行左右有个loglevel # 指定日志记录级别# Redis总共支持四个级别:debug.verbose.notice.warning,默认为verbose# d ...

  5. JavaFile I/O

    Java流类图结构: 流的概念和作用: 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.及数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将抽象为各种类,方便更直观 ...

  6. [LeetCode] Largest Triangle Area 最大的三角区域

    You have a list of points in the plane. Return the area of the largest triangle that can be formed b ...

  7. javascript的数组之reduce()

    reduce()方法对累加器和数组中的每个元素(从左到右)应用到一个函数中,最终得到一个值并返回 const array1 = [1, 2, 3, 4]; const reducer = (accum ...

  8. raycast 一小段距离碰撞到的poly

    dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos, const dtQuer ...

  9. github=>git=>composer Packages 使用教程

    2018年12月17日14:32:05 因为要做搜索,所以需要用分词工具php的分词不借助的第三方的真的很少, 目前选择的是 http://www.phpbone.com/phpanalysis/ 但 ...

  10. Python全栈-magedu-2018-笔记10

    第三章 - Python 内置数据结构 集set 约定 set 翻译为集合 collection 翻译为集合类型,是一个大概念 set 可变的.无序的.不重复的元素的集合 set定义 初始化 set( ...