很多时候写js,根本不会用到「对象」这个东西,因为一般而言初级前端的作用是操作DOM实现一些炫酷效果,而万能的jQuery已经替我们封装好了90%在开发中会用到的函数。但是有个大自然的规律是,越是没有难度的事情,它的价值就越低,如果你想走一条进阶之路,了解标题中的这些名词的含义是非常重要的———这是我在学习过程中的感悟。
言归正传。
1、什么是object
Javascript中有一条定理:一切皆对象。之所以这么有名是因为对象object这个概念在所有OO语言中都存在,但是没有像Javascript这么面向对象面向的这么彻底,我觉得一方面是因为Javascript的语言特质决定的,它轻巧,开放,灵活,优美。。。『Javascript是最好的语言』
怎么又开始废话了。
好了再次言归正传。
在Javascript,一切皆对象,而什么是对象呢?大多数书本上的说法是:对象是一组属性和方法的集合,我表示同意。
对象可以通过如下方法来创建:
var o = new Object();也可以var o = {};
对象怎么创建的不重要,重要的是它能做什么,你可以这样往对象里面增加属性和方法:
var o = new Object();
o.name = 'xheldon';
o.site = 'xheldon.com';
o.come = function(){
console.log('GlaDOS')
}
也可以这样:
var o = {
name:'xheldon',
site:'xheldon.com',
come:function(){
console.log('GlaDOS')
}
}
『一切皆对象』!?
我们可以这样声明一个字符串的属性和方法:
var s = new String('abc');s.name = 'def';console.log('s的值是:'+s+',s.name的值是:'+s.name);//输出结果『s的值是abc,s.name的值是:def』
这样一来,字符串就增加了一个叫做name的属性而被当做对象来处理。
注意:不能直接通过如下方式来为字符串增加属性和方法:
var s = 'abc';s.name = 'def';console.log('s的值是:'+s+',s.name的值是:'+s.name)//输出结果『s的值是:abc,s.name的值是:undefined』
其实前一个增加属性的本质是先隐式的将字符串'abc'转化成String对象,再将其赋值给s,最后再添加属性。因此这个操作是对引用类型而言的,对基本类型增加属性和方法是无效的(但是也不会报错,只是undefined),由此可见,『一切皆对象』在Javascript中是个谎言,其并没有将散落在内存中的基本类型真正的当做对象对待,而是使用的时候隐式的转化成String对象,再进行操作。
所有的对象都是继承自Object对象,因此你可以对任何对象来使用typeof操作,除了基本类型之外,都会显示object类型,不过书上说instanceof这个方法检测类型会更可靠,因为它会告诉你这个对象是哪个Object子对象的实例,Javascript里面有些内置的对象,我称它为子对象,或者叫做引用类型。如String、Array、Function、Date、Number等。
示例:
console.log(typeof 'abc');//string
console.log(typeof new String('abc'))//object
console.log('abc' instanceof String)//false
var n = new String('abc');n instanceof String//true
2、context和this
如上所述,Function也是对象,不过首先你需要知道的是,通过function(){}创造出来的就是函数,它就是对象,它没有名字。
我就是我,是不一样的烟火
但是就像new String('abc')一样,这个语句是有效的,但是也是毫无用处的。你不给它名字,它就一直躺在内存中,直到内存回收器将其回收,就像它从未存在过一样。
也许有人会说,匿名函数不也是经常被使用吗?此为后话,我们一会儿再提。
因此,你想声明一个函数,首先第一件事就是给它个名字以方便我们引用:
var s = function(){console.log('我被引用啦!')};//我被引用啦!
而声明一个函数并将其赋值给一个变量之后,它还是静静的躺在内存中,就像从未存在过,你需要在其赋值的变量后面加个括号来执行它,就像这样:
s();
那这个和上下文有啥关系呢?别急,好戏开始了。
context翻译过来是上下文,上下文是和this以及作用域是密不可分的。你可以像下面这样将this指向一个函数的上下文(如果你能明白「指向」的含义的话):
var o = {
name:'xheldon',
getname:function(){
console.log(this.name);
}
}
o.getname();//xheldon
名字为getname的函数的上下文指向对象o,所以毫无疑问o.name为xheldon
下面这个将context变得复杂一点点:
var o = {
name:'xheldon',
getname:function(){
console.log(this.name);
}
}
var p = {
name:'whyme'
}
o.getname.call(p)//whyme
这个例子中我将对象o中的叫做getname的函数(也即对象o的方法)的上下文设置为p。
有过其他语言基础的同学可能会有疑问,这特么函数声明时的this还能随便让给别人的啊?
是的,在Javascript中,函数执行时的上下文是由函数调用时决定的,而不是函数声明时决定的。
还有个例子,我们将这个例子再变复杂一点点:
var o = {
name:'xheldon',
getname:function(){
var name = 'inset';
var a = function(){
console.log(this.name);
}
a();
}
}
o.getname()//undefined
为啥是undefined呢?按照刚才的说法,函数也是对象,那a()执行的过程是在m函数里面执行的,按理说a函数的this.name应该是inset才对啊!
非也~
在Javascript中,所有的函数,如果左边没有xxx.的形式的对象来引用,或者没有call,apply,bind之类的来强行改变上下文,那么这个函数默认的上下文对象指向的是window也即指向顶层对象,即使这个函数不在window顶层声明且已经使用了var声明也是如此。
不信?没关系,实践是检验真理的唯一标标准
看下上面那个例子的变形:
var name = "I'm the window object!"
var o = {
name:'xheldon',
getname:function(){
var name = 'inset';
var a = function(){
console.log(this.name);
}
a();
}
}
o.getname()//I'm the window object!
3、scope和closure
closure中文翻译为闭包,中文就是牛逼,故名思议,闭包意为封闭的包,也即内部的东西你是看不到的。
你们要的匿名函数:匿名函数最大的作用是用来创建闭包,如下:
(function(para){
//body
})(para)
闭包常常被用在一个函数的内部嵌套(nested)。除非在闭包里return相关信息否则外部函数无法访问里面的东西。
为什么?
内部函数内心独白:『特么的这是老子的scope啊!!!』
没有块级作用域:
function s(){
var name = 'xheldon';
{
var name = 'anotherxheldon'
}
console.log(name);
}
s()//anotherxheldon
闭包示例:
function a(){
var b = 'xheldon';
(function(){
var b = 'anotherxheldon';
console.log(b);
})()
console.log(b);
};
a();//分别输出『anotherxheldon』和『xheldon』
闭包的应用:创建命名空间,减少命名冲突:
var o = {};
(function(){
var a = function(){ /*代码的实现省略了*/ };
function bt(){}
var xxx;一些可能会冲突的命名
var xxx;一些可能会冲突的命名
var xxx;一些可能会冲突的命名
oEvent.a = a;
oEvent.b = b;
})();
这里a和b都是局部变量,可以为o对象新增了两个方法,这样就可以访问闭包内的函数而又不会造成命名冲突,大大减少了全局变量的使用;
闭包还能简化操作:
var s = (function(x,y){return x+y})(2,3)//s被赋值为5
//也可以这样写,但是不推荐:
var s = function(x,y){return x+y}(2,3)//省略括号的写法
而不使用闭包时:
var s1 = function(x,y){return x+y}
var s2 = s1(2,3)
4、return
简单说,return返回的是指针/引用,举例来说:
返回字符串值:
var f = function(){var s = 'name';return s};
var sValue = f();//sValue的值为'name'字符串
返回函数引用:
var s = function(){return function(){console.log('xheldon')}};
s()()//xheldon
因为s()执行的结果是个匿名函数的引用,因此需要再执行一次,所以就再加了个括号。
连续返回:
function compare(a,b){
return function(a,b){
if(a>b){return -1}
if(a<b){return 1}
if(a=b){return 0}
}
}
/**
*等同于
*function compare(a,b){return a-b}
**/
转载自:http://www.xheldon.com/?p=449