this
指向调用它的对象(多个对象时指向它上一级的对象)
- 全局window
- 单纯的函数调用 window(在严格模式中,则是undefined)
- 作为对象的方法:指向当前对象
- 作为构造函数:新创建的对象(实例)
- 内部函数:window,而不是当前对象(用that保存)
- 使用call、apply设置this:将某个函数绑定到某个对象上
1
2
3
4
5
6
7
8
9
10
11
12
13var o={
a: 10,
b: {
fn: function(){
console.log(this.a)
}
}
}
o.b.fn(); // undefined 此时的this应该是b
const j=o.b.fn;
j(); // window
赋值表达式的值是函数本身,this不能得到维持
1 | function a() { |
箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。
this与闭包
this对象是在运行时基于函数的执行环境绑定的。但是匿名函数的执行环境具有全局性,因此this对象通常指向window。
1 | var a={ |
由于每个函数在调用的时候会自动取得两个特殊的变量:this、arguments,只会搜索到其活动对象为止,因此永远不可能访问外部函数中的这两个变量。可以把外部作用域中的this对象保存在一个闭包能够访问到的变量里
call、apply、bind
在JavaScript中,call、apply和bind是Function对象自带的三个方法,都是为了改变某个函数运行时的上下文(context)而存在的,就是改变函数体内部 this 的指向。
在特定的作用域中调用函数
- apply 、 call 、bind 三者第一个参数都是 this 要指向的对象,也就是想指定的上下文
- apply 、 call 、bind 三者都可以利用后续参数传参
- bind 是返回对应 函数,便于稍后调用;apply 、call 则是立即调用
- call
1 | objA.call(objB, arg1, arg2, arg3) |
- apply
1 | objA.apply(objB,[arg1, arg2, arg3]) |
- 第一个参数都是你想指向的上下文,第二个参数call 需要按顺序传递进去,而 apply 则是放在数组里
- 若call、apply的第一个参数是null/空,则this指向window
- call、apply是改变上下文this并立即执行改变的函数、bind可以让函数想什么时候调用就什么时候,可以再执行时再添加参数
- bind
bind()方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16this.num = 9;
var mymodule = {
num: 81,
getNum: function() {
console.log(this.num);
}
};
mymodule.getNum(); // 81
var getNum = mymodule.getNum;
getNum();
// 9 因为在这个例子中,"this"指向全局对象
var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81
应用场景
其他对象可以利用数组/对象原型上面的方法
- 数组之间追加
1 | var array1 = [12 , "foo" , {name:"Joe"} , -2458]; |
- 获取数组中的最大值和最小值
1 | var numbers = [5, 458 , 120 , -215 ]; |
- 定义一个 log 方法,让它可以代理 console.log 方法
1 | function log(msg) { |
可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的
1 | function log(){ |
给每一个 log 消息添加一个”(app)”的前辍
- arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift
1 | function log(){ |
模拟实现
call
- 判断传入的对象是否是null/undefined,是的话对象转为window对象
- 为传入的对象创建一个新方法指向调用call的函数this,执行函数传入参数获取返回值,删除创建的方法,返回返回值
改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Function.prototype.myCall = function (context) {
var context = context || window
// 给 context 添加一个属性
// getValue.call(a, 'yck', '24') => a.fn = getValue
context.fn = this
// 将 context 后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a.fn('yck', '24')
var result = context.fn(...args)
// 删除 fn
delete context.fn
return result
}
apply
1 | Function.prototype.myApply = function (context) { |
bind
bind 和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind 实现柯里化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效(也不指向window),但传入的参数依然生效