春风得意马蹄疾
一日看尽长安花

原型经典面试题Foo和Foo.getName

题目

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	

运行结果

解释

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,然后得到了一个什么自定义属性都没有实例。

Like
Like Love Haha Wow Sad Angry
赞(2) 打赏
未经允许不得转载:栗子纪blog » 原型经典面试题Foo和Foo.getName
分享到: 更多 (0)
0 0 vote
Article Rating
Subscribe
提醒
guest
0 评论
Inline Feedbacks
View all comments

创作不易,打赏一下作者买瓶洗发露

支付宝扫一扫打赏

微信扫一扫打赏

0
Would love your thoughts, please comment.x
()
x