首先看一道网易的面试题:

var a = {a:"haha",getA:function(){console.log(this.a);}
}
var b = {a:"hello"}
var getA = a.getA;
var getA2 = getA.bind(a);
function run(fn){fn();
}
//分别输出
a.getA();//haha
getA();//window下面的a对象
run(a.getA);//window下面的a对象
getA2.call(b);//haha

  这里考察了三个点:形参实参的理解、this的指向、call和bind对this指向的影响。

一、this指向问题

  关于this指向的问题,上一篇:理解JavaScript里this关键字有比较好的总结。

  (1)这里有三种简单情况:

  1、如果函数中的this没有调用它的对象,那么this指向的就是window(注意:严格模式下这种情况的this会为空,即undefined

  2、如果函数中的this被不包含子对象的对象所调用,那么this指向的就是调用它的对象。

  3、如果函数中的this被包含多级对象的对象调用,this指向的也只是它上一级的对象,如下例:

var demoObj = {a:1,b:{fun:function(){console.log(this.a); }}
}
demoObj.b.fun();//undefined,因为this指向demoObj.b,b里面没有a

  这里this不是指向demoObj对象,而是指向demoObj.b对象,这里找不到demoObj.b对象里的a,所以会输出undefined。 

  (2)还有三种特殊情况:

  1、还是上面的例子,改一下调用函数的方式,如下。

var demoObj = {a:1,b:{fun:function(){console.log(this.a); }}
}
var newFun = demoObj.b.fun;
newFun();//undefined,this指向window

  这里还是得到undefined,但是this的指向却是window,这里的undefined是因为没找到window对象里的a,才输出的undefined。虽然函数fun是被对象b所调用,但是在将fun赋值给变量newFun的时候并没有执行,newFun的上级对象window,所以最终执行时指向的是window。

  2、构造函数用new实例对象时对this的影响。

function Fun(){this.name = "haha";
}
var stu = new Fun();
console.log(stu.name); //haha

  这里之所以对象stu.name可以输出haha,是因为new关键字就是创建一个对象实例,这个stu对象中包含了this.name这个属性,相当于复制但却没有执行。在执行时调用这个函数Fun的是对象stu,所以this指向的就是对象stu。

  用new操作符创建对象时发生的事情:

  第一步: 创建一个Object对象实例。
  第二步: 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
  第三步: 执行构造函数中的代码(这里的执行并不是真的让this指向哪里,而是为这个新对象添加属性)。
  第四步: 返回新生成的对象实例

  原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要。

  3、有return的函数在new时对this的影响(正常的构造函数是没有return语句),我们先看下面的几个例子。

//例1
function Fun()
{  this.name = 'haha';  return {};
}
var stu = new Fun();
console.log(stu.name); //undefined,stu为{}

//例2
function Fun()
{  this.name = 'haha';  return function(){};
}
var stu = new Fun();
console.log(stu.name); //undefined

//例3
function Fun()
{  this.name = 'haha';  return 123;
}
var stu = new Fun();
console.log(stu.name); //haha,stu为Fun的实例

//例4
function Fun()
{  this.name = 'haha';  return undefined;
}
var stu = new Fun();
console.log(stu.name); //haha

  可以看出:如果return的是一个对象,那么this会指向返回的对象,如果return的不是一个对象,那么this还是指向函数的实例。

  但是return的是null时比较特殊。虽然null也是对象,但是this还是指向函数的实例。

//例5
function Fun()
{  this.name = 'haha';  return null;
}
var stu = new Fun();
console.log(stu.name); //haha

二、call、bind、apply对this指向的影响

  call和apply只有参数不同,这里就只讨论call,因为call和bind参数使用方法是一样的。

  1、call是动态的改变this的指向,即换个对象执行原对象方法的方法,并立即执行;

  2、bind是静态改变this的指向,并返回一个修改后的函数。

  就拿开始的题目最后一个输出来说:

  如果只是使用call的话:在执行到这两句时动态改变了this的指向,所以call(b)的输出hello,call(a)的输出haha。

getA.call(b);//hello
getA.call(a);//haha

  接下来看有bind影响的:

var getA2 = getA.bind(a);

  这里getA其实是a.getA,那么getA.bind(a)将this指向a,其实还是返回了a.getA函数赋值给了getA2。注意:其实函数没有变化,但是内部已经将this指向了a

getA2.call(b);//haha
//相当于a.getA.call(b);

  此时无论call里是a还是b,都会输出haha,因为内部的this已经被bind绑定指向bind里面的a了。怎么理解呢,看下面示例:

var getA2 = getA.bind(a);
var getA3 = getA.bind(b);
getA2.call(b);//haha
getA3.call(b);//hellovar getA2 = getA.bind(a);
var getA3 = getA2.bind(b);
getA2.call(b);//haha
getA3.call(b);//haha

  使用bind静态指定this:第一次使用bind后,this就被固定为bind的参数了,call、apply、bind均无法改变。

  如果new一下,this就是指向当前构造函数的实例;其他情况,this一直被静态绑定为a。

var a = {a:"haha",getA:function(){this.c = "12345678";console.log(this,this.a);}
}
var b = {a:"hello"}
var getA = a.getA;
var getA2 = getA.bind(a);
var getA3 = getA2.bind(b);
var getObj = new getA3();//new一下,this就是当前构造函数的实例
getA3();//其他情况,this一直被静态绑定为a

  总的来说,call方法是在调用时改变this并立即执行这个函数,bind方法可以先改变函数中的this,之后对应的函数可以在需要的时候再调用。

三、call、bind、apply用法:

  1、fun.apply(context,[argsArray])

  立即调用fun,同时将fun函数原来的this指向传入的新context对象,实现同一个方法在不同对象上重复使用

  context:传入的对象,替代fun函数原来的this;

  argsArray:一个数组或者类数组对象,其中的数组参数会被展开作为单独的实参传给 fun 函数,需要注意参数的顺序。

  2、fun.call(context,[arg1],[arg2],[…])

  同apply,只是参数列表不同,call的参数需要分开一个一个传入。如果不知道参数个数,则使用apply。

  使用:

Math.max()    //只接收单独的参数,通过下面的方法可以在数组上面使用max方法:
Math.max.apply(null, array);    //会将array数组参数展开成单独的参数再传入
Array.prototype.push.apply(arr1,arr2);    //将一个数组拆开push到另一个数组中;不用apply则会将后续数组参数当成一个元素push进去。
Array.prototype.slice.call(arguments);    //在类素组对象上使用slice方法
function isArray(obj){return Object.prototype.toString.call(obj) === '[object Array]' ;
}    //验证是否是数组

  3、fun.bind(context,[arg1],[arg2],[…])

  使fun方法执行的context永不变。静态指定this

  arg1:要传递到新函数的参数列表

  返回一个函数供后续调用,其函数体和原函数fun一样,但新函数的this指向新传入的context对象。新函数会具有bind方法指定的初始参数arg1/arg2...,后续调用新函数时的实参要往已有参数的后面排。就是科里化方式,绑定默认的参数,后面的参数可以变化。

  使用:

//原来的函数有4个参数
var displayArgs = function (val1, val2, val3, val4) {console.log(val1 + " " + val2 + " " + val3 + " " + val4);
}
var emptyObject = {};
// 生成新函数时bind方法指定了2个参数,则新函数会带着这个两个实参
var displayArgs2 = displayArgs.bind(emptyObject, 12, "a");
// 调用时传入另2个参数,要在bind方法传入的2个实参后面
displayArgs2("b", "c");
// Output: 12 a b c

  bind的参数可以在执行的时候再次添加,但是要注意的是,参数需要按照形参的顺序添加

var demoObj = {name:"haha",fun:function(a,b,c){console.log(a,b,c);}
}
var newFun = demoObj.fun;
var newFun2 = newFun.bind(demoObj,5);
newFun2(7,9);//5,7,9

转载于:https://www.cnblogs.com/goloving/p/7259988.html

深入理解this和call、bind、apply对this的影响及用法相关推荐

  1. 如何理解“跳出率”,它对SEO有什么影响?

    跳出率是搜索引擎优化专家,多年以来热衷讨论的话题,特别是在衡量企业网站相关产品转化率的时候,有着重要的参考价值,但在以往的数据分析中,却又是经常被SEO人员所忽略的指标. 因此,正确的认知" ...

  2. C#-深入理解async和await的作用及各种适用场景和用法

    第十五节:深入理解async和await的作用及各种适用场景和用法 一. 同步VS异步 1.   同步 VS 异步 VS 多线程 同步方法:调用时需要等待返回结果,才可以继续往下执行业务 异步方法:调 ...

  3. javascript函数进阶详细内容 函数闭包 箭头函数 call bind apply用法 偏函数 回调函数

    JS函数进阶 这次的内容我会给大家详细介绍函数方面的内容 1.箭头函数:ES6新增的定义函数的方式,箭头函数是用来简化函数定义语法的. - 箭头函数的语法: ()=>{} ()里面写形式参数,{ ...

  4. javascript学习系列(20):数组中的bind,apply,call

    最好的种树是十年前,其次是现在.歌谣 每天一个前端小知识 提醒你改好好学习了 知乎博主 csdn博主 b站博主  放弃很容易但是坚持一定很酷     我是歌谣 喜欢就一键三连咯 你得点赞是对歌谣最大的 ...

  5. 自己实现call|bind|apply三兄dei

    要简单实现call.bind.apply首先要弄清楚这哥仨是啥,调用入参返回值都是什么,然后照葫芦画瓢,一点点填起来就好了: 能看思考咋实现的想必已经不需要在多赘言三个方法了,直接开始搞起: 首先这哥 ...

  6. 通过实例理解javascript 的call()与apply()

    先前使用javascript多是为了表单验证,最多是实现客户端的交互功能.基本不用call.apply之类的难懂方法. 现在流行富客户端了.javascript也越来越对象化了.不得不学习一下! 一. ...

  7. thinkphp绑定事件用php,thinkphp6事件系统的理解 tp6绑定事件 bind

    namespace app\index\controller; use think\admin\Controller; use think\facade\Event; use app\common\m ...

  8. python基础中apply()函数的正确用法

    函数格式为:apply(func,*args,**kwargs) 用途:当一个函数的参数存在于一个元组或者一个字典中时,用来间接的调用这个函数,并肩元组或者字典中的参数按照顺序传递给参数 解析:arg ...

  9. 聊聊高并发(五)理解缓存一致性协议以及对并发编程的影响

    Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬件差异,给上层的开发者一个一致的使用接口.Java内存模型就是这样一个中间层的模型,它为程序员屏蔽了底层 ...

最新文章

  1. Flash Builder 4.7 类模板编辑设置
  2. TCP listen()函数内幕
  3. 全球与中国终端天线市场发展战略与运营规划分析报告
  4. android 桌面文件夹ui美化
  5. hdu4533 威威猫系列故事——晒被子
  6. 通过注册表修改远程桌面默认3389端口
  7. 普通调幅(AM)与包络检波(matlab实现)
  8. Android之解析XML总结(SAX、Pull、Dom三种方式)
  9. Matplotlib画图教程:在QT界面中嵌入三维图片
  10. [CMake] set_property 和 set_target_properties
  11. 零散的JavaScript公用方法
  12. GitHub遭遇史上最强DDoS攻击:峰值流量1.35Tbps!
  13. Python:批量爬取下载中国知网(CNKI)PDF论文
  14. 重磅 | 数据挖掘之父韩家炜:文本语料库的数据挖掘(附视频+PPT下载)
  15. 模糊逻辑(Fuzzy Logic)
  16. Gmail企业邮箱在用OUTLOK或FOXMAIL有时出现密码错误的解决办法
  17. html将英文日期格式转化为中文日期格式,excel中文日期与英文日期的转换
  18. 10543: 孤岛症候群
  19. Python数据分析(Pandas)
  20. v4l2架构专题模块handler分析 -- handler ctrl的注册2

热门文章

  1. Tomcat5.5报错:Apache Portable Runtime was not found
  2. 3D竞技比赛或成数字娱乐新里程碑
  3. 受教黑金文档,再度优化兼容irq uart代码
  4. Exception in thread “main“ java.lang.NoClassDefFoundError: javax/script/Compilable
  5. NoClassDefFoundError: com/google/protobuf/RpcCallback
  6. @property和@setter和@getter
  7. caffe在线可视化(转)
  8. 大话数据结构:散列表
  9. 入门无人驾驶学习路线
  10. 1.12 四类向量组