javascript中的对象查找【转】 -ag凯发k8国际
近期群里常有人提一些简单的问题,比如发一段代码乱七八糟的代码,然后说里面某个变量是什么,比如这里就有个很好的例子:
function fn(arg) {alert(this.arg);
alert(this);
}
fn(123);
var o = { fn: fn };
o.fn(123);
然后就可能有这样的问题:
为什么this.arg是undefined?为什么2次调用fn的this是不一样的?
为此,我觉得自己作为一个虽然不成熟的前端,对于一些自己力所能及的事情,还是应该传道授业解惑的。所以,这篇文章,计划从非常肤浅的层面上,来解释一下javascript中的对象查找是如何进行的。
注意,本篇文章只是从表象上来介绍对象查找这一行为的过程,文章中的观点并不全正确,甚至存在着一些谬误,但是这也是为了让初学者更好地理解对象查找这一过程。相信如果说得太过抽象、深入,反而会引起一些负面效果。如果有一天,你再回过来,发现这个文章说得并不那么正确,那么恭喜你,那个时候的你已经可以找到正确前进的道路,这篇中的错误也不会再对你有任何影响。
对象的分类
所谓对象查找,即在一段可执行代码的作用域内,找到一个当前需要的对象。在javascript中,需要进行查找的对象大致可以分为3种类型:
- 变量查找,如foo ;,这里就会去查找一个叫作foo的变量。
- 属性查找,如foo.bar ;,这里会去查找foo这个变量下的一个叫作bar的属性。
- this查找,即针对this关键字的处理。
区分这3种类型的对象查找是首先要完成的任务,你可以基于以下原则进行判断:
- 变量仅由变量名组成,即单独的foo、bar等。
- 属性永远由2种形式去访问,即foo.bar和foo['bar'],因此看到有“.”或“[]”即可当成是属性查找。
- this就不用说了,好好的关键字。
看一下这段代码:
var foo =this;foo.bar();
这2行的代码就体现了3种对象查找,分别为:
变量的查找
当确定一个对象的查找为变量查找后,可以按照变量查找的规则来查看。
变量查找,即在作用域链上进行查找,作用域链是javascript非常著名的2条链之一,以下代码体现一个标准的作用域链:
var foo =1;function a() {
var bar =2;
function b() {
foo =3;
function c() {
alert(foo ',' bar); // 注意这一行
}
c();
}
b();
}
a();
在c函数中,一共进行了2个变量的查找,分别为foo和bar。
变量的查找可以简单地遵守“从下向上”的原则,即:
总结一下,变量的查找是延着作用域链进行的,作用域链可以简单地看成函数间的包含关系,被包含的函数中不存在某个变量时,在包含他的函数中查找,直到全局作用域。
属性的查找
当确定一个对象的查找为属性查找后,可以按照属性查找的规则来查看。
属性查找,即在原型链上进行查找,原型链是javascript双链的另一条,以下可以表示出一个原型链:
var a =function() {};var b =function() {};
var c =function() {};
b.prototype =new a();
c.prototype =new b();
a.prototype.foo =1;
b.prototype.bar =2;
c.prototype.foo =3;
var o =new c();
alert(o.foo ',' o.bar); // 这一行进行查找
属性查找是一个不断寻找prototype的过程,即:
总结一下,属性查找是延着原型链进行的,原型链的具体知识这里不作详细解释,可以另找文章进行参考。所有的对象,其原型链最终会是object.prototype。
this的查找
this的查找是很多人迷茫的一点,也似乎有很多人抱有this不稳定这样的看法,实在令人无语。this的查找可以说是3种对象查找中最为简单的,因为其实this对象的确定根本没有一个“查找”的过程。
首先,this对象只会在一个函数中需要确定,如果是在全局域下,this永远为global对象,在浏览器中通常就是window对象。而在javascript中,函数的调用一共有4种方式:
function invocation pattern诸如`foo()`的调用形式被称为function invocation pattern,是函数最直接的使用形式,注意这里的foo是作为单独的变量出现,而不是属性。
在这种模式下,foo函数体中的this永远为global对象,在浏览器中就是window对象。
诸如`foo.bar()`的调用形式被称为method invocation pattern,注意其特点是被调用的函数作为一个对象的属性出现,必然会有“.”或者“[]”这样的关键符号。
在这种模式下,bar函数体中的this永远为“.”或“[”前的那个对象,如上例中就一定是foo对象。
`new foo()`这种形式的调用被称为constructor pattern,其关键字`new`就很能说明问题,非常容易识别。
在这种模式下,foo函数内部的this永远是new foo()返回的对象。
`foo.call(thisobject)`和`foo.apply(thisobject)`的形式被称为apply pattern,使用了内置的`call`和`apply`函数。
在这种模式下,`call`和`apply`的第一个参数就是foo函数体内的this,如果thisobject是`null`或`undefined`,那么会变成global对象。
应用以上4种方式,确定一个函数是使用什么样的pattern进行调用的,就能很容易确定this是什么。
另外,this是永远不会延作用域链或原型链出现一个“查找”的过程的,只会在函数调用时就完全确认。
总结
对于一个对象的查找:
注意把一次查找过程拆分开来,比如this.foo.bar.yahoo(),可以拆分成这样的代码,就能更清楚了:
var o =this; // this查找var foo = o.this; // 属性查找
var bar = foo.bar; // 属性查找
bar.yahoo(); // 属性查找,加method invocation pattern
最后,如果有一天你可以了解这些东西,这篇文章对你用户也就不大了:
- 为什么延作用域查找不到会有referenceerror。
- 其实变量也是属性,一个特殊对象的属性。
- this也许不是global,也许会是undefined。
转载于:https://www.cnblogs.com/tangge/archive/2011/04/26/2029295.html
总结
以上是ag凯发k8国际为你收集整理的javascript中的对象查找【转】的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 保留关键字 (transact-sql)
- 下一篇: c# socket编程第二篇