当javaScript从入门到提高前需要注意的细节:对象部分
2013-02-24 18:50
741 查看
最近看了一个帖子,楼主抱怨说javaScript不是标准的面向对象语言,最多勉强算是基于面向对象的语言。这样的说法也是非常有现实的市场的,主要是基于class的编程语言影响力太大了,C++、java、C#哪个不是名门之后,搞得大家现在一说面向对象就是必须有class关键字了。
面向对象的开发编程只是一种编程的思想,和对编程的指导意见(设计模式更是一种经验的归纳和名称,绝对不是圣经来的)。面向对象的思想主要是建议开发人员注意几个事项:尽可能的实现代码重用(过程性的函数也是代码重用来的),尽可能的为特定的个体设计独特的数据类型,在这个独特的类型中包含了这个类型所有自我完成的运算,但是仅仅将需要调用者关心的数据和函数公开,其他都屏蔽掉。
具体一个语言是怎么实现面向对象的,和面向对象的开发准则并没有具体的约定。由于历史原因,我们大部分的面向对象编程选择了C++风格的静态CLASS方式,所以我们很多人就习惯上了基于CLASS的面向对象编程方式而已。而且CLASS从C++开始到java再到C#,编译器厂商为了讨好开发人员再语法糖上面下了极大的功夫,推陈出新了众多用于描述对象封装,继承,多态应用的关键字,让我们开发效率极大的提高,自然我们也就喜欢上了CLASS的编程,也自然的认可了CLASS作为面向对象开发的标准或代言人。
既然我们开始理解了面向对象的编程不仅仅有基于CLASS的方式,还有其他的各种方式,比如VB是基于IMPLEMENTS的实现方式,而javaScript是基于PROTOTYPE来实现的。
javaScript的特殊在于,其没有类,所有的一切都是实例(这里我特别的用了实例而不是对象,就是担心说都是对象其实是不严谨的),从类型来观察,javaScript提供了6种我们可以访问的基本数据类型:undefined、null、boolean、string、number、object,以下的代码在null的时候有点另人疑惑,其他都蛮正常,原因是在javaScript中null被定义为了Null。
JavaScript
说到Null,我们知道在javaScript中还有String、Date、Number、Boolean、Object、Function,在java或C#中,我们可以判断出这些应该是对象,而且是对基本数据类型的隐射,但是在javaScript中,那就完全不同
JavaScript
以上得到的结果全是function。这点就告诉我们两个基本事实:1 javaScript中只有object是对象数据类型(就这么唯一一个);2 对象和函数直接的确有着非常暧昧的关系。
如果对以上的函数采用new运算符,我们得到的结果是
JavaScript
我们可以观察到除了Function之外,其他都返回对象。现在我们开始进入主题。
在javaScript中最简单的对象定义就是使用字面量进行定义
JavaScript
上面这段代码,我们可以得到两个对象。要记住在javaScript中,不采用CLASS来抽象对象,所有的一切值都是实例的。我们不需要任何CLASS就可以得到一个对象,使用{}就是声明了一个对象实例。在这样的一个直接对象中所定义的值默认都是公开的,试图采用var在字面量的对象中声明一个私有属性是不可以的。需要说明下,javaScript对象是一个key-value的集合,我们说的属性其实是一个key,正确的将应该是我们在Poker中定义了3个可便利的Key:Title,Style和Value。讲属性那是方便使用C#、Java的程序员了解。
javaScript对面向对象的封装和CLASS的不同,他没有提供public和private等访问修饰符。如果我们使用现代的浏览器,比如IE9,Chrome等支持ECMAScript5 的浏览器,那么我们可以定义不可遍历的key,并且可以为key来定义set和get,并且可以约定一个key只读,好像已经接近CLASS的习惯了,如下是一个demo
JavaScript
但使用字面量直接定义对象的话,至少还有两个麻烦,1是重用比较麻烦,2是对私有变量的封装还是不给力。虽然说字面量创建的对象也是可以继承的比如
JavaScript
因为javaScript中字面量的对象都是实例,我们不管这样的继承是不是比较麻烦或者另类,但至少我们可以认识到,Poker对象(实例是一直存在的)每一个继承者都需要一个Poker的实例空间
JavaScript
以下的代码更是明确的告诉我们,Poker的实例中this指向的是实际调用方法的子类(当然你承认这个是子类的话)
JavaScript
同时,我们可以了解到一个继承链的事情是:当一个被调用的key在当前对象没有定义的时候,会顺着继承链向上去找,直到找到第一个key存在为止(object自身的toString在这个demo中被无视了),如果都没有就是undefined了。你可以认为是CLASS中的重写来实现多态吧,我想可以这么说。
一般来讲,我不建议在字面量(我用这个词非常不舒服,我喜欢叫直接对象)用来继承,直接对象最有价值的用法就是作为参数或什么的用一次,简单高效,看的明白。如果要合理的使用内存,灵活的创建对象,那必须用function才比较爽。
JavaScript
上面的代码显然舒适多了,而且容易理解。我不反对吧Poker这个函数叫构造函数,事实上他也的确帮助我们构造了一个Poker对象,不过由于javaScript没有CLASS的概念,所以我更喜欢倾向于说:Poker函数帮助我们构造了一个以Poker为原型的对象。叫起来非常的绕口,那就还是说是构造函数吧。
无论是直接对象还是函数构造得到的对象,对于javaScript来讲都是动态的,这个实例可以动态的添加key。
看看以下代码,在构造完成了Pkoner10和PokerA之后,我们在Poker的原型上创建了一个新的函数createDom,这个函数依然对Pkoner10和PokerA是有效的。
JavaScript
不过你要记得,function新加的key必须加在prototype上,以下代码就是错误的
JavaScript
原因我估计是这样的,function当函数用的时候就是函数,当用new构造的时候,编译器将this指向的key防止到了function的prototype(原型)上,以下算是一个证明吧
JavaScript
说到key了,正好说下javaScript中的数组,数组一般在静态语言的概念中式连续分配的一段内存,大小是固定的。不过javaScript中的数组我看其实是对象的变形
JavaScript
最后的for得到的不是数组的值,而是数组的index,所以我们可以这样求值
JavaScript
所以,估计arr[i] = i是动态的添加了一个属性,并赋值而已啦。
还是回到构造函数来说,一般看来,为了符合面向对象的一个编程意思:对象是自己的一组数据的集合,所以我们一般在构造函数中定义对象的属性(也就是数据),数据由构造函数的参数提供,而对象的方法在外部用函数编写,并且指向这个构造的原型。原因是在javaScript中字面量的对象没有property……
这个property就是function构造出的实例,如果把方法直接写在function上,那就是这个function的静态成员了,估计可以这么说,一下是一个demo
JavaScript
那么函数,构造函数,对象之间的原型关系到底怎么样的呢?下面的代码描述了这些关系
JavaScript
函数的构造函数是Function对象;
函数的对象化(函数创建的对象)的构造函数是function的定义;
函数的构造函数的原型是一个空函数;
函数的原型和函数创建的对象的构造函数的原型相等(所以对象是函数构造出来的);
函数的构造函数的原型和函数的对象化的构造函数的原型不同;
对象没有原型可以直接访问(这个和字面量的对象一样的);
上面的文字看上去和绕口令一样的,但多念念就明白了……
所以证明前面的说法:如果需要函数的成员让实例访问,要么加到函数代码里面,要么在外面加到函数的原型上去
面向对象的开发编程只是一种编程的思想,和对编程的指导意见(设计模式更是一种经验的归纳和名称,绝对不是圣经来的)。面向对象的思想主要是建议开发人员注意几个事项:尽可能的实现代码重用(过程性的函数也是代码重用来的),尽可能的为特定的个体设计独特的数据类型,在这个独特的类型中包含了这个类型所有自我完成的运算,但是仅仅将需要调用者关心的数据和函数公开,其他都屏蔽掉。
具体一个语言是怎么实现面向对象的,和面向对象的开发准则并没有具体的约定。由于历史原因,我们大部分的面向对象编程选择了C++风格的静态CLASS方式,所以我们很多人就习惯上了基于CLASS的面向对象编程方式而已。而且CLASS从C++开始到java再到C#,编译器厂商为了讨好开发人员再语法糖上面下了极大的功夫,推陈出新了众多用于描述对象封装,继承,多态应用的关键字,让我们开发效率极大的提高,自然我们也就喜欢上了CLASS的编程,也自然的认可了CLASS作为面向对象开发的标准或代言人。
既然我们开始理解了面向对象的编程不仅仅有基于CLASS的方式,还有其他的各种方式,比如VB是基于IMPLEMENTS的实现方式,而javaScript是基于PROTOTYPE来实现的。
javaScript的特殊在于,其没有类,所有的一切都是实例(这里我特别的用了实例而不是对象,就是担心说都是对象其实是不严谨的),从类型来观察,javaScript提供了6种我们可以访问的基本数据类型:undefined、null、boolean、string、number、object,以下的代码在null的时候有点另人疑惑,其他都蛮正常,原因是在javaScript中null被定义为了Null。
JavaScript
alert(typeof undefined); alert(typeof null); alert(typeof 123); alert(typeof 123.45); alert(typeof true); alert(typeof false); alert(typeof "Hello"); alert(typeof {});
说到Null,我们知道在javaScript中还有String、Date、Number、Boolean、Object、Function,在java或C#中,我们可以判断出这些应该是对象,而且是对基本数据类型的隐射,但是在javaScript中,那就完全不同
JavaScript
alert(typeof Number); alert(typeof String); alert(typeof Date); alert(typeof Boolean); alert(typeof Function); alert(typeof Object);
以上得到的结果全是function。这点就告诉我们两个基本事实:1 javaScript中只有object是对象数据类型(就这么唯一一个);2 对象和函数直接的确有着非常暧昧的关系。
如果对以上的函数采用new运算符,我们得到的结果是
JavaScript
alert(typeof new Number()); alert(typeof new String()); alert(typeof new Date()); alert(typeof new Boolean()); alert(typeof new Function()); alert(typeof new Object());
我们可以观察到除了Function之外,其他都返回对象。现在我们开始进入主题。
在javaScript中最简单的对象定义就是使用字面量进行定义
JavaScript
var obj = {}; var Poker = { Title: "K", Style: "spade", Value: 13 };
上面这段代码,我们可以得到两个对象。要记住在javaScript中,不采用CLASS来抽象对象,所有的一切值都是实例的。我们不需要任何CLASS就可以得到一个对象,使用{}就是声明了一个对象实例。在这样的一个直接对象中所定义的值默认都是公开的,试图采用var在字面量的对象中声明一个私有属性是不可以的。需要说明下,javaScript对象是一个key-value的集合,我们说的属性其实是一个key,正确的将应该是我们在Poker中定义了3个可便利的Key:Title,Style和Value。讲属性那是方便使用C#、Java的程序员了解。
javaScript对面向对象的封装和CLASS的不同,他没有提供public和private等访问修饰符。如果我们使用现代的浏览器,比如IE9,Chrome等支持ECMAScript5 的浏览器,那么我们可以定义不可遍历的key,并且可以为key来定义set和get,并且可以约定一个key只读,好像已经接近CLASS的习惯了,如下是一个demo
JavaScript
var obj = {}; var Poker = { Title: "K", Style: "spade", Value: 13, State: 1 }; Object.defineProperties( Poker, { "backgroundImg": { value: "images\\common\\hide.png", enumerable: false, //不可以for 遍历 writable: false//只读 }, "forgroundImg": { value: "images\\spade\\K.png", enumerable: false, //不可以for 遍历 writable: false//只读 }, Img: { get: function() { return this.State == 0 ? this.backgroundImg : this.forgroundImg; }, enumerable: true } } ); alert(Poker.Img); //images\\spade\\K.png for (var key in Poker) { alert(key); //backgroundImg 和 forgroundImg无法被遍历到 } alert(Poker.backgroundImg); //依然可以访问 Poker.backgroundImg = "XXX"; alert(Poker.backgroundImg); //依然是images\\common\\hide.png
但使用字面量直接定义对象的话,至少还有两个麻烦,1是重用比较麻烦,2是对私有变量的封装还是不给力。虽然说字面量创建的对象也是可以继承的比如
JavaScript
var Poker = { State: 1 }; var PokerA = { Title: "A", Style: "spade", Value: 14, __proto__: Poker }; var Poker10 = { Title: "10", Style: "club", Value: 10, __proto__: Poker }; alert(PokerA.Title); //A alert(PokerA.State); //1 alert(Poker10.Title); //10 alert(Poker10.State); //1 //在IE8-访问 alert(PokerA.__proto__.State); alert(Poker10.__proto__.State);
因为javaScript中字面量的对象都是实例,我们不管这样的继承是不是比较麻烦或者另类,但至少我们可以认识到,Poker对象(实例是一直存在的)每一个继承者都需要一个Poker的实例空间
JavaScript
Poker.State = 3; PokerA.State = 0; alert(PokerA.State); //0 alert(Poker10.State); //3 alert(Poker.State); //3
以下的代码更是明确的告诉我们,Poker的实例中this指向的是实际调用方法的子类(当然你承认这个是子类的话)
JavaScript
var Poker = { State: 1, toString: function() { alert(this.Style + this.Title + ":" + this.Value); } }; var PokerA = { Title: "A", Style: "spade", Value: 14, __proto__: Poker }; var Poker10 = { Title: "10", Style: "club", Value: 10, __proto__: Poker }; PokerA.toString(); // spadeA: 14 Poker10.toString(); //club10:10
同时,我们可以了解到一个继承链的事情是:当一个被调用的key在当前对象没有定义的时候,会顺着继承链向上去找,直到找到第一个key存在为止(object自身的toString在这个demo中被无视了),如果都没有就是undefined了。你可以认为是CLASS中的重写来实现多态吧,我想可以这么说。
一般来讲,我不建议在字面量(我用这个词非常不舒服,我喜欢叫直接对象)用来继承,直接对象最有价值的用法就是作为参数或什么的用一次,简单高效,看的明白。如果要合理的使用内存,灵活的创建对象,那必须用function才比较爽。
JavaScript
var Poker = function(style, title, value, state) { this.Title = title; this.Style = style; this.Value = value; this.State = arguments[3] === undefined ? 0 : state; var backgroundImg = "images\\common\\hide.png"; var forgroundImg = "images\\spade\\" + title + ".png"; this.Img = (function(x) { return x == 0 ? backgroundImg : forgroundImg; } (this.State)); this.toString = function() { return this.Style + this.Title + ":" + this.Value } } var Poker10 = new Poker("spade", "10", "10"); var PokerA = new Poker("spade", "A", "14", 1); alert(PokerA.toString()); // spadeA: 14 alert(Poker10.toString()); //club10:10 alert(PokerA.Title); //A alert(PokerA.State); //1 alert(Poker10.Title); //10 alert(Poker10.State); //0 alert(PokerA.Img); //images\spade\A.png alert(Poker10.Img); //images\common\hide.png
上面的代码显然舒适多了,而且容易理解。我不反对吧Poker这个函数叫构造函数,事实上他也的确帮助我们构造了一个Poker对象,不过由于javaScript没有CLASS的概念,所以我更喜欢倾向于说:Poker函数帮助我们构造了一个以Poker为原型的对象。叫起来非常的绕口,那就还是说是构造函数吧。
无论是直接对象还是函数构造得到的对象,对于javaScript来讲都是动态的,这个实例可以动态的添加key。
看看以下代码,在构造完成了Pkoner10和PokerA之后,我们在Poker的原型上创建了一个新的函数createDom,这个函数依然对Pkoner10和PokerA是有效的。
JavaScript
var Poker10 = new Poker("spade", "10", "10"); var PokerA = new Poker("spade", "A", "14", 1); Poker.prototype.createDom = function() { return $("<img id=" + this.Style + this.Title + ">"); } $( function() { PokerA.createDom().appendTo("body"); Poker10.createDom().appendTo("body"); } );
不过你要记得,function新加的key必须加在prototype上,以下代码就是错误的
JavaScript
Poker.insert = function(box, poker) { poker.appentTo(box); } PokerA.insert("body", PokerA.createDom()); //错误了
原因我估计是这样的,function当函数用的时候就是函数,当用new构造的时候,编译器将this指向的key防止到了function的prototype(原型)上,以下算是一个证明吧
JavaScript
alert(Poker.prototype.constructor === Poker); //true alert(PokerA.constructor === Poker); //true alert(PokerA.createDom === PokerA.__proto__.createDom); //true alert(PokerA.__proto__.constructor === Poker.prototype.constructor); //true
说到key了,正好说下javaScript中的数组,数组一般在静态语言的概念中式连续分配的一段内存,大小是固定的。不过javaScript中的数组我看其实是对象的变形
JavaScript
var arr = []; for (var i = 0; i < 5; i++) { arr[i] = i; } arr.push(99); for (var index in arr) { alert(index); // 0 1 2 3 4 5 }
最后的for得到的不是数组的值,而是数组的index,所以我们可以这样求值
JavaScript
var arr = []; for (var i = 0; i < 5; i++) { arr[i] = i; } arr.push(99); for (var index in arr) { alert(arr[index]); }
所以,估计arr[i] = i是动态的添加了一个属性,并赋值而已啦。
还是回到构造函数来说,一般看来,为了符合面向对象的一个编程意思:对象是自己的一组数据的集合,所以我们一般在构造函数中定义对象的属性(也就是数据),数据由构造函数的参数提供,而对象的方法在外部用函数编写,并且指向这个构造的原型。原因是在javaScript中字面量的对象没有property……
这个property就是function构造出的实例,如果把方法直接写在function上,那就是这个function的静态成员了,估计可以这么说,一下是一个demo
JavaScript
function Poker(style, title, value) { this.Style = style; this.Title = title; this.Value = value; } Poker.max = function(Poker1, Poker2) { var p; for (var i = 0; i < arguments.length; i++) { if (arguments[i] instanceof Poker) { if (typeof p == "undefined") { p = arguments[i]; } else { p = p.Value > arguments[i].Value ? p : arguments[i]; } } } return p; } var p = Poker.max(new Poker("club", "K", 13), new Poker("diamond", "A", 14), "", new Poker("diamond", "10", 10)); alert(p.Style + p.Title + "[" + p.Value + "]"); // diamondA[14]
那么函数,构造函数,对象之间的原型关系到底怎么样的呢?下面的代码描述了这些关系
JavaScript
alert(Poker.constructor); //function Function() { [native code] } alert(new Poker().constructor); //function Poker(style, title, value) { // this.Style = style; // this.Title = title; // this.Value = value; // } alert(Poker.constructor.prototype); //function Empty() {} alert(Poker.prototype == new Poker().constructor.prototype); // true alert(Poker.constructor.prototype == new Poker().constructor.prototype); // false alert(new Poker().propertye); //undefined
函数的构造函数是Function对象;
函数的对象化(函数创建的对象)的构造函数是function的定义;
函数的构造函数的原型是一个空函数;
函数的原型和函数创建的对象的构造函数的原型相等(所以对象是函数构造出来的);
函数的构造函数的原型和函数的对象化的构造函数的原型不同;
对象没有原型可以直接访问(这个和字面量的对象一样的);
上面的文字看上去和绕口令一样的,但多念念就明白了……
所以证明前面的说法:如果需要函数的成员让实例访问,要么加到函数代码里面,要么在外面加到函数的原型上去
相关文章推荐
- 当javaScript从入门到提高前需要注意的细节:对象部分
- 当javaScript从入门到提高前需要注意的细节:对象部分
- 当javaScript从入门到提高前需要注意的细节:对象部分
- 当javaScript从入门到提高前需要注意的细节:变量部分 .
- 当javaScript从入门到提高前需要注意的细节:函数部分
- 当javaScript从入门到提高前需要注意的细节:变量部分
- 当javaScript从入门到提高前需要注意的细节:函数部分
- 【当javaScript从入门到提高前需要注意的细节:变量部分】 的总结
- 当javaScript从入门到提高前需要注意的细节:闭包部分
- 当javaScript从入门到提高前需要注意的细节:变量部分
- 当javaScript从入门到提高前需要注意的细节:闭包部分
- 当javaScript从入门到提高前需要注意的细节:函数部分
- 当javaScript从入门到提高前需要注意的细节:闭包部分
- 当javaScript从入门到提高前需要注意的细节:变量部分
- 当javaScript从入门到提高前需要注意的细节:变量部分
- 当javaScript从入门到提高前需要注意的细节:变量部分
- 【JavsScript】当 JavaScript 从入门到提高前需要注意的细节:变量部分
- javaScript从入门到提高前需要注意的细节:变量部分
- 当javaScript从入门到提高前需要注意的细节:变量部分
- 当javaScript从入门到提高前需要注意的细节:变量部分