在执行上下文的创建阶段, 会分别生成变量对象, 建立作用域链, 确定this指向. this指向是在函数被调用的时候确定的, 也就是执行上下文被创建时确定的. 一个函数中的this指向可以非常灵活.
1 2 3 4 5 6 7 8 9 10 11
| var a = 4; var obj = { a: 5 }
function foo() { console.log(this.a); }
foo(); foo.call(obj);
|
在函数执行过程中, this一旦被确定, 就不可更改.
1 2 3 4 5 6 7 8 9 10 11
| var a = 4; var obj = { a: 5 }
function foo() { this = obj; console.log(this.a); }
foo();
|
全局对象中的this
全局对象对象中的this, 指向它本身.
1 2 3 4 5 6 7 8 9 10 11 12 13
| this.a = 'zero';
var b = 'one';
c = 'two';
console.log(a); console.log(b); console.log(c);
|
函数中的this
1 2 3 4 5
| var a = 10; function bar() { console.log(this.a); }
|
1 2 3 4 5 6 7 8 9
| var a = 10; function bar() { function foo() { console.log(this.a); } foo(); } bar();
|
1 2 3 4 5 6 7 8 9 10 11
| var a = 10; var obj = { a: 50, b: this.a + 10, foo: function () { return this.a; } }; console.log(obj.b); console.log(obj.foo());
|
想要准确确定this指向, 找到函数的调用者以及区分它是否独立调用就变得十分关键. 在一个函数上下文中, this由调用者提供, 由调用函数的方式来决定. 如果调用者函数被某一个对象所拥有, 那么该函数在调用时, 内部的this指向该对象, 如果函数独立调用, 那么该函数内部的this, 则指向undefined. 但在非严格模式中, this指向undefined时, 它会被自动指向全局对象.
1 2 3 4 5 6 7
| function bar() { 'use strict'; console.log(this); } bar(); window.bar();
|
在上面的例子中, bar()
作为独立调用者, 按照定义的理解, 它内部的this指向undefined
. 而window.bar()
因为fn()方法被window对象所拥有, 内部的this指向window对象.
在the third demo
中, 对象obj的b属性使用this.a + 10
来计算. 单独的{}
不会形成新的作用域的, 因此这里的this.a
, 由于没有作用域的限制, 它仍处于全局作用域中, 所以这里的this其实指向的是window对象.
1 2 3 4 5 6 7 8 9 10 11
| var a = 5; var foo = { a: 8, bar: function () { return this.a; } } console.log(foo.bar());
var bingo = foo.bar(); console.log(bingo());
|
foo.bar()
中, bar是调用者, 它不是独立调用, 被对象foo所拥有, 因此它的this指向foo. 而bingo()
作为调用者, 尽管它与foo.bar的引用相同, 但是它是独立调用的, 因此this指向undefined, 在非严格模式中, 自动指向window全局对象.
1 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
| var a = 5; function bar() { return this.a; } var foo = { a: 8, bar: bar }; console.log(foo.bar());
function foo() { console.log(this.a); } function bingo(fn) { fn(); }
var a = 5; var obj = { a: 8, bar: foo };
bingo(obj.bar);
|
call,apply显式指定this指向
JavaScript内部给我们提供两种方法call()
和apply()
, 可以手动设置this的指向. 所有函数都具有这两个方法, 除了参数略有不同, 实现功能完全一样.
1 2 3 4 5 6 7 8 9
| function bar() { console.log(this.a); } var obj = { a: 10 };
bar.call(obj);
|
1 2 3 4 5 6 7 8 9 10 11
| function bar(a, b) { console.log(this.c + a + b); } var obj = { c: 3 };
bar.call(obj, 10, 5); bar.apply(obj,[10,5]);
|
1 2 3 4 5 6 7 8
| function example(a, b, c, d) { console.log(arguments); var arg = [].slice.call(arguments); console.log(arg); } example('zero','one','two','three');
|
1 2 3 4 5 6 7 8 9 10 11
| var bar = { name: 'joker', say: function() { console.log(this.name + 'say hello world'); } }; var foo = { name: 'knight' }; bar.say.call(foo);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
var Person = function (name, age) { this.name = name; this.age = age; this.sex = ['male,female']; }
var Son = function (name, age, hobby) { Person.call(this, name, age); this.hobby = hobby; }
Son.prototype.message = function() { console.log('name: ' + this.name + ', age: ' + this.age + ', sex: ' + this.sex + ', hobby: ' + this.hobby); }
new Son('Mike', 18, 'swim').message();
|
在Son构造函数中使用call()方法将父级构造函数执行了一次, 相当于将Person中的代码在Son构造函数复制了一份, this指向为Son构造函数new出来的实例对象.
1 2 3 4 5 6 7
| var Son = function (name, age, hobby) { this.name = name; this.age = age; this.sex = ['male','female']; this.hobby = hobby; }
|
在向其它执行上下文的传递中, 确保this指向保持不变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
var obj = { a: 8, bar: function () { setTimeout(fucntion () { console.log(this.a); },1000); } }; obj.bar();
var obj = { a: 8, bar: function() { var that = this; setTimeout(fucntion () { console.log(that.a); },1000); } }; obj.bar();
|
1 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
| function bind(fn, obj) { return function () { return fn.apply(obj, arguments); } }
var obj = { a: 8, bar: function () { setTimeout(bind(function () { console.log(this.a); }, this), 1000); } }; obj.bar();
var obj = { a: 8, bar: function () { setTimeout(function () { console.log(this.a); }.bind(this), 1000); } };
|
构造函数与原型方法上的this
1 2 3 4 5 6 7 8 9 10 11
| function Person(name, age) { this.name = name; this.age = age; }
Person.prototype.foo = function () { return this.name; }
var bingo = new Person('Jack', 40); bingo.foo();
|
new操作符调用构造函数会经历是个阶段:
- 创建一个新对象
- 构造函数的this指向这个新对象
- 指向构造函数的代码, 为这个对象添加属性和方法
- 返回新对象
new操作符调用构造函数, this指向这个新建的对象, 然后将新对象返回, 被实例对象bingo
接收.
原型方法上的this, 根据上面例子函数中的this定义, bingo.foo()
中的foo为调用者, 它被bingo对象所拥有, foo中的this指向bingo对象.
参考链接