方法

this引用问题

JavaScript的对象也可以为其定义方法;

方法与函数不同之处在于,方法可以通过this来引用对象中的属性和其他方法;

1
2
3
4
5
6
7
8
9
10
11
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};

xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

在JavaScript中存在一个问题,this具体指向谁需要视情况而定!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getAge() {
var y = new Date().getFullYear();
// 此处的this具体指向谁,视情况而定
return y - this.birth;
}

var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};

// this指向了xiaoming
xiaoming.age(); // 25, 正常结果
// this指向了window
getAge(); // NaN

方法必须通过obj.xxx()进行调用才能确保this引用正常!

1
2
3
var fn = xiaoming.age; // 先拿到xiaoming的age函数
// 这里调用fn()还是window
fn(); // NaN

即使是在对象内部也无法保证能够正常的引用到this引用!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
function getAgeFromBirth() {
var y = new Date().getFullYear();
// 闭包函数,this引用指向了window
return y - this.birth;
}
return getAgeFromBirth();
}
};

xiaoming.age();//NaN

strict模式下函数的this不再指向window,而是指向undefined!

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';

var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};

var fn = xiaoming.age;
fn(); // Uncaught TypeError: Cannot read property 'birth' of undefined

捕获this引用,解决闭包函数this引用问题!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'use strict';

var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
// 在闭包中用that代替this,解决this指向问题
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};

xiaoming.age(); // 25

apply方法修复this指向

函数的this默认是指向window或undefined(strict mode),但是我们可以通过apply来控制其指向;

apply方法接收两个参数,一个指定要绑定的this变量,另一个则是传递给函数的参数数组;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}

var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};

xiaoming.age(); // 25
// 为getAge函数绑定xiaoming变量为this
// 因为getAge不需要参数,所以此处传递一个空数组;
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

call方法与apply方法类似,不同在于,call传递给函数的参数是直接按顺序传入的,而apply则是通过数组进行传递;

1
2
3
// 如果函数不需要绑定this变量,则默认传入null
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

apply和call只有在函数没有绑定this作用域的情况下才能起作用,如果已经绑定了,则绑定的this会被忽略掉。

箭头函数的this作用域绑定了所在语境的作用域,此时重新为其绑定的this将被忽略。

1
2
3
4
5
6
7
8
9
10
var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth仍是1990
// 重新绑定的this无效,仍然是obj
return fn.call({birth:2000}, year);
}
};
obj.getAge(2015); // 25

apply方法实现装饰器

在JavaScript中,所有的对象都是动态的,即使是系统内置的函数,我们也可以对其进行重新赋值!

利用apply方法可以动态修改函数的特点,我们可以对内置函数进行包装,实现装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'use strict';

var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 调用原函数
};

// 每次调用parseInt函数都会执行count+=1
parseInt('10');
parseInt('20');
parseInt('30');
console.log('count = ' + count); // 3