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();//1
getName();//1
结果为1,1
我一开始也觉得离谱,懂了之后就恍然大悟
解析:
1、首先是Foo().getName();这句
拆开来看,先执行Foo()
Foo干了什么事呢?
getName = function () {console.log(1) }
注意这个getName前面没有var,所以这是个全局变量,window下的
然后Foo return了this,这个普通函数的this指向的是window
所以这句话也可以是window.getName()
window.getName()原来是打印4,但是被Foo的1覆盖了,所以是1
2、getName();这句
还是window下的getName,还是1
在JavaScript中,函数的this值取决于函数调用的上下文。对于普通函数(非箭头函数),当函数被调用时,它的this值通常是调用该函数的对象。然而,当函数被直接调用(而不是作为对象的方法调用)时,this值会默认指向全局对象(在浏览器中为window对象)。
提供的代码示例中,函数是直接调用的,没有明确的调用对象。因此,在这种情况下,this值将指向全局对象(在浏览器中为window对象)。
把题稍微改下,还是上面那个题,最后调用函数那两句换成一下:
Foo.getName();//2
getName();//4
输出就是2,4
分析:
Foo.getName()在上面定义好了就是2,也没有被覆盖,因为Foo没有小括号,所以Foo没被调用
下面的getName直接就是4,也没有被覆盖
再把题目稍微改下,还是上面的题,最后调用函数改成:
new Foo().getName();//3
分析:
因为new了一下,所以这个Foo是新的,这个新的Foo身上没有getName,所以去原型链上查找
原型链查找顺序:现在自身查找 -> 构造函数上找 -> 对象原型中找 -> 对象的上一层原型中找
Foo()函数里面的getName = function () {console.log(1)}前面没有this,所以它还是个全局变量,没有挂的元素构造函数Foo()上,所以要去函数原型上找,原型上的是3,所以输出3
接下来看一个新的题:
var o = {
a:10,
b:{
fn:function () {
console.log( this.a )
console.log( this )
}
}
}
o.b.fn()
输出为:
undefined
{ fn: [Function: fn] }
分析:
1、 console.log( this.a )分析
b是一个对象,fn是b里面的函数
fn里面的函数是指向拥有它的对象的,也就是b
但是b里面没有a,所以是undefined
如果b里面有a,那么就打印b里面的a
2、console.log( this )分析
this指向的就是b,b里面就是{ fn: [Function: fn] }
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
,1、首先是Foo.getName();
答案是2,第二个函数,后面也没有什么能覆盖它的,就是2了
2、getName();
不是4就是5,最后一个函数使用的是函数声明的方式,倒数第二个函数使用的是声明变量的方式,声明变量>函数声明,所以两者共存的时候,优先使用声明变量,所以不是5而是4
3、Foo().getName();
注意第一个函数里面的getName前面没有var ,所以是window下的getName,普通函数内部的this指向的就说window,所以这个调用也等同于window.getName(),刚好是1。还有个疑问,window下的,那为什么不是4或者5呢?本来全局作用域下的应该是4的,不是5,说了变量声明>函数声明,但是调用了this.getName()之后,全局getName就被覆盖成1了
4、getName();
这里还是1,刚刚说被覆盖了
5、new Foo.getName();
实际上是new (Foo).getName(),在new Foo的时候,Foo没有getName方法,所以直接调用Foo上的静态方法,结果为2
6、new Foo().getName();
带()的会继承原型链上的方法,5中不带括号的不继承原型链,所以直接输出2,这里直接输出3
new Foo.getName();
这个表达式等同于 (new Foo).getName();。这里,new Foo 创建了一个新的 Foo 实例,但是,我们并没有调用该实例上的 getName 方法。实际上,我们正在访问 Foo 函数上的静态方法 getName,因此我们直接调用了 Foo.getName,输出 2。
new Foo().getName();
在这个表达式中,我们首先使用 new Foo() 创建了一个新的 Foo 实例。这个实例从 Foo.prototype 继承了 getName 方法。因此,当我们调用 new Foo().getName(); 时,我们实际上调用了从 Foo.prototype 继承的 getName 方法,输出 3。
- 代码题: 看代码说结果, 一个关于 this 指向的
var a = 1;
var obj = {
a: 2,
console() {
function fn() {
console.log(this.a);
}
fn();
}
};
obj.console(); // 问输出什么? -- 严格模式是Undefined,非严格模式是1
在此代码中,obj.console()
被调用时,内部的 fn
函数是在全局上下文中被调用的(不是作为对象的方法被调用,而是作为普通函数被调用),所以 this
在 fn
函数内部指向全局对象。
在浏览器环境中,全局对象是 window
,而 var a = 1;
是在全局范围内定义的,所以 this.a
在 fn
中就相当于 window.a
,它的值为 1
。
所以,obj.console();
的输出是 1
。
如果这段代码在严格模式下('use strict';
)执行,this
将会是 undefined
,并且尝试访问 this.a
会抛出一个错误。但在这个提供的非严格模式的示例中,输出是 1
。
为什么带()的继承原型链,不带()不继承?
这个解释与 JavaScript 的运算符优先级有关。在 JavaScript 中,new
关键字的优先级高于成员访问(.
)。因此,表达式 new Foo.getName()
和 new Foo().getName()
会有不同的执行顺序。
new Foo.getName();
这个表达式的解析顺序是这样的:
-
Foo.getName
被评估为 Foo
函数对象上的 getName
方法。
- 使用
new
运算符调用该方法,即 new (Foo.getName)()
。
由于 Foo.getName
已经被定义为输出 2 的函数,所以结果是 2。
new Foo().getName();
这个表达式的解析顺序是:
- 首先,
new Foo()
创建了一个新的 Foo
实例。由于使用了 new
,this
指向新创建的对象,该对象继承自 Foo.prototype
。
- 然后,我们在新对象上调用
getName
方法。由于对象从 Foo.prototype
继承,它将使用在 Foo.prototype
上定义的 getName
方法,即 console.log(3)
,所以结果是 3。
总结:区别在于运算符优先级和表达式的解析顺序。不带括号的版本解析为对 Foo.getName
的 new
调用,而带括号的版本则创建一个新的 Foo
实例,然后在该实例上调用 getName
。这两者的差异导致了不同的输出。