js的this理解以及call apply bind改变this指向
关于JavaScript中的this
1. 函数预编译过程this - > window
function foo() {
console.log(this);
}
foo(); //全局变量
函数调用中的this也指向全局变量。
注:ECMAScript5的strict模式不存在全局变量,这里的this是undefined。
2. 全局作用域里this -> window
console.log(this); //全局变量
全局范围使用this指向的是全局变量,浏览器环境下就是window。
注:ECMAScript5的strict模式不存在全局变量,这里的this是undefined。
3. 对象方法调用
var test = {
foo: function () {
console.log(this);
}
}
test.foo(); //test对象
对象方法调用中,this指向调用者。
4. 构造函数调用
如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。使用new来调用函数, 或者说发生构造函数调用时,会自动执行下面的操作。
- 在函数体最前面隐式的创建一个var this=Object.create(foo.prototype)的空对象
- 执行this.xx= xx;往this里面增加属性方法
- 隐式的返回this
function foo(x){
this.x = x;
console.log(this); // foo {x: 2}
}
var f = new foo(2);
console.log(f.x); // 2
使用new来调用foo()时, 会构造一个新对象并把它绑定到foo()调用中的this上。即构造函数的this指向它实例化出来的对象。
5. call apply bind改变的this
function foo(a, b) {
console.log(this);
}
var bar = {
};
foo.apply(bar, [1, 2]); //bar
foo.call(1, 2); //Number对象
使用Function.prototype的call或者apply方法是,函数内部this会被设置为传入的第一个参数。call与apply后面如果是undefined和null,this指向的是window
6. 元素绑定事件,事件触发后 执行的函数中的this 指向的是当前的元素
<input type="button" value=>
<script>
document.querySelector("input").onclick = function(){
console.log(this); //指向当前按钮
};
</script>
改变this指向
在ECMAScript中,每个函数都包含两个继承而来的方法:apply() 和 call(),这两个方法的用途都是在特定的作用域中调用函数,主要作用跟bind一样,用来改变函数体内this的指向,或者说是在函数调用时改变上下文。
1. call和apply
apply() 和 call(), 这两个方法的用途都是在特定的作用域中调用函数,主要作用跟 bind 一样,用来改变函数体内 this 的指向,或者说是在函数调用时改变上下文。
我们可以运用这个特性实现借用别的函数实现自己的功能如下代码:
function Person(name, age, sex) {
this. name = name;
this.age = age;
this.sex = sex;
}
function Student ( name, age, sex, tel, grade) {
// Person.call(this,name, age ,sex) ;
Person.apply(this,[name, age ,sex]) ;
this.tel = tel;
this.grade = grade;
}
var student = new Student( 'sunny', 123, 'male', 139, 2017)
console.log(student.name)//sunny
我们可以看出来,原本Student构造函数中是没有name属性的,我们call和apply都是为了动态改变 this 而出现的,
再来个常见的使用例子:数组没有max方法,可以使用Math对象上的max方法
const arr =[1,2,3,4,5]
const max = Math.max.apply(null,arr)
console.log(max)//5
call
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
fun.call(thisArg,arg1,arg,…)
- thisArg是fun函数运行时指定的this值(在非严格模式下,指定为null或undefined时会自动指向全局对象)
- arg1.arg2…是指定的参数列表
apply
fun.apply(thisArg,[argsArray])
- thisArg是fun函数运行时指定的this值(在非严格模式下,指定为null或undefined时会自动指向全局对象)
- argsArray是一个数组或类数组对象, 数组中的元素将作为单独的参数传给fun函数如果
- argsArray为nul或undefined时表示不需要传入任何参数。
2. bind
fun.bind(thisArg,arg1,arg2,…).
- thisArg当绑定函数被调用时,该参数会作为原函数运行时的this指向。当使用new操作符调用绑定函数时,该参数无效。
- arg1, arg2, …当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。//如果你某个函数绑定新的this指向并且固定先传入几个变量可以在绑定的时候就传入,之后.调用新函数传入的参数都会排在之后
const obj= {
}
function test(..args) {
console.log(args);
const newFn = test.bind(obj, 1,2);
newFn(3,4);
// [1,2,3,4]
3. call apply bind总结
直接看看三者之间的关系吧
//先来一个对象big吧
var big = {
name:'BaBa',
saying:function (age){
console.log(this.name,'age:',age);
}
};
//再来一个small对象
var small= {
name:'ErZi'
};
//如果想调用big的saying方法来说出‘Erzi’:
//使用bind方法
big.saying.bind(small)(20);//打印结果为ErZi age: 20
//使用call方法
big.saying.call(small,20);//打印结果为ErZi age: 20
//使用apply方法
big.saying.apply(small,[20]);//打印结果为ErZi age: 20
总结三者
1. 使用场景
- 当一个函数需要改变this指向时且参数较少时可以使用call
- 如果参数较多,可以将参数整理到一个数组,使用apply
- 如果想生成一个新函数长期绑定某个对象时,可以使用bind
2. 相同点
都是用来改变this指向
3. 不同点
- call和apply的用法机乎相同,唯一不同就是参数不同。 call职能一 个个参数传入, apply参
数是一个数组。 - bind的传参方式和call相同,但是改变调用函数的指向并返回一个新的函数,之后再调用这个
函数时候this就指向bind绑定的第一个参数
拓展:自己封装bind方法
//bind方法
//1、bind方法放在函数的原型中
// -->fn.__proto__===fn的构造函数.prototype
// -->所有的函数对象的构造函数是Function
// -->Function 创建了Function
// -->Function 创建了Object
// -->Function 创建了fn
Function.prototype._bind=function(target){
//这里的this其实fn
//target表示新函数的内部的this的值
//利用闭包创建一个内部函数,返回那个所谓的新函数
return ()=>{
//执行fn里面的逻辑
this.call(target); //this.apply(target)
}
// //等价于:
// var _that=this;
// return function(){
// _that.call(target);
// }
}
function fn(){
console.log(this);
}
var _f1=fn.bind({
age:18})