javascript没那么简单(转)

原文出处:http://www.cnblogs.com/bestfc/archive/2010/08/02/1790173.html

写在前面
似乎园子里最近少了些人,各个文章的阅读量少了许多
废话不说,写此文目的是为了让更多的程序员理解javascript的一些概念,对是理解不是了解
我们已经了解得够多了,该是向深入理解的方向靠拢的时候了
为什么这么说,前些日子收到面试邀请,那就去试试呗,有几年没有面试过了吧
和面试官坐在沙发上,聊天式的他问我答,以下就是几个javascript方面的问题
>请创建一个对象,包括几个公有属性,接下来是为对象创建一个公有方法,然后为对象创建几个私有属性,一个私有方法
说实话,这几个问题我默名其妙,要是他让我用jquery写个拖动插件什么的,我估计我能写挺好,原生的javascript,晕,虽然我看过jquery源码解读,但这些基本概念要命
稍后,我会在正文中将答案写出来
如果面试官在看,说声谢谢,面试完之后我就去深圳购书中心买javascript去了,好贵,呵呵,看中之后,到卓越定了一个《javascript王者归来》
然后基本上啥也没干,仔细的看了一个多星期,看不明白的就到园子里来找,园子里的宝贝真不少!(还压了个韵。。。)
本文的例子输出使用如下方法,便于查看
function dwn(s){
 document.write(s+"<br/>");
}
以下内容有些是原创,有些来自于网络,或者可以看成是读书笔记,如果有哪里不对的,请各位不吝赐教,在下感激不尽

正文开始
一,function
从一开始接触到js就感觉好灵活,每个人的写法都不一样,比如一个function就有N种写法
如:function showMsg(){},var showMsg=function(){},showMsg=function(){}
似乎没有什么区别,都是一样的嘛,真的是一样的吗,大家看看下面的例子
///------------------------------------------------------------------------------------------------
//函数定义:命名函数(声明式),匿名函数(引用式)
//声明式,定义代码先于函数执行代码被解析
function t1(){
 dwn("t1");
}
t1();
function t1(){
 dwn("new t1");
}
t1();
//引用式,在函数运行中进行动态解析
var t1=function(){
 dwn("new new t1");
}
t1();
var t1=function(){
 dwn("new new new t1");
}
t1();
//以上输出:new t1,new t1,new new t1,new new new t1
可能想着应该是输出t1,new t1,new newt1,new new new t1,结果却并不是这样,应该理解这句话:声明式,定义代码先于函数执行代码被解析
如果深入一步,应该说是scope链问题,实际上前面两个方法等价于window.t1,可以理解为t1是window的一个公有属性,被赋了两次值,以最后一次赋值为最终值
而后面两个方法,可以理解为是t1是个变量,第四个方法的var去掉之后的结果仍然不会改变
然而,当第四个方法改成function t1(){}这样的声明式时,结果变成了new new new t1,new new new t1,new new t1,new new t1
前面两个按照我的理解可以很好的理解为什么是这个答案,第三个也可以理解,但是最后一个输出让我比较纠结,希望有高手出现解答一下
另外匿名函数还有(function(){...})()这样的写法,最后一个括号用于参数输入
还有var t1=new function(){..}这样的声明,实际上t1已经是一个对象了
例:
var t2 = new function()
{
     var temp = 100; //私有成员
     this.temp = 200; //公有成员,这两个概念会在第三点以后展开说明
     return temp + this.temp;
}

alert(typeof(t2));   //object
alert(t2.constructor());  //300
除此之外,还有使用系统内置函数对象来构建一个函数,例:
var t3 = new Function(‘var temp = 100; this.temp = 200; return temp + this.temp;‘); //这个位置加不加new结果都一样,WHY
alert(typeof(t3)); //function
alert(t3());  //300

二,创建对象
首先我们理解一下面向对象编程(Object-Oriented Programming,OOP),使用OOP技术,常常要使用许多代码模块,每个模块都提供特定的功能,每个模块都是孤立的,甚至与其它模块完全独立
。这种模块化编程方法提供了非常大的多样性,大大增加了代码的重用机会。可以举例进一步说明这个问题,假定计算机上的一个高性能应用程序是一辆一流赛车。如果使用传统的编程技巧,这辆赛车就是
一个单元。如果要改进该车,就必须替换整个单元,把它送回厂商,让汽车专家升级它,或者购买一个新车。如果使用OOP技术,就只需从厂商处购买新的引擎,自己按照说明替换它,而不必用钢锯切割车体。
不过大部分的论点是,javascript并不是直接的面向对象的语言,但是通过模拟可以做到很多面向对象语言才能做到的事,如继承,多态,封装,javascript都能干(没有做不到,只是想不到)
///------------------------------------------------------------------------------------------------
//以下三种构造对象的方法
//new Object,实例化一个Object
var a=new Object();
a.x=1,a.y=2;
//对象直接量
var b={x:1,y:2};
//定义类型
function Point(x,y){ //类似于C#中的类
 this.x=x;
 this.y=y;
}
var p=new Point(1,2); //实例化类
第一种方法通过构造基本对象直接添加属性的方法来实现,第二种和第一种差不多,可以看成是第一种方法的快捷表示法
第三种方法中,可以以”类“为基础,创造多个类型相同的对象

三,对象属性的封装(公有和私有)
以例子来说明
function List(){
 var m_elements=[]; //私有成员,在对象外无法访问,如果此处无var声明,则m_elements将变成全局变量,这样外部是可以直接访问到的,如alert(m_elements[0])
 m_elements=Array.apply(m_elements,arguments);
 //此处模拟getter,使用时alist.length;
 //等价于getName()方式:this.length=function(){return m_elements.length;},使用时alist.length();
 //公有属性,可以通过"."运算符或下标来访问
 this.length={
  valueOf:function(){
   return m_elements.length;
  },
  toString:function(){
   return m_elements.length;
  }
 }
 //公有方法,此方法使用得alert(alist)相当于alert(alist.toString())
 this.toString=function(){ 
  return m_elements.toString();
 }
 //公有方法
 this.add=function(){
  m_elements.push.apply(m_elements,arguments);
 }
 //私有方法如下形式,这里涉及到了闭包的概念,接下来继续说明
 //var add=function()或function add()
 //{
 //m_elements.push.apply(m_elements,arguments);
 //}
}
var alist=new List(1,2,3);
dwn(alist);  //=alert(alist.toString()),输出1,2,3
dwn(alist.length); //输出3
alist.add(4,5,6); 
dwn(alist);  //输出1,2,3,4,5,6
dwn(alist.length); //输出6

四,属性和方法的类型
javascript里,对象的属性和方法支持4种不同的类型:private property(私有属性),dynamic public property(动态公有属性),static public property/prototype property(静态公有属性或原型属性),
static property(静态属性或类属性)。私有属性对外界完全不具备访问性,可以通过内部的getter和setter(都是模拟);动态公有属性外界可以访问,每个对象实例持有一个副本,不会相互影响;原型
属性每个对象实例共享唯一副本;类属性不作为实例的属性,只作为类的属性。
以下是例子:
///------------------------------------------------------------------------------------------------
//动态公有类型,静态公有类型(原型属性)
function myClass(){
 var p=100; //private property
 this.x=10;  //dynamic public property
}
myClass.prototype.y=20; //static public property or prototype property,动态为myClass的原型添加了属性,将作用于所有实例化了的对象,注意这里用到了prototype,这是一个非常有用的东东
//要想成为高级javascript阶段,prototype和闭包必须得理解和适当应用
myClass.z=30; //static property

var a=new myClass();
dwn(a.p) //undefined
dwn(a.x) //10
dwn(a.y) //20
a.x=20;
a.y=40;
dwn(a.x); //20
dwn(a.y); //40
delete(a.x); //删除对象a的属性x
delete(a.y); //删除对象a的属性y
dwn(a.x); //undefined
dwn(a.y); //20 静态公有属性y被删除后还原为原型属性y
dwn(a.z); //undefined 类属性无法通过对象访问
dwn(myClass.z);

五,原型(prototype)
这里只讲部分,prototype和闭包都不是几句话都能讲清楚的,如果这里可以给你一些启蒙,则万幸矣
习语”照猫画虎“,这里的猫就是原型,虎是类型,可以表示成:虎.prototype=某只猫 or 虎.prototype=new 猫()
因为原型属性每个对象实例共享唯一副本,所以当实例中的一个调整了一个原型属性的值时,所有实例调用这个属性时都将发生变化,这点需要注意
以下是原型关系的类型链:
function ClassA(){
}
ClassA.prototype=new Object();
function ClassB(){
}
ClassB.prototype=new ClassA();
function ClassC(){
}
ClassC.prototype=new ClassB();
var obj=new ClassC();
dwn(obj instanceof ClassC); //true
dwn(obj instanceof ClassB); //true
dwn(obj instanceof ClassA); //true
dwn(obj instanceof Object); //true
带默认值的Point对象:
function Point2(x,y){
 if (x) this.x=x;
 if (y) this.y=y;
}
//设定Point2对象的x,y默认值为0
Point2.prototype.x=0;
Point2.prototype.y=0;
//p1是一个默认(0,0)的对象
var p1=new Point2(); //可以写成var p1=new Point2也不会出错,WHY
//p2赋值
var p2=new Point2(1,2);
dwn(p1.x+","+p1.y); //0,0
dwn(p2.x+","+p2.y); //1,2
delete对象的属性后,原型属性将回到初始化的状态:
function ClassD(){
 this.a=100;
 this.b=200;
 this.c=300
}
ClassD.prototype=new ClassD(); //将ClassD原有的属性设为原型,包括其值
ClassD.prototype.reset=function(){ //将非原型属性删除
 for (var each in this) {
  delete this[each];
 }
}
var d=new ClassD();
dwn(d.a); //100
d.a*=2;
d.b*=2;
d.c*=2;
dwn(d.a); //200
dwn(d.b); //400
dwn(d.c); //600
d.reset(); //删掉非原型属性,所有回来原型
dwn(d.a); //100
dwn(d.b); //200
dwn(d.c); //300

六,继承
如果两个类都是同一个实例的类型,那么它们之间存在着某种关系,我们把同一个实例的类型之间的泛化关系称为继承。C#和JAVA中都有这个,具体的理解就不说了。
在javascript中,并不直接从方法上支持继承,但是就像前面说的,可以模拟啊
方法可以归纳为四种:构造继承法,原型继承法,实例继承法和拷贝继承法。融会贯通之后,还有混合继续法,这是什么法,就是前面四种挑几种混着来~
以下例子来源于王者归来,其中涉及到了apply,call和一些Array的用法,有兴趣的可以自己在园子里搜索一下
1,构造继续法例子:
//定义一个Collection类型
 function Collection(size)
 {
  this.size = function(){return size};  //公有方法,可以被继承
 }
 
 Collection.prototype.isEmpty = function(){   //静态方法,不能被继承
  return this.size() == 0;
 }
 
 //定义一个ArrayList类型,它"继承"Collection类型
 function ArrayList()
 {
  var m_elements = []; //私有成员,不能被继承
  m_elements = Array.apply(m_elements, arguments);

//ArrayList类型继承Collection
  this.base = Collection;
  this.base.call(this, m_elements.length);
 
  this.add = function()
  {
   return m_elements.push.apply(m_elements, arguments);
  } 
  this.toArray = function()
  {
   return m_elements;
  }
 }

ArrayList.prototype.toString = function()
 {
  return this.toArray().toString();
 }
 //定义一个SortedList类型,它继承ArrayList类型
 function SortedList()
 {
  //SortedList类型继承ArrayList
  this.base = ArrayList;
  this.base.apply(this, arguments);

this.sort = function()
  {
   var arr = this.toArray();
   arr.sort.apply(arr, arguments);
  }
 }

//构造一个ArrayList
 var a = new ArrayList(1,2,3);
 dwn(a);
 dwn(a.size()); //a从Collection继承了size()方法
 dwn(a.isEmpty); //但是a没有继承到isEmpty()方法

//构造一个SortedList
 var b = new SortedList(3,1,2);
 b.add(4,0); //b 从ArrayList继承了add()方法
 dwn(b.toArray()); //b 从ArrayList继承了toArray()方法
 b.sort();  //b 自己实现的sort()方法
 dwn(b.toArray());
 dwn(b);
 dwn(b.size()); //b从Collection继承了size()方法
2,原型继承法例子:
//定义一个Point类型
 function Point(dimension)
 {

this.dimension = dimension;
 }

//定义一个Point2D类型,"继承"Point类型
 function Point2D(x, y)
 {
  this.x = x;
  this.y = y;
 }
 Point2D.prototype.distance = function()
 {
  return Math.sqrt(this.x * this.x + this.y * this.y);
 }
 Point2D.prototype = new Point(2);  //Point2D继承了Point

//定义一个Point3D类型,也继承Point类型
 function Point3D(x, y, z)
 {
  this.x = x;
  this.y = y;
  this.z = z;
 }
 Point3D.prototype = new Point(3);  //Point3D也继承了Point
 
 //构造一个Point2D对象
 var p1 = new Point2D(0,0);
 //构造一个Point3D对象
 var p2 = new Point3D(0,1,2);

dwn(p1.dimension);
 dwn(p2.dimension);
 dwn(p1 instanceof Point2D); //p1 是一个 Point2D
 dwn(p1 instanceof Point); //p1 也是一个 Point
 dwn(p2 instanceof Point); //p2 是一个Point
以上两种方法是最常用的
3,实例继承法例子:
在说此法例子之前,说说构造继承法的局限,如下:
function MyDate()
{
 this.base = Date;
 this.base.apply(this, arguments);
}
var date = new MyDate();
alert(date.toGMTString); //undefined,date并没有继承到Date类型,所以没有toGMTString方法
核心对象的某些方法不能被构造继承,原因是核心对象并不像我们自定义的一般对象那样在构造函数里进行赋值或初始化操作
换成原型继承法呢?,如下:
function MyDate(){}
MyDate.prototype=new Date();
var date=new MyDate();
alert(date.toGMTString); //‘[object]‘不是日期对象,仍然没有继承到Date类型!
现在,换成实例继承法:
function MyDate()
 {
  var instance = new Date(); //instance是一个新创建的日期对象
  instance.printDate = function(){
   document.write("<p> "+instance.toLocaleString()+"</p> ");
  } //对instance扩展printDate()方法
  return instance;  //将instance作为构造函数的返回值返回
 }
 var myDate = new MyDate();
 dwn(myDate.toGMTString()); //这回成功输出了正确的时间字符串,看来myDate已经是一个Date的实例了,继承成功
 myDate.printDate(); //如果没有return instance,将不能以下标访问,因为是私有对象的方法
4,拷贝继承法例子:
Function.prototype.extends = function(obj)
{
 for(var each in obj)
 {
  this.prototype[each] = obj[each]; 
  //对对象的属性进行一对一的复制,但是它又慢又容易引起问题
  //所以这种“继承”方式一般不推荐使用
 }
}
var Point2D = function(){
 //……
}
Point2D.extends(new Point())
{
 //……
}
这种继承法似乎是用得很少的。
5,混合继承例子:
function Point2D(x, y)
{
 this.x = x;
 this.y = y;
}
function ColorPoint2D(x, y, c)
{
 Point2D.call(this, x, y); //这里是构造继承,调用了父类的构造函数
 //从前面的例子看过来,这里等价于
 //this.base=Point2D;
 //this.base.call(this,x,y);
 this.color = c;
}
ColorPoint2D.prototype = new Point2D();  //这里用了原型继承,让ColorPoint2D以Point2D对象为原型

本来还想继续写闭包,不过写这个还真是很难,我也是废了好几个小时的力气才算勉强弄明白,推荐以下博文:
http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1530074.html#1884486

javascript没那么简单(转)

时间: 2024-09-29 00:33:46

javascript没那么简单(转)的相关文章

缘来幸福没那么简单

曾以为幸福一直在手中,可以信手拈来,但是原来幸福没那么简单,尤其是要等属于自己的缘来,幸福没那么简单.幸福要等待,要争取,要经营,并非理所当然,并非会一直停在那里等我去拿.  当我刚离开父母追求属于我的生活,属于我的幸福时,刚开始我觉得这是一件轻而易举的事情.我相信我可以轻松地重新找到一份我喜欢的工作,遇到一个适合我的人,然后事业爱情双丰收,就这样幸福地生活下去.我绝不会如同父母所说的那样后悔放弃已经拥有的一切,选择到一个陌生的城市重新开始的,一定要向父母证明我一定会得到幸福.  然而,当我求职

javascript标签语句简单介绍

javascript标签语句简单介绍:由于对于标签语句的应用并不多,所以可能很多朋友都不是太了解,下面就对它做一下简单介绍,希望能够给需要的朋友带来一定的帮助.标签其实是一个标示符,关于表示符这里就不多介绍了,具体可以参阅javascript标示符的概念是什么一章节, 标签可以与变量重名,它是一个独立的语法元素,它的作用是标识标签化语句(labeled statement).一.标签声明:标签可以声明在任何一个语句前面,或者语句块前,以使得语句或语句块被“标签化(labeled)”,简单的说就是

JavaScript 日期格式化 简单实用

JavaScript 日期格式化 简单实用 代码如下,引入jquery后直接后加入以下代码刷新可测试 Date.prototype.Format = function (fmt) { //author: meizz var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小时 "m+": this.

/dev/mem可没那么简单

这几天研究了下/dev/mem.发现功能非常奇妙,通过mmap能够将物理地址映射到用户空间的虚拟地址上.在用户空间完毕对设备寄存器的操作,于是上网搜了一些/dev/mem的资料. 网上的说法也非常统一,/dev/mem是物理内存的全映像,能够用来訪问物理内存,一般使用方法是open("/dev/mem",O_RDWR|O_SYNC),接着就能够用mmap来訪问物理内存以及外设的IO资源,这就是实现用户空间驱动的一种方法.用户空间驱动听起来非常酷.可是对于/dev/mem,我认为没那么简

javascript操作cookie简单插件

javascript操作cookie简单插件:恰当灵活的使用cookie可以给用户带来诸多方便,尽管它一直被不少人士所诟病,但是它的应用还是无处不在,关于cookie这里就不多介绍了,具体可以参阅javascript如何操作cookie一章节,下面给出操作cookie的一个简单插件,代码如下: var cookie={ //创建cookie setCookie:function (name, value, iDay) { var oDate = new Date(); oDate.setDate

Javascript函数的简单学习 - &lt;转&gt; 博客园

Javascript函数的简单学习 函数的定义与调用1:函数的定义    语法格式    function 函数名(数据类型 参数1){//function是定义函数的关键字        方法体;//statements,用于实现函数功能的语句        [返回值return expression]//expression可选参数,用于返回函数值            }    //1:函数名:区分大小写,并且在同一个页面中,函数名是唯一的    //2:parameter:可选参数,用于

javascript的constructor简单介绍

javascript的constructor简单介绍:constructor可以返回对象对创建它的构造函数的引用,例如: var arr=[1,2,3]; console.log(arr.constructor===Array); 以上代码中的输出值是true,这说明数组对象arr的constructor属性指向它的构造函数Array.可能上面的代码过于简单了,下面再来一点稍稍复杂的代码分析一下: Object.prototype.webName="蚂蚁部落"; function sh

[JavaScript] js实现简单的代码运行框

<script type="text/javascript">// <![CDATA[ function runCode(obj) { var winname = window.open('', "_blank", ''); winname.document.open('text/html', 'replace'); winname.document.write(obj.value); winname.document.close(); } //

编译原理实战入门:用 JavaScript 写一个简单的四则运算编译器(四)结语

四则运算编译器,虽然说功能很简单,只能编译四则运算表达式.但是编译原理前端部分几乎都有涉及,词法分析,语法分析,还有代码生成. 再复杂的编译器.再简单的编译器,功能上是差不多的,只是复杂的编译器实现上会更困难. 这个系列的文章是为了帮助你入门,在这个基础上再去看编译原理相关书籍,不至于打瞌睡. 如果你对编译原理很有兴趣,并且想更深一步的学习,在这里强烈推荐你看一本书--我心目中的神书--<计算机系统要素-从零开始构建现代计算机>. 这本书神在哪? 神在它通俗易懂,对小白足够友好,但又不过分肤浅