this、call和apply
来源:互联网 发布:云计算港股上市公司 编辑:程序博客网 时间:2024/05/22 02:27
2.1 this
javascript总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定,而非函数被声明时的环境。
2.11 this的指向
(1)作为对象调用
(2)作为普通函数调用
(3)构造器调用
(4)Function.prototype.call或Function.prototype.apply调用
1.作为对象的方法调用
当函数作为对象的方法被调用时,this指向对象:
var obj={ a:1, getA:function(){ alert(this===obj);//输出true alert(this.a);//输出1 }}obj.getA();
2.作为普通函数调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的this总是指向全局对象。在浏览器的javascript里,这个全局对象是window对象。
window.name="globalName";var getName=function(){ return this.name;}console.log(getName());
callback函数
div节点的事件函数内部,有一个局部的callback方法,callback被作为普通函数调用时,callback内部的this指向了window,但我们往往是想让它指向该div节点。此时有一个简单的解决方案,可以用一个变量保存一个div节点的引用:
window.id="window";document.getElementById("div").onclick=function(){ var that=this;//保存div的引用 var callback=function(){ alert(that.id);//输出div } callback();}
3.构造器调用
javascript没有类,但是可以从构造器中创建对象,同时也提供了new运算符,使构造器更像是一个类。
除了宿主提供的一些内置函数,大部分的javascript函数都可以当做构造器使用。构造器的外表和普通函数一模一样,它们的区别在于被调用的方式。当做new运算符调用函数时,该函数总会返回一个对象,通常情况下,构造器里的this就指向返回的这个对象。
var MyClass=function(){ this.name="seven";}var obj=new MyClass();alert(obj.name);//输出seven
var MyClass=function(){ this.name="seven"; return{//显示的返回一个对象 name:"anne" }}var obj=new MyClass();alert(obj.name);//输出anne
4.Function.prototype.call和Function.prototype.apply
call和apply方法能很好的体现javascript的函数语言特性,在javascript中,几乎每一次编写函数式语言风格都离不开call和apply。跟普通的函数调用相比,用Function.prototype.call或Function.prototype.apply可以动态的改变传入的this.
var obj1={ name:"seven", getName:function(){ return this.name; }}var obj2={ name:"anne"};console.log(obj1.getName());//输出sevenconsole.log(obj1.getName.call(obj2));//输出anne
2.1.1 丢失的this
var obj={ myName:"seven", getName:function(){ return this.myName; }}console.log(obj.getName());//输出:sevenvar getName2=obj.getName();console.log(getName2());//输出undefined
当调用obj.getName时,getName方法是作为对象的属性被调用。this指向obj对象。
当用另外一个变量getName2来引用obj.getName,并且调用getName2,此时是普通函数的调用方式,this指向全局window,所以程序执行是undefined.
var getId=document.getElementById;getId("div1");
document.h方法内部实现需要用到this,这个this本来被期望指向document,当getElementId方法作为document对象的属性被调用时,方法内部的this确实是指向document的。
但当用getId来引用document.getElementById之后,再调用getId,此时就成了普通函数调用,函数内部的this指向了window,而不是原来的document。
我们可以把apply将document当做this传入getId函数,帮助修正this:
var getId=document.getElementById;getId("div1");document.getElementById=(function(func){ return function(){ return func.apply(document,arguments); }})(document.getElementById);var getId=document.getElementById;var div=getId("div1");alert(div.id);//输出div1
2.2 call和apply
Function原型有两个方法,它们是Function.prototype.call和Function.prototype.apply。
2.2.1 call和apply的区别
它们的作用一样,区别是在于传入参数的形式不同。
apply接受两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标签的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数:
var func=function(a,b,c){ alert([a,b,c]);}func.apply(null,[1,2,3]);//输出1,2,3
call传入的参数数量是不固定的,跟apply相同的是,第一个参数也是代表函数体内this的指向。从第二个参数开始往后,每个参数被依次传入函数:
var func=function(a,b,c){ alert([a,b,c]);}func.call(null,[1,2,3]);//输出1,2,3
当调用一个函数的时候,javascript的解释器并不会计较形参和实参的数量、类型以及顺序上的区别,javascript的参数内部就是用一个数组来表示的,只要用apply推进去就可以啦。
call是包裹在apply语法上的一颗语法糖,如果我们明确知道函数接受多少个参数,而且想一目了然地表达形参和实参的对应关系,那么也可用call来传送参数。
如果我们传入的第一个参数是null,函数体内的this会指向默认的宿主对象,在浏览器则是window.
有时候我们使用call和apply的目的不在于指向this,而是另有用处,比如借用其他对象的方法。那么我们可以传入null来代替某个具体对象。
例如:Math.max.apply(null,[1,2,3,4,5])//输出5
2.2.2 call和apply的用途
1.改变this的指向
var obj1={ name:"seven"};var obj2={ name:"anne"};window.name="window";var getName=function(){ alert(this.name);}getName();getName.call(obj1);//输出sevengetName.call(obj2);//输出anne
在实际开发中,经常会遇到this指向被不经意改变的场景,比如有一个div节点,div节点的onclick事件中的this本来是指向这个div的:
document.getElementById("div1").onclick=function(){ alert(this.id);//输出div1}
假如该事件有一个内部函数func,在事件内部调用func函数时,func函数体内的this就直接指向了window,而不是我们预期的div,见如下代码:
document.getElementById("div1").onclick=function(){ alert(this.id);//输出div1 var func=function(){ alert(this.id);//输出undefined } func();}
这个时候我们使用call来修正this的场景。
document.getElementById("div1").onclick=function(){ alert(this.id);//输出div1 var func=function(){ alert(this.id);//输出div1 } func.call(this);}
2.Function.prototype.bind
大部分的浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this指向,即使没有原生的Function.prototype.bind来实现,我们来模拟一个也不是难事,代码如下:
Function.prototype.bind=function(context){ var self=this;//保存原函数 return function(){//返回一个新的函数 return self.apply(context,arguments); //执行新的函数的时候,会把之前传入的context //当做新的函数体内this }};var obj={ name:"sven"};var func=function(){ alert(this.name);//输出seven}.bind(obj);func();
我们通过Function.prototype.bind来包装func函数,并且传入一个对象context当做参数,这个context对象就是我们想要修正的this对象。
在Function.prototype.bind内部实现中,我们先把func函数的引用保存起来,然后返回一个新的函数。当我们在将来执行func函数时,实际上先指向的是刚刚返回的这函数。在新的函数内部,
self.apply(context,arguments)这句代码才是执行原来的func函数,并且指定context对象为func函数体内的this.Function.prototype.bind=function(){ var self=this,//保存原函数 context=[].shift.call(arguments),//需要绑定的this上下文 args=[].slice.call(arguments);//剩余的参数转成数组 return function(){//返回一个新函数 return self.apply(context,[].concat.call(args,[].slice.call(arguments))); //执行新的函数的时候,会把之前传入的context当做新函数体内的this //并且组合两次分别传入参数,作为新函数的参数 }};var obj={ name:"seven"}var func=function(a,b,c,d){ alert(this.name);//输出seven alert([a,b,c,d]);//输出1,2,3,4}.bind(obj,1,2);func(3,4);
3.借用其他对象的方法
借用方法的第一步场景是“借用构造函数”,通过这种技术,可以实现一些类似继承的场景:
var A=function(name){ this.name=name;}var B=function(){ A.apply(this,arguments);}B.prototype.getName=function(){ return this.name;}var b=new B("SVEN");console.log(b.getName());//输出seven
函数的参数列表arguments是一个类数组对象,虽然它也有下标,但它并非是真正的数组,所以不能像数组一样,进行排序操作或者往集合里添加一个新的元素。这种情况下,我们常常会借用Array.prototype对象上的方法。比如想往arguments中添加一个新的元素,通常会借用
Array.prototype.push:(function(){ Array.prototype.push.call(arguments,3); console.log(arguments);//输出1,2,3})(1,2);
在操作arguments的时候,我们经常非常频繁的找Array.prototype对象借用方法。
想把arguments转成真正的数组的时候,可以借用Array.prototype.slice方法;想截去arguments列表中的头一个元素时,又可以借用Array.prototype.shift方法。
Array.prototype.push实际上是一个属性复制的过程,把参数按照下标一次添加到被push的对象上面,顺便修改了这个对象的length属性,至于被修改的对象是谁,到底是数组还是类数组对象,这并不重要。
由此可以判断,我们可以将“任意”对象传入Array.prototype.push;
var a={};Array.prototype.push.call(a,"first");alert(a.length);//输出1alert(a[0]);//first
而在低版本的IE浏览器中执行,必须显式的给对象a设置length属性:
var a={ length:0};
借用Array.prototype.push方法的对象还要满足一下两个条件:
(1)对象本身要可以存储属性
(2)对象的length属性可读写
比如number类型的属性不可能借用到Array.prototype.push方法:
- this、call和apply
- this、call和apply
- this、call和apply
- call、apply、bind和this
- 理解JavaScript中的this、call和apply
- this call apply
- js (this,call,apply)
- js (this,call,apply)
- this call() apply()理解
- Javascript apply, call this
- this,call,apply
- this call apply
- javascript中this,apply和call方法的使用
- JS 的this、new、apply和call详解
- this、new、call和apply的相关问题
- JS中this、new、apply和call详解
- JS的this总结(上)-call()和apply()
- javascript 中的this call apply
- linux进程间通信--管道
- 一些 UIView 中管理 Subview 常用的方法
- 降低时间复杂度的几种方法【持续更新】
- 【MySQL】----MySQL日志管理
- 今天开启博客之旅
- this、call和apply
- Linux command ps
- JDBC的分页查询以及hibernate高级查询分页
- 2017 年软件开发人员需要面对的 7 个改变
- ARKit从入门到精通(2)-ARKit工作原理及流程介绍
- 5-7 最长对称子串 (25分) 对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。
- git备忘录
- 签到签到
- C语言的字符串函数strcmp()和strcnmp()