题目如下:
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();getName();Foo().getName();getName();new Foo.getName()new Foo().getName()new new Foo().getName()// 输出结果为// 2// 4// 1// 1// 2// 3// 3复制代码
下面对输出值进行分析:
-
Foo.getName()
输出为 2, 访问的是函数 Foo 上的静态属性,输出为 2。现在,尝试在函数内定义 getName 函数和在 Foo 原型上绑定 getName 函数, 都无法成功执行
Foo.getName()
, 而以字面量创建对象的方式创建对象后,则能正常的执行getName()
函数。
通过创建对象运行 getName()
定义在函数内部无法正常执行,即使它是全局变量。
结论: 由于函数本身是对象,通过函数绑定属性和方法属于静态方法 ,可以直接调用。绑定在原型上的属性和方法要创建对象后才能调用,在构造函数对象内部定义的方法无法通过对象调用。
-
getName();
结果输出为 4,而不是输出 5。这是因为JS 存在变量声明提升(所有声明的变量或声明的函数都会被提升到当前函数的顶部)。故代码执行顺序为:
var getName;function getName() { alert(5); }// ... 省略代码getName = function () { alert(4) }复制代码
最终执行
getName
输出为 4延伸题目:
console.log( Foo )function Foo() { console.log(1);}var Foo = 1复制代码
Foo 的输出结果为?
-
Foo().getName();
输出值为1, 先执行 Foo() 函数,定义全局变量 getName, 之后调用全局对象的getName()
方法, 返回 1。注意,
Foo()
函数返回的this
指向的是全局对象 window,所以调用的是全局对象getName()
。 函数里的 getName 绑定的是全局对象,通过 Foo 调用会报错。Node 下执行这条语句会报错,因为 node 没有全局对象 window, 所以无法调用 getName
-
getName()
调用全局函数, 因为执行Foo()
, 更新了 getName 的值,所以返回 1。 -
new Foo.getName()
考察了运算符的优先级。.
的优先级高于 new, 相当于执行new (Foo.getName)()
, 相当于执行 getName 的构造函数,返回 2 -
new Foo().getName()
执行方式为(new Foo()).getName()
先生成 Foo 对象, 再执行getName()
函数。 在new Foo()
返回的是新创建的空对象,由于对象这时还没绑定属性 getName, 所以这时调用的是原型上的 getName, 结果返回3注意: 构造函数
return this
,在执行 new 的时候,返回的是新创建的对象。延伸题目:
function A() { this.a = 2; function B() { this.a = 1; } return B();}console.log(new A());复制代码
a的值是? 如果
return new B();
a的值是? -
new new Foo().getName()
可以改写为new ((new Foo()).getName)()
先初始化实例,然后将原型对象上的 getName() 作为构造函数执行,结果返回 3
最终代码可以优化为
var getName;function getName() { alert(5); }function Foo() { getName = function() { alert(1); } return this}Foo.getName = function() { alert(2); }Foo.prototype.getName = function() { alert(3); }getName = function () { alert(4); }Foo.getName(); // 2getName(); // 4Foo();getName(); // 1getName(); // 1Foo.getName(); // 2(new Foo()).getName(); // 3(new Foo()).getName(); // 3复制代码