# 作用域、this
# this指向总结
普通函数中的this:
- this总是代表它的直接调用者, 例如 obj.func ,那么func中的this就是obj
- 在非严格模式下,没找到直接调用者,则this指的是 window
- 在严格模式下,没有直接调用者的函数中的this是 undefined,es6默认开启了严格模式,所以在这种情况下就是undefined
箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this.
不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,如果第一个参数为空,那么就是 window。
看题
function foo() {
console.log(this.a)
}
var a = 1
foo() // 1
const obj = {
a: 2,
foo: foo
}
obj.foo() // 2
const c = new foo() // undefined
2
3
4
5
6
7
8
9
10
11
12
13
- 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
- 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
- 对于 new 的方式来说,this 被永远绑定在了 c 上面,不会被任何方式改变 this
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // window
2
3
# 优先级
首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。
# 1、下面的输出结果是?
function A(name) {
this.name = name || 'Tom'
this.msg = "use 'this.' set in function"
}
function B() {};
B.prototype = A;
var b = new B();
console.log(b.name); // A,输出的是函数A的名字
console.log(b.msg); // undefined
2
3
4
5
6
7
8
9
10
11
分析:A里面,this.name和this.msg是实例上才会有的属性。B.prototype = A ,A没有经过new,因此不会有name和msg。
b.name返回 A,是因为b上面没有name属性,他就会沿着原型链向上查找,然而 b.proto 为函数A,每一个函数都有一个属性为name,其值是函数的名字。
function abc() { /* 这是一个名为'abc'的函数 */ }
abc.name // 'abc'
2
# 2、分别写出以下答案
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 Foo对象上的getName() ,这里不会是3,因为只有Foo的实例对象才会是3,Foo上面是没有3的
getName(); //-> 4 window上的getName,console.log(5)的那个函数提升后,在console.log(4)的那里被重新赋值
Foo().getName(); //-> 1 在Foo函数中,getName是全局的getName,覆盖后输出 1
getName(); //-> 1 window中getName()
new Foo.getName(); //-> 2 Foo后面不带括号而直接 '.',那么点的优先级会比new的高,所以把 Foo.getName 作为构造函数
new Foo().getName();//-> 3 new Foo()会先执行,此时是Foo的实例,会调用原型上的getName方法
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 3、箭头函数里面的this
箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。作用域是指函数内部,这里的箭头函数,也就是c,所在的作用域其实是最外层的js环境,因为没有其他函数包裹;然后最外层的js环境指向的对象是winodw对象,所以这里的this指向的是window对象。
let a = {
b: function() {
console.log(this)
},
c: () => {
console.log(this)
}
}
a.b() // a
a.c() // window
let d = a.b
d() // window
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4、this判断下面输出为多少?
var name1 = 1;
function test() {
let name1 = 'kin';
let a = {
name1: 'jack',
fn: () => {
var name1 = 'black'
console.log(this.name1)
}
}
return a;
}
test().fn() // ?
2
3
4
5
6
7
8
9
10
11
12
13
14
15
答案: 输出1
因为fn处绑定的是箭头函数,箭头函数并不创建this,它只会从自己的作用域链的上一层继承this。这里它的上一层是test(),非严格模式下test中this值为window。
- 如果在绑定fn的时候使用了function,那么答案会是 'jack'
- 如果第一行的 var 改为了 let,那么答案会是 undefind, 因为let不会挂到window上
# 5、下面程序的输出结果是?
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
2
3
4
5
6
7
8
9
10
11
12
13
14
output:
10
2
2
解析:分析下在method(fn,1)执行时,经历了什么:
首先两个参数fn和1会被放入arguments中,在arguments中第一个参数就是我们传入的函数;接下来fn执行,此时this没有绑定因此指向window,输出10。
然而到了arguments0这一句,相当于把arguments[0]中的第一个参数拿来执行, 效果如下:
arguments[0] () //执行,等同于下面的
arguments.0() //当然这句话是不合法的,但是这样我们可以更清楚知道,this是指向arguments实例本身
2
3
arguments.length就是它本身的长度(arguments是一个类数组,具有length属性),因此输出2。
# 6、判断输出结果,并且解释原因?
var a = 1;
(function a () {
a = 2;
console.log(a);
})();
// 输出结果
ƒ a () {
a = 2;
console.log(a);
}
2
3
4
5
6
7
8
9
10
立即调用的函数表达式(IIFE) 有一个 自己独立的 作用域,如果函数名称与内部变量名称冲突,就会永远执行函数本身;所以上面的结果输出是函数本身;
# 7、下面代码中 a 在什么情况下会打印 1
var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
2
3
4
解法1:利用 toString
let a = {
i: 1,
toString () {
return a.i++
}
}
if(a == 1 && a == 2 && a == 3) {
console.log(1);
}
2
3
4
5
6
7
8
9
10
解法2:利用 valueOf
let a = {
i: 1,
valueOf () {
return a.i++
}
}
if(a == 1 && a == 2 && a == 3) {
console.log(1);
}
2
3
4
5
6
7
8
9
10
解法3:数组这个就有点妖了
var a = [1,2,3];
a.join = a.shift;
if(a == 1 && a == 2 && a == 3) {
console.log(1);
}
2
3
4
5
解法4:ES6的symbol
let a = {
[Symbol.toPrimitive]: (i => () => ++i) (0)
};
if(a == 1 && a == 2 && a == 3) {
console.log(1);
}
2
3
4
5
6
解法5:Object.defineProperty
Object.defineProperty(window, 'a', {
get: function() {
return this.value = this.value ? (this.value += 1) : 1;
}
});
if(a == 1 && a == 2 && a == 3) {
console.log(1);
}
2
3
4
5
6
7
8