题目
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
//变量提升后
function Foo(){
getName=function(){
console.log(1)
}
return this;
}
var getName;
function getName(){console.log(5)}
Foo.getName=function(){console.log(2)}
Foo.prototype.getName=function(){console.log(3)}
getName=function(){console.log(4)}
//输出结果
Foo.getName()//2
getName()//4
Foo().getName()//1
getName()//1
new Foo.getName()//2
new Foo().getName()//3
new new Foo().getName()//3
运行结果
image解释
1、 Foo.getName()
为什么输出2,不是3?这就得说说构造函数的静态属性与实例属性。 我们都知道函数属于对象,而对象拥有可以自由添加属性的特性,函数也不例外,构造函数也是函数:
function Fn() {};
Fn.person = '听风是风';
Fn.sayName = function () {
console.log(this.person);
};
Fn.sayName(); // 听风是风
比如这个例子中,我为构造函数Fn添加了静态属性person与静态方法sayName,我们可以通过构造函数Fn直接访问。在JS中,我们将绑定在构造函数自身上的属性方法称为静态成员,静态成员可通过构造函数自身访问,而实例无法访问。
let people = new Fn();
people.sayName();// 报错,实例无法访问构造函数的静态属性/方法
那有什么属性是实例可以访问而构造函数自身无法访问的呢,当然有,比如实例属性。这里我将实例属性细分为构造器属性与原型属性两种,看下面的例子:
function Fn() {
// 构造器属性
this.name = '听风是风';
this.age = 26;
};
// 原型属性
Fn.prototype.sayName = function () {
console.log(this.name);
};
let people = new Fn();
people.sayName(); // 听风是风
2、 getName()
这里考的是变量提升与函数声明提升 ,function getName()变量提升,声明后被 getName= function(){...}覆盖,输出4
3、 Foo().getName()
getName是全局变量,所以在函数Foo内也能直接访问,于是getName被修改成了输出1的函数,之后返回了一个this。
由于Foo().getName()等同于window.Foo().getName(),所以this指向window,这里返回的this其实就是window。
4、 getName()
这里输出1已经毫无悬念,上一分析中,getName的值在Foo执行时被修改了,所以再调用getName一样等同于window.getName(),同样是输出1。
5、 new Foo.getName()
等价于new (Foo.getName()),在分析一中我们已经知道了Foo.getName是Foo的静态方法,这里的getName虽然是Foo的静态方法,但是既没有继承Foo的原型,自身内部也没提供任何构造器属性(this.name这样的),所以new这个静态方法只能得到一个空属性的实例。
因此这里new的过程就相当于单纯把Foo.getName执行了一遍输出2,然后返回了一个空的实例
6、new Foo().getName()
这里考了new基本概念,首先这个调用分为两步,第一步new Foo()得到一个实例,第二步调用实例的getName方法
7、new new Foo().getName()
第一眼也是看的我很懵,我们知道new一个函数都是new fn(),函数带括号的。所以这里其实可以拆分成这样:
var a = new Foo();
new a.getName();
由于构造函数Foo自身啥构造器属性都没有,只有原型上有一个输出3的原型方法,所以实例a是一个原型上有输出3的函数getName。
那么第二步,由于原型上的getName方法也没提供构造器属性,自身原型上也没属性,所以第二步也算是单纯执行a.getName()输出3,然后得到了一个什么自定义属性都没有实例。
网友评论