this是在运行时绑定还是编译时绑定?
this是用于访问当前方法所属的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function foo() { console.log(this); }
var obj = { name: "obj", foo: foo }
foo();
obj.foo();
foo.call("hello world")
|
在浏览器中的运行结果:
1 2 3
| Window {window: Window, self: Window, document: document, name: '', location: Location, …} {name: 'obj', foo: ƒ} String {'hello world'}
|
通过这个例子,我们可以知道:
- this和函数定义的位置没有关系,只和调用者有关系
- this是在运行时被绑定的
隐式绑定
1.通过对象调用函数绑定this
在这个例子中obj调用了foo()方法。因此this会隐式地被绑定obj对象上
1 2 3 4 5 6 7 8 9 10
| function foo() { console.log(this); }
var obj = { name: "obj", foo: foo }
obj.foo();
|
运行结果
下面这个例子可能会对初学者带来疑惑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function foo() { console.log(this); }
var obj1 = { name: "obj1", foo: foo }
var obj2 = { name: "obj2", obj1: obj1 }
obj2.obj1.foo();
|
结果是什么呢?是:
1
| { name: 'obj1', foo: [Function: foo] }
|
所以结论是:永远记住,谁直接调用foo()(换言之,谁离foo()更近),那么foo()中的this就指向谁。
显式绑定
显式绑定有两种方法,call 和 bind
call
1 2 3 4 5 6 7
| function foo() { console.log(this); }
foo.call(window); foo.call({name: "obj"}); foo.call(666);
|
运行结果:
1 2 3
| Window {window: Window, self: Window, document: document, name: '', location: Location, …} {name: 'obj'} Number {666}
|
bind
foo()是原函数,bar是新函数(通过bind给bar绑定了调用者)。因此,今后执行bar的时候,函数中的this永远指向obj。
1 2 3 4 5 6 7 8 9 10 11
| function foo() { console.log(this); }
var obj = { name: "obj", }
var bar = foo.bind(obj);
bar();
|
运行结果:
new关键字绑定
常见面试问题:通过new关键字创建一个新对象的步骤是什么?构造函数是如何创建新对象的?
当我们使用new关键字调用函数的时候,会执行如下操作:
- 创建一个空对象
- 空对象的__proto__属性指向构造函数的Prototype属性
- 执行构造函数,如果构造函数中有this,则此this指向刚刚创建的空对象
- 返回刚刚创建的对象
1 2 3 4 5 6 7
| function Student(name) { console.log(this); this.name = name; }
var xiaoming = new Student("xiaoming"); console.log(xiaoming.name);
|
运行结果:
显式、隐式、new绑定的优先级
显式绑定优先级高于隐式绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function foo() { console.log(this); }
var obj1 = { name: "obj1", foo: foo }
var obj2 = { name: "obj2", foo: foo }
obj1.foo(); obj2.foo();
obj1.foo.call(obj2);
|
new 绑定优先级高于隐式绑定
1 2 3 4 5 6 7 8 9 10
| function foo() { console.log(this); }
var obj = { name: "obj", foo: foo }
new obj.foo();
|
new绑定优先级高于显式绑定
1 2 3 4 5 6 7 8 9 10 11
| function foo() { console.log(this); }
var obj = { name: "obj", foo: foo }
var bar = foo.bind(obj); var newObj = new bar();
|
所以,new绑定 > 显式绑定(bind) > 隐式绑定
面试题
面试题1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function foo() { console.log(this); }
var obj1 = { name: "obj1", foo: foo }
var obj2 = { name: "obj2", }
(obj2.foo = obj1.foo)();
|
面试题2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var name = "全局window"
var person = { name: "person", sayName: function() { console.log(this.name); } }
function sayName() { var fun = person.sayName; fun(); person.sayName(); (b = person.sayName) (); }
sayName();
|
答案:
1 2 3 4 5 6 7
| 面试题1 (obj2.foo = obj1.foo)() -> window
面试题2 fun() -> 全局window person.sayName() -> person (b = person.sayName)(); -> 全局window
|