this
1. 全局范围内使用this
,它会指向全局对象
浏览器中的全局对象为Window
2. 函数调用时,this
会指向全局对象
注意:在对象里调用函数(不是方法),this
也会指向全局对象,这会导致一些奇怪的问题
ECMAScript5
严格模式下不存在全局对象,因此this
是undefined
3. 方法调用时,this
指向test
对象
4. 调用构造函数时,在函数内部,this
指向新创建的对象
通过new
关键字方式调用的函数都被认为是构造函数
5. 显式的设置this
即使用Function.prototype
中的call
、apply
方法时,函数内部的this
指向call/apply的第一个参数
1
2
3
4
5
6
7
8
9
| function foo() {
consoloe.log(this.bar);
}
var fooo = {
bar: 'hello'
};
foo.apply(fooo);
foo.call(fooo);
|
常见错误
由于第二个规则,在对象方法内调用函数会使函数内部的this
指向全局对象,所以下面代码的结果不是预期想要的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| var Foo;
Foo = (function() {
function Foo() {
this.bar = 'hello';
}
Foo.prototype.method = function() {
function test() {
console.log(this.bar);
}
test(); // undefined, except 'hello'
console.log(this.bar); // 'hello'
};
return Foo;
})();
var foo = new Foo;
foo.method();
|
解决方法一:可以先获取对this
的引用
1
2
3
4
5
6
7
8
| Foo.prototype.method = function() {
var that = this;
function test() {
console.log(that.bar);
}
test(); // 'hello'
console.log(this.bar); // 'hello'
};
|
解决方法二:CoffeeScript中的fat arrow =>
1
2
3
4
5
6
7
8
9
10
| Foo.prototype.method = function() {
var test;
test = (function(_this) {
return function() {
console.log(_this.bar);
};
})(this);
test(); // 'hello'
console.log(this.bar); // 'hello'
};
|
原理相似,只不过用了IIFE而已
另外一个奇怪的地方就是当一个方法赋值给一个变量的时候:
1
2
| var test = foo.method;
test();
|
此处的test
是被当作普通函数调用的,因此函数内部的this
不是指向foo
对象的!
解决方法很简单,经常能够看见:
1
2
3
4
5
| test.call(foo); // or apply
// 常用
var hasProp = {}.hasProperty;
hasProp.call(foo, bar);
|
作用域与命名空间
JavaScript不支持块级作用域
虽然语法类似与C/C++,但是JS使用的是函数作用域
,理解这一点非常重要
函数声明与表达式的不同
函数声明会被提前,所以像下面这段常识中不可正常运行的代码其实是能够正常运行的
1
2
3
4
| foo(); // 'hello world'
function foo() {
console.log("Hello world");
}
|
但是!如果是函数赋值表达式就不同了
1
2
3
4
5
| foo; // 'undefined'
foo(); // 出错:TypeError
var foo = function() {
console.log("hello world");
};
|
上面这段代码其实相当于这段:
1
2
3
4
| var foo;
foo; // undefined
foo(); // TypeError
foo = function() {};
|
提升规则
声明变量(还有函数声明)会被提到当前作用域的最前端,在运行前解析,但是赋值却是在后面完成的,即在执行时执行
Keyword: hoisting
由于提升规则的存在,有时在不注意间会导致匪夷所思的结果:
1
2
3
4
| var foo = 'bar';
(function() {
console.log(foo); // 'bar'
})();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| var foo = 'bar';
(function() {
console.log(foo); // undefined
var foo = 'inner';
})();
/* actual code */
var foo = 'bar';
(function() {
var foo;
console.log(foo);
foo = 'inner';
})();
|