浅谈js中的this,call,apply

什么是this?

简单点来说JavaScript中的this总是指向一个对象,至于这个对象是什么,就有很多情况了。

出去with和eval不常用的情况,实际应用中,this的指向大致有这四种分类:

1.作为对象的方法调用

var obj = {
    name: 'ydb',
    sayName: function(){
        console.log(this.name);
    }
}
obj.sayName();

2.作为普通函数调用

var obj = {
    name: 'ydb',
    sayName: function(){
        console.log(this.name);
    }
}
var sayName = obj.sayName;
sayName();

这里全局的sayName就是一个普通函数,谁调用this就指向谁(在这里浏览器中指向window),输出undefined

3.构造器调用

class Perople {
    constructor(name){
        this.name = name;
    }
    sayName(){
        console.log(this.name);
    }
}
var person1 = new Perople('ydb');

4.Function.prototype.call和Function.prototype.apply调用

var obj1 = {
    name: 'ydb',
    sayName: function(){
        console.log(this.name);
    }
}
var obj2 = {name: 'ydb2'};
var obj3 = {name: 'ydb3'};
obj1.sayName.call(obj2); // ydb2
obj1.sayName.apply(obj3); // ydb3

上面就是this常用的四种方法。

关于this我们总是会遇到this指向不正确的问题,比如:

var getID = document.getElementById;
console.log(getID('div1'));

假如我们有一个id属性为div1的dom元素,我们这样子获取,在有些浏览器引擎中是会抛出异常的,因为这个方法里面内部用到了this,它希望的this是docuemnt这个对象,我们现在把getID执行,就把这个函数作为普通函数调用了,其内部的this已经指向window了,所以会抛出异常。

我们使用call或者apply来修复这个this,代码如下:

document.getElementById = (function(func){
    return function() {
        return func.apply(document,arguments);
    }
})(document.getElementById)
var getID = document.getElementById;
console.log(getID('div1'));

好了,现在我们就修复了this,这里用到了高阶函数,不做额外的扩展。

既然call,apply很常用我们就来说说他们之间的区别:

他们都能改变函数执行this的指向,还可以传入额外的参数,但是他们之间的传参是有区别的,看如下代码:

console.log(Math.max.apply(null,[1,2,3]));
console.log(Math.max.call(null,1,2,8));
在这里我们使用内置对象Math的max方法,求最大值,不需要改变this,所以传入null(this指向默认的宿主对象),后面的参数就是这两个方法掺入的额外参数。
我们看见apply是可以数组方法传参的,call只能一个一个参数列出来传入。
 
相信你们应该见过还有一个实现this指向改变的方法,就是Function.prototype.bind
有了apply和call我们可以很简单的实现一个bind,代码如下:
Function.prototype.bind = function(ctx){
    var _that = this; //保存原函数的引用
    return function() {
       return  _that.apply(ctx,arguments);
    }
}

看看是不是很简单。还可以根据自己的需求扩展这个bind方法,但是这里是为了演示,最好不要给js的内置对象的属性做修改,避免出现全局的错误,想要好的实现,可以参考vue源码中对数组变化监听的实现。这里也用到了高阶函数,所以说高阶函数是走向高级程序员的必经之路(虽然我是一个小彩笔)。

既然有了改变this指向的方法,那我们可以调用其他对象的方法实现很多功能,举一个例子,假如一个对象满足下面两个条件,那么就可以实现这么一个功能。

1.对象本身要可以存取属性

2.对象的length属性可读写

实现的功能如下:

var obj = {
    length: 0
}
Array.prototype.push.call(obj,'a');
Array.prototype.push.call(obj,'b');
console.log(obj[0]);
console.log(obj[1]);
console.log(obj.length);

我们知道数组的push方法是增加一个元素,那么我们这里借用数据的push方法,实现了给对象增加元素。

谢谢看官。

原文地址:https://www.cnblogs.com/jsydb/p/12513179.html