一个很有趣的javascript题目

代码如下所示

//声明Fn函数
function Fn() {
   //函数表达式 这里没有使用 var、let、const来定义变量,所以是全局变量
    getName = function () { 
        console.log('CJF1');
    };
    return this;
}
//函数Fn添加getName函数(静态函数)
Fn.getName = function () {
    console.log('CJF2');
};
//原型上添加getName函数,所有实例共享
Fn.prototype.getName = function () { 
    console.log('CJF3');
};
//函数表达式
var getName = function () { 
    console.log('CJF4');
};
//函数声明getName
function getName() { 
    console.log('CJF5');
}
Fn.getName();  //第一个
getName();    //第二个
Fn().getName(); //第三个
getName();       //第四个
new Fn.getName(); //第五个
new Fn().getName();   //第六个
new new Fn().getName();  //第七个

接下按照步骤一个一个执行
第一个

Fn.getName();  //输出 "CJF2"
//给函数Fn添加属性getName,可以看出在javascript中函数也是对象。函数本身并没有执行,这里调用的函数Fn的属性getName方法

第二个

getName(); //输出 "CJF4" 
//这里是调用全局的getName方法,相当于执行window.getName()

//全局的getName方法有两个 ,一个函数表达式,另一个是函数声明
//函数表达式
var getName = function () { 
    console.log('CJF4');
};
//函数声明getName
function getName() { 
    console.log('CJF5');
}

/*var声明的函数表达式和函数声明function都会被提升,但是函数声明的提升的级别是比
var声明的函数表达式要高的,所以上面的代码的实际执行结果是*/

//函数声明getName
function getName() { //先执行
    console.log('CJF5');
}
//函数表达式
var getName;//变量声明提升,但是赋值并未提升
getName = function () { //后执行
    console.log('CJF4');
};

第三个

Fn().getName();//输出 "CJF1"
//此时调用Fn()函数,执行的是普通函数调用返回的this对象为window对象(即调用window.getName()),Fn函数内部的getName声明并未使用var、let或者const来声明,所以相当于定义一个全局变量,执行Fn函数相当于此时的getName函数为 
window.getName = function(){
    console.log('CJF1');
}

第四个

getName();  //输出 "CJF1"
//如上面描述的,此时的getName函数已经是Fn函数内部声明的getName方法

前面四个相对简单一点儿,后面的三个可能理解起来比较费力,后面三个跟javascript运算符优先级有很大的关系
此处我只截取MDN上部分优先级别如图所示
一个很有趣的javascript题目_第1张图片

更具体请移步至JavaScript运算符优先级汇总表
第五个

new Fn.getName(); //输出 "CJF2"
//跟运算符的优先级别我们可以看出,.(成员访问)的优先级比new的优先级高,所以此时相当于将Fn的getName属性当作函数构造器来使用

第六个

new Fn().getName();//输出 "CJF3"
//这个可能有两种调用方式 1、(new Fn()).getName() 2、new (Fn().getName)()
//由上面的运算符优先级,我们知道new的优先级比函数调用的优先级高,所以该情况按照第一种方式执行的,new Fn()返回的实例没有getName方法,所有从原型上查找getName方法,最后输出"CJF3"

第七个

new new Fn().getName(); //输出 "CJF3"
//有了第六个情况,这个问题现在就比较清晰了,按照优先级划分的话,就出现如下情况  
new (new Fn().getName)()
//然后括号中的 new Fn().getName  在第六个问题分析中出现,所以执行的顺序是
new ((new Fn()).getName)()

这个题目让人难以理解的就是运算符优先级的问题,可能很多人都不是很清楚,所以我们在自己写代码的时候尽量少使用运算符优先级来操作,理解起来很费劲。为了让自己和其他人一眼就看出你代码的意思,可以使用括号”()”进行分割,这样可以提高代码的可阅读性。

如果有写的不对的,或者理解不到位地方,欢迎各位指正。

你可能感兴趣的:(javascript)