做完一段时间的嵌入式开发,又回到web开发了,陌生的感觉一点点。。。
之前看过不少node.js的热评,凑个热闹记住这个名字后后也没太在意,最近的项目中用到了一个新的java框架,其中种种魔术式的约定让人没有安全感,于是有了想深入探索的冲动。框架的资料说明不多,时间也不是很充裕,忍了一年多后还是跳进了源代码中。。。对框架刚有了点懵懵懂懂的感觉,于是又有了想玩转框架的冲动,世界框架万千种,如何将这个框架下的程序轻松地转化到其它框架?嘿嘿,思维发散似乎有点过了,想到了node.js(碗里面的刚吃一点,就想到锅里面的,主啊,求包养)
虽然不是太看好node.js,但好歹也喜欢javascript(javascript挂了一个java的羊头,node也完全可以自己搞一门程序语言,但由于有了性能良好的javascript引擎V8,以及javascript的简单语法,直接来了一个组合拼凑。这个发明人好像有点偷懒哦)。
看了很多说明,node.js最突出的特点是异步,事件驱动。事件驱动好说,哪个web画面上没有几个button、输入框,web开发整天跟这些打交道。至于异步接触最多的就是那个ajax了,点了一个按钮在程序中调用一个ajax的方法,不用等方法返回,然后该干嘛干嘛,ajax处理结束后会自动调用一个回调方法(ajax刚出道时很神秘,后来发现核心就是一个浏览器中的api时,对它的认识一直就凝固在此)。居然唠叨了这么半天,看来也太年轻++了。
写个微博主要是想让自己能深入一点了解,如果看官有异议,噢,求看官包养!
javascript的语法很简单,主要想关注一下其面向对象部分。
javascript的面向对象的实现方法跟现在主流不太一样,在javascript中提到继承,prototype这个关键字不可忽略(web开发中使用的javascript都很简单,都是写几个function之类的,这个关键字虽然很眼熟,也不能保证能完整地写出这个字母,而且还是在准备些这篇文章时才明白是一个有文字意义的单词:原型,雏形,蓝本),javascript中的对象是基于原型的。不明觉厉,好吧,从头开始吧。
-------------------------------------------------------------------
最开头是什么,对象嘛,非class莫属,很可惜,javascript 中class不是关键字,只是保留字,只能看不能用,这注定javascript中的对象跟java长得不一样了。
javascript也有内建的对象,最熟悉应该是Array了,
var arrs = new Array();
arrs[0] = 100;
.....
console.log(typeof Array); ---function
console.log(typeof arrs); ---object
原来,在javascript中,
对象是function,实例是object
关键字new貌似没有java中那么严谨:
var arrs = Array();
var arrs = new Array;
var arrs = new Array();
三条语句的效果是一模一样,狂野程序员的醉爱。
如果写成var arrs = Array;此时的arrs等于Array的别名。console.log(arrs == Array); ---true
-------------------------------------------------------------------
最开头是什么,对象嘛,非function莫属
题外话:javascript中有一个内建的Function对象,就差第一个字母的大小写,其语法是
Function(参数1,参数2.。。,代码字符串)
var func = new Function("info", "console.log('in Function:' + info);");
func(111);
实际效果跟下面的程序一样:
function func(info) {console.log('in Function:' + info);}
func(111);
这个函数貌似没什么用处,跟下面的代码是等同的,
Function(arg1, arg2, codestr) = function(arg1, arg2){eval(codestr); return this;}
再来做个小小的测试:
var claz = function() {consoel.log("****");return 123;};
console.log(typeof claz); ---function
console.log(typeof claz()); ---****number
console.log(typeof (new claz())); ---****object
console.log(claz); ---[Function]
console.log(claz()); ---****123
console.log(new claz()); ---****{}
怎么样,在new的作用下,function像不像class中的构造函数,构建了对象实例,不要new则是函数值返回值。
两种情况下,函数内部的程序都会执行。
把claz改成通常模式:
function claz() {
console.log("****");
return 123;
}
效果是一样的,只是两种不同的写法
-------------------------------------------------------------------
好吧,有了构造函数,那么对象的属性呢?
java中有this这个关键字,javascript中也有,在javascript中var声明了一个局部变量,this则表示实例
function claz(){
var a = 456;
this.a = 123;
console.log("init:" + a); ---init:456 说明this的作用域比函数局部变量高
}
var cls = new claz();
console.log("property:" +cls.a); ---property:123
this的作用域在构造器之外,看来是属于顶级杀器之类的了。如果有内部function的时候,这个this是啥情况呢?
function topfunc() {
thiz = this;
this.subfunc = function() {
console.log(">>>" + (thiz == this));
}
}
var cls = new topfunc();
new cls.subfunc(); ---false
cls.subfunc(); ---true
如此看来,当内部function只是作为简单的函数时,跟父function共享this;如果作为一个对象时,内部function拥有自己的this
那么this可以更改么,譬如:
function topfunc2() {
this = new function(){}
}
很遗憾,编译都通不过。
其实var定义的变量也是一个实例,可以往实例中动态追加属性:
cls.a1 = "a1";
cls.show = function (){console.log("method call");}
console.log(cls.a1); ---a1
cls.show(); ---method call
如果这样
console.log(cls.this); ---undefined
this和实例变量是不是一回事,再来测试一下:
function claz(){
var thiz = this;
this.check = function (clsv) {
console.log(clsv == thiz);
};
}
var cls = new claz();
cls.check(cls); ----true
也就是说,引用对象自己时,在function内部用this,在外部用变量名,有点意思。
如果this和变量名二合一不知道有什么效果
function func(){}
var fc = new func();
console.log(fc.this); ----undefined
没这种用法,那么this.this呢:
function func(){
this.this = this;
this.p = 100;
}
var fc = new func();
console.log(fc.this); --{ this: [Circular], p: 100 }
这种方法证明了猜测是可行的。不过这样也导致了循环引用,
console.log(fc.this.this.this.this.this.this.this.p); ---100
这种用法是不是很酷?
既然可以动态往对象中追加属性以及方法,那么把一个对象的方法赋值给另外的对象,那么方法中变量的作用域会不会乱?
function topfunc() {
this.a = 10;
this.b = 20;
this.subfunc = function() {
console.log(">>>" + this.a); ①
this.b = 200 + this.a; ②
this.a = 1000; ③
console.log(">>>" + this.a); ④
}
}
function topfunc1() {
this.a = 11;
this.b = 21;
}
var cls = new topfunc();
var cls1 = new topfunc();
cls1.subfunc = cls.subfunc;
cls1.subfunc(); ---10 1000
console.log(cls.a + "," + cls.b); ---10,20
console.log(cls1.a + "," + cls1.b); ---1000,210
感觉有点乱,程序乱,log结果也乱,思绪也有点乱了:
按照变量作用域寻找路径,先找本地变量,然后父作用域,在①处,this.a还没有申明于是返回上一层,有点闭包的意思:
function topfunc() {
var a = 100;
var b = 200;
return function subfunc() {
var b = 210;
a++;
b++;
console.log(a + "," + b);
}
}
var topf = topfunc();
topf(); ---101,211
topf(); ---102,211
在subfunc中,a变量没有申明,于是其变量作用域找到上一层,一直引用着topfunc作用域中的变量,而b变量在subfunc已经申明过,其作用域是函数内部
所以上一个例子中的②就比较有意思了
this.b = 200 + this.a;
this.b算是在申明一个本地变量,此时的this是一个全局变量,于是this.b=cls1.b。而this.a在之前一直都是引用,还是cls.a,一旦对a进行赋值之类的,
this.a = 1000; ③
那么其性质也发生了变化,此时a的分身又割断了一条丝,被引用到cls1上,于是发生了上面怪怪的现象。
纷繁的表面现象下掩盖着许多真实的故事,真相是不是这样,不想再去看javascript引擎的源代码探个究竟,欢迎拍砖。
-------------------------------------------------------------------
谈了这么久,小可怜prototype该出来谢幕了
function topfunc() {}
var topf = new topfunc();
console.log(topfunc.prototype); ---{}
console.log(String.prototype); ---{}
console.log(topf.prototype); ---undefined
看来,这个prototype算是Class级,每个function对内建立prototype这么一个属性,而实例则没有。
先看看prototype和上面介绍的动态添加属性的差异:
function topfunc() {}
var a = new topfunc();
var b = new topfunc();
a.a = 100;
console.log(a.a); ---100
console.log(b.a); ---undefined
a,b是两个独立的实例,享有自己的空间,a添加了属性a,是不会影响其它实例的
如果是prototype的话:
function topfunc() {}
var a = new topfunc();
var b = new topfunc();
topfunc.prototype.a = 100;
console.log(a.a); ---100
console.log(b.a); ---100
看来prototype是Class级的,往其添加的属性、函数将会影响所有的实例。
稍微修改一下:
function topfunc() {}
var a = new topfunc();
var b = new topfunc();
a.a = 99;
topfunc.prototype.a = 100;
console.log(a.a); ---99
console.log(b.a); ---100
上例中,a变量中添加了a属性,然后在topfunc.prototype添加了a属性,log打印的结果表明a变量中添加的属性是优先的。
也就是说prototype的作用域比this还要高一级,哪里是什么小可怜,是大boss:
同时也可以看到,prototype可以在new 实例化之前,也可在其之后,代码执行时是看最近变化的值,感觉prototype对于实例来说就是一个全局变量。
function func(){}
var a = new func();
func.prototype.p = 100;
func.prototype = null;
console.log(a.p); ---100
把func的prototype设置成了null,还是能打印出prototype中添加的属性,说它是全局变量还是不妥,再来看下面的:
function func(){}
var c = {p : 200};
var a = new func();
func.prototype = c;
console.log(a.p); ---undefined
在对象实例化后,将prototype设置为自定义对象,此时自定义对象中的属性找不到。如果将赋值语句提前到实例化之前,就可以使用属性了。
难道是在解析function的时候,创建了一个object,function实例化时将内部指针也指向这个object,也就是说function的实例以及function本身都引用这个对象?
那么将func.prototype = null;后,实例仍然可以使用的现象就好说了,func.prototype=null,但是实例还是引用初始prototype对象。
同样func.prototype.p = 100;放在实例化之前/之后都可以影响代码,func.prototype和实例引用同一个对象,因此有全局变量的感觉。
在最后一个例子中,function的prototype和实例引用的对象是不相同的,实例肯定不能引用function中prototype
function func(){}
func.prototype.p = 300;
var c = {p : 200};
var a = new func();
func.prototype = c;
console.log(a.p); --300 //实例a还是指向初始化时的prototype
var a1 = new func(); //实例a1指向新的prototype,也就是c
console.log(a1.p); ---200
例子证明了猜测还是正确的。
如果想通过实例而影响function的值,
function func(){}
func.prototype.p = 300;
var a = new func();
a.p = 400;
console.log(a.p, func.prototype.p); ---400 300
看来是不可行的,实际上在a.p = 400;时,实例就创建了一个属性。
在javascript最遗憾的事莫过于字符串没有trim函数,往往都是调用一个自定义函数来实现,多么希望“dont't trim me”.trim(),prototype可以满足这个癖好:
String.prototype.trim = function(raw) {return "trimed";};
console.log("dont't trim me".trim()); ----trimed
怎么样,威力大吧。
当然还可以对已有的函数进行覆盖:
String.prototype.substr = function(s1, s2) {return "substring";};
console.log("dont't trim me".substr(1,2)); ----substring
再来看看prototype是什么:
console.log(typeof String.prototype); ---object
噢,一个实例,那么也可以这样咯:
String.prototype = null;
console.log("dont't trim me".substr(1,2)); ----TypeError: Object #<Console> has no method 'substr'
那么function,prototype,实例之间都有些什么瓜葛呢,看看下面的例子(今天头有点晕,例子难免受影响)
function topfunc() {
this.show = function subfunc() {console.log("sub function");}
//show1在函数变量域中不存在,往上寻找也不存在,报[ReferenceError: show1 is not defined]错误
try{show1();}catch(e){console.log(e);}
//topfunc.prototype.show1,直接指明是function对象,运行正常
topfunc.prototype.show1();
}
topfunc.prototype.show1 = function(){console.log("show1");}
//在实例的范围中搜索show1,看来function的prototype也在搜索的范围之内
new topfunc().show1();
topfunc.prototype.show1();
//show1只是function属性prototype的属性,报[has no method 'show1']错误
try{topfunc.show1();}catch(e){console.log(e);}
//show是topfunc实例的一个属性,不是function级的,报[TypeError: Object #<topfunc> has no method 'show']错误
try{topfunc.prototype.show();}catch(e){console.log(e);}
//下面的用法报[has no method 'show1']错误
try{topfunc.show();}catch(e){console.log(e);}
从上面的例子可以看出,function和实例之间的属性是分开的,互不干涉。只是在实例中调用方法时,会搜索function的prototype。
不相信的话可以将上面的
try{show1();}catch(e){console.log(e);}
改为:
try{this.show1();}catch(e){console.log(e);}
新的语句运行良好。
如果将prototype换成别的自定义属性,结果会是什么样:
topfunc.prototype = new function(){};
topfunc.prototype.show1 = function(){console.log("show1");}
运行良好,看来prototype也不过如此,一普通对象。
利用这个特性,可以简单地实现继承
var a = {show : function() {console.log("inherit.....");}};
function func(){}
func.prototype = a;
new func().show();
再试试有什么需要注意的:
function topfunc() {
try{this.show1();}catch(e){console.log(e);}
}
topfunc.prototype = new function(){};
topfunc.prototype.show1 = function(){console.log("show1");}
topfunc(); // TypeError: Object #<Object> has no method 'show1']
new topfunc();
由上看出,prototype中的对象绑定是在对象实例化的时候进行的。
String.prototype.trim = function(){return ""};
var c = "aa".trim();
上面的比较特殊,"aa"本身就包含一个实例化的过程。
function show0(){console.log("show0");}
function topfunc() {
this.show0(); //show00
show0(); //show0
}
topfunc.prototype.show0 = function(){console.log("show00");};
new topfunc();
如果不特别指明是实例的方法,则默认为是function层次,代码不会去搜索实例的prototype
function topfunc() {
try{this.show1();}catch(e){console.log(e);}
}
topfunc.myprototype = new function(){};
topfunc.myprototype.show1 = function(){console.log("show1");}
new topfunc();
由上看出,prototype还是有点特殊的,换其它的还不行。
除了这个prototype之外,function还有那些属性呢:
function topfunc(){this.show1=function(){};}
var props = Object.getOwnPropertyNames(topfunc);
for(var p in props){
console.log(props[p]);
}
打印出如下信息:
length
name
arguments
caller
prototype
如果打印实例的属性:
var props = Object.getOwnPropertyNames(new topfunc());
则显示:
show1
-------------------------------------------------------------------
还记不记得开头的console.log(new claz()); ---****{}
实例的符号表示是{},我们都知道{}一般用来封装代码段,难道还能表示实例
function topfunc() {this.fun1 = function(){};
this.fun2 = function(){};
this.p1 = 100;
}
console.log(new topfunc());
打印结果:{ fun1: [Function], fun2: [Function], p1: 100 }
嗯,代码是分号分隔,实例是逗号分隔,那么上面的代码可以等换为:
var fun1 = {
fun1: function(){},
fun2: function(){},
p1 : 100
};
console.log(fun1);
打印结果:{ fun1: [Function], fun2: [Function], p1: 100 }
用这种方法可以直接实例化一个对象,形式是key:value,key的前面不需要this,而且不能要
var fun1 = {
this.fun1: function(){}, //属性不能有this
var a = 100, //属性中不能有代码
blank: var b = 200
};
如果还有点印象的话,一定还会想起[]
console.log(new Array()); // []
这个[]其实也是一个对象,表示一个对象数组
var as = [1,2,3,4];
console.log(as instanceof Array); //true
-------------------------------------------------------------------
呼,终于把一些问题点给理顺了
面向对象可不能没有继承,但是extend也不能用,既然不愿实现,何必苦苦哀求。再说了javascript又不是java
不过来抖一抖相关的趣事秘闻,先从Object对象来谈谈:
var obj1 = {
fun1 : function(){},
p1 : 100
};
var obj2 = Object.create(obj1);
obj2.p2 = 200;
for(var p in obj2){console.log(p);}
===
p2
fun1
p1
obj2经过Object.create,把obj1的属性全数继承了过来,像不像继承?
×题外话,这个for in倒是挺猛的,可以把实例的属性全部枚举出来,试试打印其它对象的:
var obj2 = "";
for(var p in obj2){console.log(p);}
打印为空,奇怪了,字符串有很多方法的,怎么可能为空。不看源代码单纯猜测还真是没安全感。
不过Object的另外一个函数让人怀疑javascript内部还有蹊跷:
var obj1 = {fun1 : function(){},p1 : 100};
Object.defineProperties(obj1,{
p1 : {enumerable:false, value:200}
});
var obj2 = Object.create(obj1);
obj2.p2 = 200;
for(var p in obj2){console.log(p);}
===
p2
fun1
在Object.defineProperties的函数中,将p1的enumerable设置成了false,for in中就不会打印了,原来for in会调用一个变量的enumerate接口,同样Object.keys(obj1)也会调用。
如果在程序末尾调用obj2.p1;或者obj1.p1,程序是没有问题的,这个设置正如其字面上的意思。
这个defineProperties中除了enumerable外,还有
writable,value
set,get 这两组是互斥的,后面的设置function,前面的设置值。还有一个configurable
,用以设置可不可删除对象的属性:delete obj.prop
然后apply和call