一道颇有难度的JavaScript题
上次分享了一道题,大家反响不错,很开心自己写的东西有人愿意花时间去看,也给了自己莫大的鼓舞,其实做题虽然不比真正的编程,但是也能够让你发现一些你之前没有注意到的语言层面的问题。所以,这次再分享一道稍微有难度的JavaScript题目。
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();
请问上述代码在浏览器环境下,输出结果是多少? 揭晓一下最终答案:
2 4 1 1 2 3 3
前四道难度不是很大,主要是后三道,基本是全军覆没,感叹实在是太绕了了。后面慢慢分析了一下,逐个讲一下吧。 首先必须注意一个问题
function Foo() {
getName = function () {
console.log('1');
};
return this;
}
在函数内部声明的getName
变量,前面是不带有var
、let
,const
的,所以其实根据LHS(这个的介绍可以去的我博客看一下关于LHS和RHS的总结),声明的getName
是在全局范围内(也是就window
)。 其次需要明确你是否知道下面代码在浏览器中的执行结果:
var getName = function () {
console.log('4');
};
function getName() {
console.log(5);
}
getName();
上述代码的执行结果是:4
。原因是这样的,var
声明的变量和函数声明function
都会被提升,但是函数声明的提升的级别是比 var
要高的,所以上面的代码的实际执行结果是:
function getName() {
console.log(5);
}
var getName = function () {
console.log('4');
};
getName();
后一个函数表达式getName
覆盖了前面的函数声明getName
,实际执行的是函数表达式(也就是是为什么JavaScript永远不会有函数重载这么一说了),所以输出的是4
。 首先我给下面的代码添加一下必要的注释:
//函数声明
function Foo() {
//全局变量
getName = function () {
console.log('1');
};
return this;
}
//为函数添加属性getName,其类型是Function,所以这里也可以看出来,Function也是一种Object
Foo.getName = function () {
console.log('2');
};
//为Foo的原型添加方法getName
Foo.prototype.getName = function () {
console.log('3');
};
var getName = function () {
console.log('4');
};
function getName() {
console.log(5);
}
下面执行第一条语句:
Foo.getName();
函数Foo
本身并没有执行,执行的是函数的属性getName
,当然输出的是:2
. 接下来执行:
getName();
这是在全局范围内执行了getName()
,有两条对应的getName
的声明,根据前面我们所提到的提升的级别来看实际执行是函数表达式:
var getName = function () {
console.log('4');
};
所以输出的是4
。 接下来执行
Foo().getName();
首先看一下JavaScript的操作符优先级,从高到低排序 从上面可以看出来
()
与.
优先级相同,所以Foo().getName()
从左至右执行。首先运行Foo()
,全局的getName
被覆盖成输出console.log('1')
,并且返回的this
此时代表的是window
。随后相当于执行的window.getName()
,那么输出的实际就是1
(被覆盖)。 下面到了
getName();
这个不用说了,执行的还是:1
(和上面一毛一样)。 下面到了三个最难的部分:
new Foo.getName();
对于这条语句的执行,有两种可能:
(new Foo).getName()
或
new (Foo.getName)()
但是我们根据操作符优先级表可以得知,其实上.
操作符要比new
优先级要高,所以实际执行的是第二种,所以是对
Foo.getName = function () {
console.log('2');
};
函数执行了new
操作,当然输出的是2
。 下面到了执行
new Foo().getName();
这个语句的可能性也有两种:
(new Foo()).getName();
或者
new (Foo().getName)();
那么应该是那种的呢?原来我以为会是第二种的执行方式,后面通过浏览器调试发现真实的执行的方式是第一种。我看到题目的作者是这么解释的:
首先看运算符优先级括号高于new。实际执行为(new Foo()).getName()。遂先执行Foo函数。
我觉得上面的解释是有问题的,对比上面两种执行方式,第一种是先执行new
,然后执行的是.
操作符,然后执行的是()
。第二种是先执行了()
,再执行的是.
,最后执行new
操作符。如果真的按照引用所说的用优先级的方式判别,其实恰恰应该执行的是第二种而不是第一种。 后来总算找到原因了,原来之前那个出现的比较多的JavaScript优先级的表并不完整,万能的MDN给出了最权威的JavaScript优先级表运算符优先级 我列举出最重要的部分(由高到低): 所以带参数的
new
操作符是优先级最高的,这下就没有问题了,执行顺序确实应该是第一种。 那么按照(new Foo()).getName();
来执行,情况就就很简单了,(new Foo())
返回了新生成的对象,该对象没有getName()
方法,所以在prototype
中找到了getName()
方法。所以输出的是3
。 胜利就在眼前,我们看一下最后一问。
new new Foo().getName();
和上一步一样的方法,我们按照优先级表给分析一下这个语句到底是怎么执行的。 首先带参数的new
操作符优先级最高,第一步划分为:
new (new Foo().getName)();
第二步划分为:
new ((new Foo()).getName)();
所以执行(new Foo()).getName
这个函数是对应的Foo.prototype.getName,所以执行new (Foo.prototype.getName)()
肯定输出的是3
。 哈哈哈,这么难得题终于解决了,开心~总结一下吧,首先JavaScript知识最好去MDN去查,万一别的地方写错了真的是害人不浅。其次,如果在写代码的时候还是少利用操作符优先级这种东西,一旦不明确的地方就立刻用()
,代码的可阅读性真的是很重要!很重要!很重要!毕竟代码还是给人看~
一道颇有难度的JavaScript题的更多相关文章
- 前端面试-----JavaScript题
用面试题,复习一下,js基础. 1.综合题 function Foo() { getName = function () { alert (1); }; return this; } Foo.getN ...
- Javascript题库
一.填空题 JavaScript有两种引用数据类型 :__数组___.__对象__. Javascript通过__setTimeout___延迟指定时间后,去执行某程序. Javascript里Str ...
- ZOJ2006 一道很尴尬的string操作题
ZOJ2006(最小表示法) 题目大意:输出第一个字符串的最小字典序字串的下标! 然后我居然想试一试string的erase的能力,暴力一下,然后20msAC了,尴尬的数据.......... #in ...
- LeetCode 第 287 号问题:寻找重复数,一道非常简单的数组遍历题,加上四个条件后感觉无从下手
今天分享的题目来源于 LeetCode 第 287 号问题:寻找重复数. 题目描述 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个 ...
- 算法是什么我记不住,But i do it my way. 解一道滴滴出行秋招编程题。
只因在今日头条刷到一篇文章,我就这样伤害我自己,手贱. 刷头条看到一篇文章写的滴滴出行2017秋招编程题,后来发现原文在这里http://www.cnblogs.com/SHERO-Vae/p/588 ...
- 通过一道笔试题浅谈javascript中的promise对象
因为前几天做了一个promise对象捕获错误的面试题目,所以这几天又重温了一下promise对象.现在借这道题来分享下一些很基础的知识点. 下面是一个面试题目,三个promise对象捕获错误的例子,返 ...
- 14 道 JavaScript 题?
http://perfectionkills.com/javascript-quiz/ https://www.zhihu.com/question/34079683 著作权归作者所有.商业转载请联系 ...
- 关于定义顺序和内存分配的关系--记一道不严谨的C语言题
include<stdio.h> #include<iostream> int main() { char a[] = "123"; char b[] = ...
- 一道int与二进制加减题
int dis_data = 32769; if( dis_data > 0x7fff) dis_data -= 0xffff; printf("%d\n",dis_dat ...
随机推荐
- 《剑指offer》替换空格
本题来自<剑指offer> 替换空格 题目: 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are% ...
- application program Can't Start
一.电脑插U盘中毒之后感染,出现无法启动,但是风扇工作,先拔掉电源,卸掉电池之后重新安装,重启电脑,之后发现电脑桌面上的程序无法启动,360webShield也无法启动,可能是电脑中毒后的假死机现象. ...
- python(10): xlsxwriter模块
import xlsxwriter as writer 注意: xlsxwriter 只能创建新文件,不可以修改原有文件.如果创建新文件时与原有文件同名,则会覆盖原有文件. import xlsxwr ...
- 20165314 学习基础和C语言基础调查
技能学习心得 你有什么技能比大多人(超过90%以上)更好?针对这个技能的获取你有什么成功的经验?与老师博客中的学习经验有什么共通之处? 从小我的父母就逼着我学习很多技能,比如钢琴,围棋,书法等,不过很 ...
- MySQL源码安装一键脚本
#红色部分根据自己的需求来定义#!/bin/bash #卸载系统自带的Mysql /bin/rpm -e $(/bin/rpm -qa | grep mysql|xargs) --nodeps /bi ...
- Eclipse中java文件生成jar文件的方法
在eclpse中找到你要导出的java程序 选中它 单击文件 -->export 在弹出的export对话框中找到 jar File 单击选中-->next 按图示顺序依次 选 ...
- Python知乎热门话题数据的爬取实战
import requestsfrom pyquery import PyQuery as pq url = 'https://www.zhihu.com/explore'headers = { 'u ...
- 安装和配置bazel
2018-12-26 21:56:23 编译和移植tensorflow的C/C++源码时,用到bazel这一构建工具.本篇blog记录遇到的安装.配置问题. 吐槽 构建工具,从make/ant/cma ...
- python之squid实现免费 IP代理 (windows win7 单机 本机 本地 正向代理 区分 HTTPS)
0.目录 1.思路2.windows安装3.相关命令行4.简单配置和初步使用5.问题:squid是否支持HTTPS6.问题:配置多个代理条目,相同ip不同port报错7.问题:根据代理请求区分HTTP ...
- Python_set集合部分功能介绍
set:无序集合,不能出现重复的元素 set的创建:s1=set() #访问速度快 #解决重复问题 x.add():添加一个新的元素,添加的重复的元素自动过滤掉 x.clear():清空集合 x.di ...