手机版

深入分析这个关键词在JavaScript编程中的使用

时间:2021-09-22 来源:互联网 编辑:宝哥软件园 浏览:

这在JavaScript中到底意味着什么?很多人会告诉你,这是指现在的对象。这样理解对吗?大多数情况下,这是真的。例如,我们经常这样在网页上编写JavaScript:

type=' submit ' value=' submit ' onclick=' this . value=' submit data ' '/这显然是指当前对象,也就是这个submit按钮。通常,我们以类似的方式使用这个。但事实并非如此?

看看这个例子:

var foo=function(){ console . log(this);} foo();new foo();

比较foo()和new foo()的运行结果,你会发现前者这个指向的不是foo本身,而是当前页面的window对象,而后者真正指向的是foo。这是为什么?

其实这涉及到JavaScript的一个重要特性,叫做“闭包”。闭包的概念不复杂也不复杂,但也不是简单到一两句话就能解释的。我将在以后的文章中深入讨论Javascript最重要的特性。现在,我想告诉你,因为闭包的存在,JavaScript中的作用域变得非常重要。

简而言之,所谓的范围就是创建函数的环境。如果没有指定,这个变量的值就是函数的当前范围。

在前面的例子中,foo()函数在全局范围内(这里是window对象),所以这个的值就是当前的window对象。以new foo()的形式,实际上创建了foo()的一个副本,并对该副本执行操作,因此这是foo()的副本。

这可能有点抽象。让我们举一个实际的例子:

input type=' button ' id=' Abutton ' value=' demo ' onclick=' '/script type=' text/JavaScript ' function demo(){ this . value=Math . random();}/脚本

如果直接调用demo()函数,程序将报告错误。因为演示函数是在window对象中定义的,所以演示的所有者(范围)是window,而这个演示也是window。并且window没有值属性,所以它报告了一个错误。

2015119174624685.png  (391372)

如果我们通过创建一个副本将这个函数的副本添加到一个HTML元素中,那么它的所有者就变成了这个元素,这也指的是这个元素:

document . getelementbyid(' Abutton ')。onclick=demo这样,aButton的onlick属性被设置为demo()的副本,这也指向aButton。

2015119174656357.png  (391373)

您甚至可以为许多不同的HTML元素创建不同的函数副本。每个副本的所有者都是对应的HTML元素,他们自己的这个也指向他们的所有者,不会造成混淆。

2015119174716805.png  (391482)

但是,如果您像这样定义元素的onlick事件:

输入类型=' button ' id=' Abutton ' value=' demo ' onclick=' demo()'/

点击此按钮后,您会发现程序将再次报告错误。——这又指向了窗户!

事实上,这个方法并没有为程序创建一个函数,而只是引用了这个函数。

详细看区别。

要创建函数的副本:

input type=' button ' id=' Abutton ' value=' demo '/script type=' text/JavaScript ' var button=document . getelementbyid(' Abutton ');函数demo(){ this . value=math . random();} button.onclick=demoalert(button . onclick);/脚本的输出是:

函数demo(){ this . value=math . random();}使用函数引用的方法:

input type=' button ' id=' Button ' value=' demo ' onclick=' demo()'/script type=' text/JavaScript ' var button=document . getelementbyid(' Button ');函数demo(){ this . value=math . random();} alert(button . onclick);/脚本的输出是:

函数onclick(){ demo();}所以你能看出区别。在函数引用的方式上,onclick事件只直接调用demo()函数,demo()函数的作用域仍然是window对象,所以这仍然指向window。

2015119174740033.png  (391368)

这就引出了另一个问题:既然函数的副本这么好用,为什么还需要函数引用的方法呢?答案是性能。每次创建一个新的函数副本时,程序都会为这个函数副本分配一定数量的内存。实际上,大多数函数不一定被调用,所以这部分内存被浪费了。使用函数引用的方法,程序只给函数的本体分配内存,而引用只分配指针,效率要高得多。程序员,先存钱,嗯?

让我们看看更好的解决方案:

脚本类型='text/javascript '函数演示(obj){ obj . value=math . random();}/scriptinput type='button '值='demo' onclick='demo(this)' /input type=' button '值=' demo ' onclick=' demo(this)'/input type=' button '值=' demo ' onclick=' demo(this)'/

这样,效率和需求都可以得到考虑。

这指向了JavaScript。由于它在运行时的绑定,这在JavaScript中可以是一个全局对象、一个当前对象或任何对象,这完全取决于函数的调用方法。在JavaScript中调用函数有几种方式:作为对象方法调用、作为函数调用、作为构造函数调用,以及使用apply或call调用。俗话说,文字不如表格,但表格不如数字。为了让人们更好地理解JavaScript这指向了什么。用下图解释:

2015119174758093.jpg  (1251421)

我称之为“JavaScript这个决策树”(在非严格模式下)。下面的例子说明了这个图表如何帮助我们做出判断:

var点={ x : 0,y : 0,moveTo :函数(x,y){ this . x=this . x x;this . y=this . y y;} };//决策树的解释:point.moveTo(1,1)函数没有被new调用,所以输入判定ofno。//如果用点(.)调用。),它会指向之前的调用对象。移动到,也就是点。moveto (1,1);//这是绑定到当前对象,也就是点对象。point.moveTo()函数在JavaScript中确定此决策树的过程如下:

1)是用new调用的point.moveTo函数吗?这显然不是。转到“否”分支,即函数是否用点()调用。)?

2)point . move to函数用点(.)调用。),也就是它走到了“是”的分支,也就是这里指向的对象点先于点。moveto

下图显示了点移动到函数的解析图:

2015119175054859.jpg  (1245416)

再举一个例子,看看下面的代码:

函数func(x){ this . x=x;} func(5);//这是全局对象窗口,x是全局变量。//决策树分析:func()函数是用new调用的吗?如果没有,是否用点号调用了entering func()函数?否则,这指向全局对象windowx//x=5func()函数在JavaScript这个决策树中判断如下:

1)func(5)函数是否用new调用?这显然不是。转到“否”分支,即函数是否用点()调用。)?

2)func(5)函数不是用点()调用的。),即转到“否”分支,即这里指向全局变量window,所以this.x实际上就是window . x;

下图显示了说明此函数指向什么的分析图:

2015119175222664.jpg  (1242416)

作为直接调用函数的一种方式,让我们看一个复杂的例子:

Varpoint={x: 0,y: 0,移至:函数(x,y){//内部函数var moveX=函数(x){ this . x=x;//这说明了什么?窗口};//内部函数var moveY=函数(y){ this . y=y;//这说明了什么?窗口};moveX(x);moveY(y);} };point.moveTo(1,1);point.x//=0点. y;//=0x;//=1y;//=1point.moveTo(1,1)实际上是在内部调用moveX()和moveY()函数,在JavaScript这个决策树里面判断这个内部moveX()函数的过程如下:

1)movex(1)函数是用new调用的吗?这显然不是。转到“否”分支,即函数是否用点()调用。)?

2)movex(1)函数不是用点()调用的。),即转到“否”分支,即这里指向全局变量window,所以this.x实际上就是window . x;

让我们看一个调用构造函数的例子:

函数Point(x,y){ this . x=x;//这个?this.y=y//这个?}var np=new Point(1,1);np.x//1var p=Point(2,2);p.x//错误,p是一个未定义的空对象。//2 var NP中的Point(1,1)函数=new Point(1,1)这在“JavaScript本决策树”中判断如下:

1)var np=new Point(1,1)用new调用?这显然是,进入“是”分支,即这指向NP;

2)那么这个. x=1,也就是NP。x=1;

var p中的Point(2,2)函数的判断过程=JavaScript中的Point(2,2)此决策树如下:

1)var p=Point(2,2)是用new来调用的吗?这显然不是。转到“否”分支,即函数是否用点()调用。)?

2)2)点(2,2)函数不是用点()调用的。)?如果判断为否,则进入“否”分支,即此处指向全局变量window,所以this.x实际上就是window . x;

3)this.x=2表示window.x=2。

最后,让我们看一下用call和apply调用函数的例子:

函数Point(x,y){ this . x=x;this.y=ythis.moveTo=函数(x,y){ this . x=x;this.y=y} } var p1=new Point(0,0);var p2={x: 0,y : 0 };p1.moveTo.apply(p2,[10,10]);//apply实际上是p2.moveto (10,10) p2.x//10p1.moveto.apply (p2,[10,10])。在JavaScript中判断这个决策树的过程如下:

正如我们所知,apply和call方法非常强大,它们允许切换函数执行的上下文,也就是受其约束的对象。P1.moveTo.apply(p2,[10,10])实际上是p2.moveTo(10,10)。P2.moveTo(10,10)可以解释为:

1) p2。moveto (10,10)函数是否用new调用?这显然不是。转到“否”分支,即函数是否用点()调用。)?

2)函数p2.moveTo(10,10)用点()调用。),即转到“是”分支,即这里指向p2之前的对象p2。MoveTo (10,10),所以p2.x=10

关于JavaScript函数执行环境的过程,在IBM developerworks文档库中有一个描述感觉很好,摘录如下:

“JavaScript中的函数可以作为普通函数执行,也可以作为对象的方法执行,这也是这一点意义如此丰富的主要原因。当执行一个函数时,将创建一个ExecutionContext,函数的所有操作都将在这个执行上下文中发生。在构建这个执行环境时,JavaScript将首先创建参数变量,这些变量包含调用函数时传入的参数。接下来,创建范围链。然后初始化变量。首先初始化函数的参数表。该值是自变量变量中的相应值。如果参数变量中没有对应的值,则参数被初始化为未定义。如果函数包含内部函数,初始化这些内部函数。如果没有,继续初始化函数中定义的局部变量。需要注意的是,这些变量在此时被初始化为未定义,在成功创建ExecutionContext之前,不会执行赋值操作。这对我们理解JavaScript中变量的范围非常重要。鉴于篇幅,我们就不在这里第一次讨论这个话题了。最后,给这个变量赋值。如上所述,它将被分配给这个全局对象、当前对象等。根据不同的函数调用方法。此时,函数的ExecutionContext被成功创建,函数开始逐行执行,所有需要的变量都从之前构造的ExecutionContext中读取。”理解这段文字对理解Javascript函数有很大的帮助。

版权声明:深入分析这个关键词在JavaScript编程中的使用是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。