详解js中的call、apply、bind、this

this的指向

默认绑定一般发生在回调函数,函数直接调用;

function test() {
    //严格模式下是undefined
    //非严格模式下是window
    console.log(this);
}
setTimeout(function () {
    //setTimeout的比较特殊
    //严格模式和非严格模式下都是window
    console.log(this);
});

arr.forEach(function () {
    //严格模式下是undefined
    //非严格模式下是window
    console.log(this);
});

隐式绑定:ES5中,this 永远指向最后调用它的那个对象

//例1:
var name = "windowsName";function a() {
    var name = "Cherry";

    console.log(this.name);  // windowsName

    console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this)         // outer: Window
//调用 a 的地方 a(),前面没有调用的对象,那么就是全局对象 window,这就相当于是 window.a();
//注意,这里没有使用严格模式,如果使用严格模式的话,全局对象就是 undefined,
//那么就会报错 Uncaught TypeError: Cannot read property 'name' of undefined。


//例2:
var name = "windowsName";
var a = {
    name: "Cherry",
    fn : function () {
        console.log(this.name); // Cherry
    }
}
a.fn();
//函数 fn 是对象 a 调用的,所以打印的值就是 a 中的 name 的值


//例3:
var name = "windowsName";
var a = {
    name: "Cherry",
    fn : function () {
        console.log(this.name); // Cherry
    }
}
window.a.fn();
//调用它的对象仍然是对象 a。


//例4:
var name = "windowsName";
var a = {
    //name: "Cherry",
    fn : function () {
        console.log(this.name); // undefined
    }
}
window.a.fn();
//最后调用 fn 的对象是 a,就算 a 中没有 name 这个属性,也不会继续向上一个对象寻找 this.name,而是直接输出 undefined


//例5:
var name = "windowsName";
var a = {
    name : null,
    // name: "Cherry",
    fn : function () {
        console.log(this.name);  // windowsName
    }
}

var f = a.fn;
f();
//虽然将 a 对象的 fn 方法赋值给变量 f 了,但是没有调用, 
//由于 “this永远指向最后调用它的那个对象”, f 并没有调用fn(),
// fn()最后仍然是被 window 调用的。所以 this 指向的也就是 window。


// 例6:
var name= "windowsName";
function fn() {
    var name = 'Cherry';
    innerFunction();
    function innerFunction() {
        console.log(this.name);  // windowsName
    }
}
fn()
// fn() 是window调用的,因此this 指向 window
//例7:
var name= "windowsName";

var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(function () {
            this.func1()
        },100);
    }
};

a.func2()     // this.func1 is not a function
//最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数,所以会报错

箭头函数
箭头函数的 this 始终指向函数定义时的 this,而非执行时。
箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

//例8:
var name= "windowsName";

var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(()=>{
            this.func1()
        },100);
    }
};

a.func2()  //Cherry

new 绑定

function test(){
	this.a = 2
	console.log(this)
}

var obj = new Test() //this指向new的实例对象

在函数内部使用 _this = this
先将调用这个函数的对象保存在变量 _this 中,然后在函数中都使用这个 _this,这样 _this 就不会改变了

//例9:
var name= "windowsName";

var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        var _this = this
        setTimeout(function(){
            _this.func1()
        },100);
    }
};

a.func2()  //Cherry
//在 func2 中,首先设置 var _this = this,这里的 this 是调用 func2 的对象 a,
//为了防止在 func2 中的 setTimeout 被 window 调用而导致的在 setTimeout 中的 this 为 window。
//我们将 this(指向变量 a) 赋值给一个变量 _this,
//这样,在 func2 中我们使用 _this 就是指向对象 a 了。

使用 apply、call、bind

const obj1 = {
    name: 'joy',
    getName() {
        console.log(this); 
        console.log(this.name); 
    }
};

const obj2 = {
    name: 'sam'
};

obj1.getName.call(obj2); //obj2 sam
obj1.getName.apply(obj2); //obj2 sam
const fn = obj1.getName.bind(obj2);
fn();//obj2 sam


//例10:使用apply
var name= "windowsName";

var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(function(){
            this.func1()
        }.apply(a),100);
    }
};
a.func2()  //Cherry


//例11:使用call
var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(  function () {
            this.func1()
        }.call(a),100);
    }
};
a.func2()  // Cherry


//例12:使用bind
var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(  function () {
            this.func1()
        }.bind(a)(),100);
    }
};
a.func2()  // Cherry

apply、call、bind 区别

apply 和 call 只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
bind() 是创建了一个新的函数,需要手动去调用

fn.apply(thisArg, argsArray)
参数:
thisArg 在 func 函数运行时使用的 this 值。但是,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。

//例13:
var a ={
    name : "Cherry",
    fn : function (a,b) {
        console.log( a + b)
    }
}

var b = a.fn;
b.apply(a,[1,2])     // 3

fn.call(thisArg, arg1, arg2, …)
参数:arg1, arg2, … 指定的参数列表。

//例14:
var a ={
    name : "Cherry",
    fn : function (a,b) {
        console.log( a + b)
    }
}

var b = a.fn;
b.call(a,1,2) // 3

//例15:
var sData = 'Wisen';
function display() {
  console.log('值是:', this.sData);
}

display.call();  // sData value is Wisen
//调用函数方法时,如果没有传递第一个参数,this 的值将会被绑定为全局对象。
// 在严格模式下,this 的值将会是 undefined。


//例16:
function greet(){
  var animal = this.name + '-----' + this.hobby
  console.log(animal)
}

var obj = {
  name: 'cats',
  hobby: 'palying'
}

greet.call(obj) //cats-----palying
//调用函数方法时,该方法的this值会绑定到 obj 对象。


//例17:
function Procuct(name, price){
  this.name = name
  this.price = price
}

function Food(name, price){
  Procuct.call(this, name, price)
  this.category = 'food'
}

function Toy(name, price){
  Procuct.call(this, name, price)
  this.category = 'toy'
}

var cheese = new Food('feta', 5)
var fun = new Toy('robot', 10)
//console.log(cheese, fun)
//在一个子构造函数中,通过调用父构造函数的 call 方法可以实现继承。

fn.bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

//例18:
var a ={
    name : "Cherry",
    fn : function (a,b) {
        console.log( a + b)
    }
}

var b = a.fn;
b.bind(a,1,2)  //没有输出
b.bind(a,1,2)()  //输出 3。  bind 是创建一个新的函数,因此必须要手动去调用

//例19:
var x = 9 // 全局环境中 this指向window
var module = {
  x: 81,
  getX: function(){ return this.x }
}

module.getX() // 81

var reX = module.getX
reX() //9  函数是在全局作用域中调用的

var boundX = reX.bind(module)
boundX() // 81 创建了新函数,this绑定到了module对象

最后:
这几种this的绑定方式,优先级的先后为:

箭头函数 -> new绑定 -> 显示绑定call/bind/apply -> 隐式绑定 -> 默认绑定

改变this的指向:

  1. 使用 ES6 的箭头函数;
  2. 在函数内部使用 _this = this;
  3. 使用 apply、call、bind;
  4. new 实例化一个对象;

参考:
https://juejin.cn/post/6844903496253177863

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值