this、call、apply、bind
this
在JavaScript中,this的指向是调用时决定的,而不是创建时决定的,this永远指向最后调用它的那个对象。
全局上下文
在全局上下文中,this指全局对象
- this等价于window对象
- var === this. === window.
在浏览器里面this等价于window对象,如果你声明一些全局变量,这些变量都会作为this的属性。
函数上下文
在函数内部,this的值取决于函数被调用的方式
直接调用
this指向window,严格模式下是undefined作为对象的一个方法
当this所在的函数被以obj.fn()形式调用时,指向objcall()、apply,(不同点:前者传的是若干个参数列表,后者是包含多个参数的数组)
this指向绑定的对象1
2
3
4
5
6
7
8
9let Person = {
name: 'YoLin',
age: 25
}
function say(job) {
console.log(`我是${this.name},今天{this.age},职业是${job}`)
}
say.call(Person, "FE")
say.apply(Person, ["FE"])bind()
this将永久地被绑定到了bind的第一个参数。
与call、apply相似,接受若干个参数列表并返回一个新的函数,不同的是需要我们手动调用。箭头函数
所有的箭头函数都没有自己的this,都指向外层。
箭头函数会捕获其所在上下文的this值,作为自己的this值。
箭头函数的 this 始终指向函数定义时的 this,而非执行时
箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。作为一个构造函数(new实例化一个对象)
this被绑定到正在构造的新对象。
通过构造函数创建一个对象其实执行这样几个步骤:
1、创建新对象
2、将this指向这个对象
3、给对象赋值(属性、方法)
4、返回thisnew的过程
1
2
3
4
5
6
7
8var a = new myFunction("Li","Cherry");
new myFunction{
var obj = {};
obj.__proto__ = myFunction.prototype;
var result = myFunction.call(obj,"Li","Cherry");
return typeof result === 'obj'? result : obj;
}1、创建一个空对象 obj;
2、将新创建的空对象的隐式原型指向其构造函数的显示原型。
3、使用 call 改变 this 的指向
4、如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。
总结
如果要判断一个函数的this绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this的绑定对象:
- 由new调用:绑定到新创建的对象
- 由call或apply、bind调用:绑定到指定的对象
- 由上下文对象调用:绑定到上下文对象
- 默认:全局对象
注意:箭头函数不使用上面的绑定规则,根据外层作用域来决定this,继承外层函数调用的this绑定。
call
手写call的思路:
- 1、将函数设置为对象的一个属性
- 2、执行该函数
- 3、删除该函数
通过Arguments 对象中取值,然后放入一个数组里把这个参数数组放到要执行的函数的参数里面去1
2
3
4
5
6
7
8
9
10
11
12
13
14// 以上个例子为例,此时的arguments为:
// arguments = {
// 0: foo,
// 1: 'kevin',
// 2: 18,
// length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 执行后 args为 [foo, 'kevin', 18]
eval() 函数会将传入的字符串当做 JavaScript 代码进行执行
1 |
|
这里 args 会自动调用 Array.toString() 这个方法。
最终代码
1 |
|
apply与call类似,只是传入的参数是一个数组
1 |
|
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
- 返回一个函数
- 可以传入参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
return new Error('Function.prototype.bind - what is trying to be bound is not callable')
}
var args = Array.prototype.slice.call(arguments,1)
var self = this
var fNOP = function() {}
var fBound = function() {
var bingArgs = Array.prototype.slice.call(arguments)
self.apply(this instanceof fNOP.prototype ? this : context, args.concat(bindArgs))
}
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound
}
类数组
具备与数组特征类似的对象
1 |
|
获取 DOM 节点的方法
方法中的 arguments 都是类数组
可以通过for循环进行遍历,类数组无法使用 forEach、splice、push 等数组原型链上的方法
call的使用场景:
- 对象的继承
1
2
3
4function Parent() {}
function Child() {
Parent.call(this)
} - 借用方法
类数组想用使用数组的方法apply的一些妙用:1
let domNodes = Array.prototype.slice.call(arr)
1、获取数组中数字最大最小值2、合并两个数组1
2Math.max.apply(null, arr)
Math.min.apply(null, arr)1
Array.prototype.push.apply(arr1,arr2)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!