this,call,apply,bind浅析
在JS中,this指向是一个难点,在本文中讲解几种常见的this指向问题,并介绍一下call,apply,bind这三个函数的用法。
一、常见的this指向情况
首先要明白一点就是,函数里面才会有this,而this指向是由函数执行的方式来决定的,并且this指向的一定是对象,常见的有三种情况:
- 函数名(),即函数名加括号自执行,该函数里面的this指向window对象;
- 对象.方法(),即对象点属性方法加括号执行,此方法内的this指向该对象;
- 无论什么形式的事件函数,函数内部的this指向触发事件的对象。
下面我们来看几个例子:
案例1:
var oBox = document.getElementById('box');
function a() {
        console.log( this );
    }
a();
document.onclick = a;
//oBox.onclick = a;
打开控制台,会看到打印了一个window,这是第6行a(),a函数自执行时,函数内部的this指向window对象。然后,我们点击一下页面,触发了document的onclick事件,此时控制台会再打印一个document,只是因为事件函数内部的this指向触发该事件的对象。同理,如果我们把第7行注释,第8行取消注释,代码功能换成给页面上一个id为‘box’的标签对象注册一个点击事件,那么当我们在页面上点击这个标签的时候,控制台会给我们打印出这个标签(如下图)。

案例二:
var obj = {
        name:'Person',
        fn:function(){
            console.log(this);
        }
    }
    obj.fn();
这个案例里,fn是对象obj的一个方法,当我们用obj.fn()执行的时候,fn里的this指向obj,我们会看到控制台打印的效果如下:

案例三:
var a = function () {
    console.log( this );
};
function x() {
     a();
}
x();
这个案例里的this指向的是window,即我们开篇说的第一种情况。这里可能有一个误导的地方就是,有人会觉得a在x中运行,this会指向x,其实我们只要再认真读一下我们之前说过的两点就会明白了,一点是this指向一定是一个对象,这里x不是对象,另一点就是函数名加括号自执行,this指向window,不管这个函数在放在哪里执行的,哪怕是放在一个对象里面,只要它是函数名加括号自执行的方式,那么它里面的this指向一定是window。
案例四:
function x() {
    function a() {
        console.log(this);
    }
    a();
}
x();
这里的this也是指向window,吃透了案例三,再看案例四就觉得简单了。
案例五:
var oBox = document.getElementById("box");
oBox.a = function () {
    console.log(this);
};
oBox.a();
(oBox.a)();
document.onclick = oBox.a;
这个案例里,第7行和第8行打印的this为id是box的标签,当我们点击一下页面后,会打印document,因为无论什么形式的事件函数,里面的this都会指向触发该事件的对象。以下是控制台的打印结果:

this剖析:this本身就是一个对象,跟其他数据类型一样,JS也会在内存中给它分配一个地址,只是是动态的,不是固定的,它的变化是根据不同执行环境来动态分配的。而this是什么时候产生呢,就是this指向的对象是什么时候产生,this就是什么时候产生,我们前面总结的第一种情况,函数名加括号自执行,实际上是window.函数名()执行的,所以this会指向window,那么实际上和第二种情况对象.函数名()执行方式是一样的。
二、改变this指向
在我们的实际运用中,有时候我们需要改变this的指向,JS给我们提供了三个改变this执行的方法:call,apply和bind,让我们先来看看它们的用法和不同之处。
- call:
在函数执行阶段使用,可以改变this指向; 
 call的第一个参数代表函数的this指向;
 原函数的第一个形参对应call的第二位实参,第二个形参对应call的第三位实参,以此类推。
- apply:
在函数执行阶段使用,可以改变this指向; 
 apply的第一个参数代表函数的this指向;
 apply的第二个参数是个数组,数组第一位对应原函数第一个形参,以此类推。
- bind:(不支持IE8及以下)
并不会立刻帮助函数去自执行,当函数执行的时候去改变this指向; 
 参数形式和call一样。
它们的用法是:函数名.call(arg1,arg2,agr3......)、函数名.apply(arg1,[arg2,arg3......])、函数.bind(arg1,arg2,arg3......),主要的区别在于call和apply会让函数立即自执行,而bind不会让函数自执行;而call和apply的区别在于传参的不同,apply的第二个参数是一个数组。下面我们来看几个实际的例子:
案例六:
function a(x,y) {
    console.log(x+y);
    console.log(this);
}
a(1,2);
a.call(document,5,5);
a.apply(oBox,[5,1]);
这个案例里,第6行a函数自执行了一次,第7行用call来执行a函数,改变this指向为document,而第8行用apply来执行函数a,改变其this指向为oBox对象,并且这里传的第二个参数用了一个数组。最终的打印结果如下:

案例七:
function a(x) {
    console.log(x);
    console.log(this);
}
a.call(null,1);
a.apply(undefined,[2]);
当我们给call、apply和bind的第一个参数传null或者undefined时,this指向window,有时候我们只需要利用call、apply、bind传参而不需要改变this指向时,我们会这样用。下面我们一起来看打印的结果:

案例八:
var oBox = document.getElementById("box");
oBox.x = function () {
     console.log(this);
};
oBox.x();
oBox.x.call(document);
对象的方法依然可以通过call、apply和bind改变this指向。效果如下:

下面我们来看看bind,它的用法和call是类似的,不同在于它不会立即执行函数,而是在函数被动执行的时候再去改变this指向,我们看下面的例子:
案例九:
var oBox = document.getElementById("box");
var oWrap = document.getElementById("wrap");
oBox.onclick = eFn.bind(oBox,200,150);
oWrap.onclick = eFn.bind(oWrap,150,200);
function eFn(x,y) {
     this.style.width = x+"px";
     this.style.height = y+"px";
}
页面上有两个div标签,一个id为box,一个id为wrap,我们希望实现一个功能,就是点击它们的时候,它们分别按照不同的值去改变宽高,因为功能一致,所以我们把它们的点击事件函数进行封装,在封装函数中改变当前点击对象的宽高,因为点击事件是被动触发的,所以此时我们会用到bind,通过bind巧妙传递参数并且改变this指向,达到我们想要实现的功能,这个案例是bind的一个经典运用。
值得一提的是,apply和bind除了改变this指向的功能,在传参方式上也给我们提供了一些好的解决办法,bind可以在传参的同时不主动执行这个函数,给我们在写事件函数传参和定时器函数传参时提供了方便,而apply用数组传参的方式也给我们提供了新的便利,比如下面的例子:
案例10:
我们需要找出一个数字数组中的最大值,我们知道Math有个max方法是找最大值的,但是max接收的参数是分开的一个个的数字,而不是数组,所以找出数组的最大值,通常我们的做法是这样的:
var arr = [3,16,5,6,9,12,32,21];
var max =arr[0];
for (var i = 1; i < arr.length; i++) {
if(arr[i]>max)
max = arr[i];
}
console.log(max);
但是apply给我们提供了一个更好的解决方式,如下:
var arr = [3,16,5,6,9,12,32,21];
var max = Math.max.apply(null,arr);
console.log(max);
以上是本文的全部内容,感谢阅读!
this,call,apply,bind浅析的更多相关文章
- call,apply,bind的用法
		关于call,apply,bind这三个函数的用法,是学习javascript这门语言无法越过的知识点.下边我就来好好总结一下它们三者各自的用法,及常见的应用场景. 首先看call这个函数,可以理解成 ... 
- JavaScript中call,apply,bind方法的总结。
		why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user:"追梦子", fn:f ... 
- call(),apply(),bind()与回调
		1.call(),apply(),bind()方法 JavaScript 中通过call或者apply用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定 ... 
- JS 的 call  apply  bind 方法
		js的call apply bind 方法都很常见,目的都是为了改变某个方法的执行环境(context) call call([thisObj[,arg1[, arg2[, [,.argN]]]] ... 
- javascript-this,call,apply,bind简述2
		上节我们一起研究了this这个小兄弟,得出一个结论,this指向调用this所在函数(或作用域)的那个对象或作用域.不太理解的朋友可以看看上节的内容,这次我们主要探讨一下call(),apply(), ... 
- javascript-this,call,apply,bind简述1
		最近在系统的学习面向对象方面的知识,遇到的最大拦路虎就数this的指向,call,apply,bind函数的使用,单独抽出一天时间把这几个烦人的家伙搞定,去学习更深入的内容. 首先介绍一下this的一 ... 
- call,apply,bind方法的总结
		why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user:"追梦子", fn:f ... 
- JavaScript中call,apply,bind方法的总结
		原文链接:http://www.cnblogs.com/pssp/p/5215621.html why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之 ... 
- call, apply,bind 方法解析
		call(), apply(),bind() 三者皆为Function的方法 call(),apply()的作用是调用方法,并改变函数运行时的context(作用上下文) bind() 的作用是引用方 ... 
- JS中call,apply,bind方法的总结
		why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user: "小马扎", fn: ... 
随机推荐
- 三 vue学习三 从读懂一个Vue项目开始
			源码地址: https://github.com/liufeiSAP/vue2-manage 我们的目录结构: 目录/文件 说明 build 项目构建(webpack)相关代码. config ... 
- Throwable相关知识1
			Throwable是所有异常Exception和错误Error的祖先 Throwable是java.lang包中一个专门用来处理异常的类.它有两个子类,即Error 和Exception,它们分别用来 ... 
- Resistance
			题意: 给出一个由n个节点和m个二元电阻元件组成的电路,求问节点1到节点n的等效电阻. 解法: 应用电子电路分析中的基尔霍夫定律,对于每一个点有流量平衡,得 对于点$x$有 $$I_{出} + \su ... 
- 3.1-3.3 HBase Shell创建表
			一.HBase Shell创建表 1.HBASE shell命令 ## hbase(main):001:0> create_namespace 'ns1' //创建命名空间:ns1 hbase( ... 
- python使用xlrd操作Excel文件
			一.xlrd读取Excel文件 用xlrd进行读取比较方便,流程和平常手动操作Excel一样,打开工作簿(Workbook),选择工作表(sheets),然后操作单元格(cell). 例子:要打开当前 ... 
- c++重载输入输出运算符
			1 最好打断点看看哦 2例子 #include <iostream> using namespace std; class Complex2 { public: Complex2(, ) ... 
- c++中IO输入输出流总结<二>
			1 文件的打开和关闭 1.1 定义流对象 ifsteam iflie;//文件输入流对象 ifsteam iflie;//文件输出流对象 fsteam iflie;//文件输入输出流对象 1.2 打开 ... 
- 国内互联网公司的开源项目及github地址汇总
			国内互联网公司的开源项目及github地址汇总 阿里 阿里的开源项目很多,这也跟@淘宝正明的开源态度密不可分.有很多重量级的项目,例如LVS.Tengine,或者很有实践价值的中间件,例如 MetaQ ... 
- JQuery学习笔记(三)遍历 DOM
			遍历 DOM jQuery 提供了多种遍历 DOM 的方法.遍历方法中最大的种类是树遍历(tree-traversal). 向上父节点parent,向下子节点child,同胞next和pre 缩写搜索 ... 
- 《剑指offer》面试题15—输出链表中倒数第n个结点
			题目:如题,且从1开始计数. 思路:要求只遍历一遍链表:设置两个指针,一个先走n步后另一个开始同步后移,当快指针已经到链表尾时慢指针正好到要输出的结点. 注意:本题思路比较好想到,主要考察的是代码的鲁 ... 
