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

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:,
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:,
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 ;
}
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);//hello var 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 = "";
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, , "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,);
newFun2(,);//5,7,9

深入理解this和call、bind、apply对this的影响及用法的更多相关文章

  1. javascript中bind,apply,call的相同和不同之处

    javasctipt中bind,apply,call的相同点是: 1,都是用来改变this的指向; 2,都可以通过后续参数进行传参; 3,第一个参数都是指定this要指向的对象; 不同点: 1,调用方 ...

  2. javascript 的bind/apply/call性能

    javascript有两种使用频率非常高的三个内置的功能:bind/apply/call.许多技术是基于高点,这些功能实现.这三个功能被用来改变的功能运行环境.从而达到代码复用的目的. 先来所说bin ...

  3. .bind.apply() 解决 new 操作符不能用与 apply 或 call 同时使用

    背景: 小明想要用数组的形式为 Cls.func 传入多个参数,他想到了以下的写法: var a = new Cls.func.apply(null, [1, 2, 3]); 然而浏览器却报错Cls. ...

  4. bind,apply,call的区别

    在Javascript中,bind, apply, call方法都可以显式绑定上下文this,这三者有何不同呢? bind只绑定this不马上执行 var person = { firstname: ...

  5. 箭头函数表达式和声名式函数表达式的区别以及 Function.prototype的bind, apply,call方法

    箭头函数不能用做构造函数 箭头函数没有arguments参数 箭头函数没有自己的this,是从作用域链上取this,是与箭头函数定义的位置有关的,与执行时谁调用无关,所以用call,apply,bin ...

  6. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  7. ajax和springmvc的请求响应原理——深入理解jQuery中$.get、$.post、$.getJSON和$.ajax的用法

    1,四大重要部分: 请求链接 post请求和get请求 请求参数形式 响应内容形式 2,从springmvc的controller角度,controller能接收到请求的前提 请求链接必须对应 pos ...

  8. 【JavaScript】深入理解call,以及与apply、bind的区别

    一.call call有两个妙用 1.继承(我前面的文章有提到用call实现call继承,有兴趣可以看下.https://www.cnblogs.com/pengshengguang/p/105476 ...

  9. 彻底理解了call()方法,apply()方法和bind()方法

    javascript中的每一个作用域中都有一个this对象,它代表的是调用函数的对象.在全局作用域中,this代表的是全局对象(在web浏览器中指的是window).如果包含this的函数是一个对象的 ...

随机推荐

  1. <泛> 多路快排

    今天写一个多路快排函数模板,与STL容器兼容的. 我们默认为升序排序 因为,STL容器均为逾尾容器,所以我们这里采用的参数也是逾尾的参数 一.二路快排 基本思路 给你一个序列,先选择一个数作为基数,我 ...

  2. 1006 Sign In and Sign Out (25)(25 point(s))

    problem At the beginning of every day, the first person who signs in the computer room will unlock t ...

  3. Linux下rz,sz与ssh的配合使用,实现文件传输

    一般来说,linux服务器大多是通过ssh客户端来进行远程的登陆和管理的,使用ssh登陆linux主机以后,如何能够快速的和本地机器进行文件的交互呢,也就是上传和下载文件到服务器和本地:    与ss ...

  4. 持续集成之Jenkins插件使用(一)- 多个job之间的串并联

    转载自:http://qa.blog.163.com/blog/static/190147002201391661510655/ Jenkins除了开源和免费,还有一个最吸引人的功能之一就是支持插件. ...

  5. 【10.5校内测试】【DP】【概率】

    转移都很明显的一道DP题.按照不优化的思路,定义状态$dp[i][j][0/1]$表示吃到第$i$天,当前胃容量为$j$,前一天吃(1)或不吃(0)时能够得到的最大价值. 因为有一个两天不吃可以复原容 ...

  6. 简单的php自定义错误日志

    平时经常看php的错误日志,很少有机会去自己动手写日志,看了王健的<最佳日志实践>觉得写一个清晰明了,结构分明的日志还是非常有必要的. 在写日志前,我们问问自己:为什么我们有时要记录自定义 ...

  7. Java的Spi机制心得

    Java spi : 是Java EE 给服务供应商提供的接口,供应商遵循接口契约提供自己的实现.. 简单来讲就是为某个接口寻找服务实现的机制. 在看JDBC源码当看到DriverManage.get ...

  8. discuz功能列表

    导航旁边的+就可以把其加入到常用操作 上 首页 管理中心首页 文件校验,在线成员,管理团队留言.系统信息.开发 团队介绍. 常用操作管理 名称和URL 全局 站点信息 站点名称.网站名称,网站URL. ...

  9. 74HC125 74HCT125 74LV125 74LVC125

    74HC125; 74HCT125Quad buffer/line driver; 3-state The 74HC125; 74HCT125 is a quad buffer/line driver ...

  10. mysql官网文档调试MYSQL资料 5.7

    http://dev.mysql.com/doc/refman/5.7/en/debugging-server.html