浅谈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); // ydb2obj1.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方法,实现了给对象增加元素。

谢谢看官。

 

相关文章