js 使用三

来源:互联网 发布:美国网络星期一图片 编辑:程序博客网 时间:2024/06/10 17:28

Javascript学习笔记七——原型链的原理

说到prototype,就不得不先说下new的过程。

我们先看看这样一段代码:

<script type="text/javascript">    
        var Person = function () { }; 
        var p = new Person(); 
</script>

很简单的一段代码,我们来看看这个new究竟做了什么?我们可以把new的过程拆分成以下三步:

<1> var p={}; 也就是说,初始化一个对象p。

<2> p.__proto__=Person.prototype;

<3> Person.call(p);也就是说构造p,也可以称之为初始化p。

关键在于第二步,我们来证明一下:

<script type="text/javascript">  
      var Person = function () { }; 
var p = new Person(); 
alert(p.__proto__ === Person.prototype); 
</script>

这段代码会返回true。说明我们步骤2的正确。

那么__proto__是什么?我们在这里简单地说下。每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。

按照标准,__proto__是不对外公开的,也就是说是个私有属性,但是Firefox的引擎将他暴露了出来成为了一个共有的属性,我们可以对外访问和设置。

好,概念说清了,让我们看一下下面这些代码:

<script type="text/javascript">       
     var Person = function () { }; 
Person.prototype.Say = function () { 
alert("Person say"); 

var p = new Person(); 
p.Say(); 
</script>

这段代码很简单,相信每个人都这样写过,那就让我们看下为什么p可以访问Person的Say。

首先var p=new Person();可以得出p.__proto__=Person.prototype。那么当我们调用p.Say()时,首先p中没有Say这个属性,于是,他就需要到他的__proto__中去找,也就是Person.prototype,而我们在上面定义了Person.prototype.Say=function(){}; 于是,就找到了这个方法。

好,接下来,让我们看个更复杂的。

<script type="text/javascript">      
      var Person = function () { }; 
Person.prototype.Say = function () { 
alert("Person say"); 

Person.prototype.Salary = 50000; 
var Programmer = function () { }; 
Programmer.prototype = new Person(); 
Programmer.prototype.WriteCode = function () { 
alert("programmer writes code"); 
}; 
Programmer.prototype.Salary = 500; 
var p = new Programmer(); 
p.Say(); 
p.WriteCode(); 
alert(p.Salary); 
</script>

我们来做这样的推导:

var p=new Programmer()可以得出p.__proto__=Programmer.prototype;

而在上面我们指定了Programmer.prototype=new Person();我们来这样拆分,var p1=new Person();Programmer.prototype=p1;那么:

p1.__proto__=Person.prototype;

Programmer.prototype.__proto__=Person.prototype;

由根据上面得到p.__proto__=Programmer.prototype。可以得到p.__proto__.__proto__=Person.prototype。

好,算清楚了之后我们来看上面的结果,p.Say()。由于p没有Say这个属性,于是去p.__proto__,也就是Programmer.prototype,也就是p1中去找,由于p1中也没有Say,那就去p.__proto__.__proto__,也就是Person.prototype中去找,于是就找到了alert(“Person say”)的方法。

其余的也都是同样的道理。

这也就是原型链的实现原理。

最后,其实prototype只是一个假象,他在实现原型链中只是起到了一个辅助作用,换句话说,他只是在new的时候有着一定的价值,而原型链的本质,其实在于__proto__!

Javascript学习笔记八——用JSON做原型

在Javascript学习笔记5——类和对象中,我简单地提到了利用JSON去构造一个对象。代码如下:

<script type="text/javascript">
        var People = {
name: "kym",
age: 21,
SayHello: function () {
alert("Hello,My name is " + this.name + ".I am " + this.age);
}
}
alert(People.name);
People.SayHello();
script>

但是我们是不能重用这个对象的,我们如何把这个对象作为原型的呢?

首先,在一个JSON对象有一个构造方法是不可能的了,那么我们就做一个简单的“工厂”吧,写一个方法来专门负责创建。

<script type="text/javascript">
        var People = {
Create: function (name, age) {
this.name = name;
this.age = age;
},
SayHello: function () {
alert("Hello,My name is " + this.name + ".I am " + this.age);
}
}
People.Create("kym", 21);
People.SayHello();
</script>

但是通过这个方法我们却发现,我们没有办法用People作为原型,让我们回顾一下:Javascript学习笔记7——原型链的原理 这篇文章,我们想一下这个过程:

var p=new People();==>p.__proto__=People.prototype。于是当我们p.SayHello()的时候就会去People.prototype中去找,结果什么都找不到。

如果可以People.prototype.SayHello=function(){}就可以解决这个问题。但是我们知道,只有function才可以有prototype。

那么我们想想之前的推导公式,怎么样能让p.SayHello()呢?如果可以p.__proto__=People就好了。那么我们想个办法:

既然在new的时候,某个对象的__proto__只能等于某个函数的prototype,我们设置一个函数X,令p.__proto__=X.prototype,我们再令X.prototype=People。这样的关系是这样:

<script type="text/javascript">
        var People = {
Create: function (name, age) {
this.name = name;
this.age = age;
},
SayHello: function () {
alert("Hello,My name is " + this.name + ".I am " + this.age);
}
};

var X = function () { };
X.prototype = People;
var p = new X();
p.Create("kym", 21);
p.SayHello();
</script>

这样就相当于用X做了一个中间变量,使得我们可以访问JSON对象的内部属性。但是这样是不是不太优雅呢?我们每次创建一个对象时,都需要来写这样一个辅助的函数。那好,我们就把这个过程封装起来:

var Factory = {
CreatePeople : function (className,name,age) {
var temp = function () {
className.Create(name, age);
};
temp.prototype = className;
var result = new temp();
return result;
}
};
var people = Factory.CreatePeople(People,"kym",21);
people.SayHello();

但是这样也有一个缺点,就是每次我增加一个类,就需要向Factory里注册一个新方法,这样是很麻烦的,我在很久以前的 玩转方法:call和apply 中说过关于call和apply的区别,因为这里的参数不固定,我们不可能一一列举,因此我们在这里可以用apply来改善这个方法:

<script type="text/javascript">
        var People = {
Create: function (name, age) {
this.name = name;
this.age = age;
},
SayHello: function () {
alert("Hello,My name is " + this.name + ".I am " + this.age);
}
};

var Factory = {
Create: function (className, params) {
var temp = function () {
className.Create.apply(this, params);
};
temp.prototype = className;
var result = new temp();
return result;
}
};
var people = Factory.Create(People,["kym",21]);
people.SayHello();
</script>
Javascript学习笔记九——prototype封装继承

在上文中,我利用prototype的原理做了一个封装的New,然后我就想到,我是否可以用prototype的原理进一步封装面向对象的一些基本特征呢?比如继承。

好,那就让我们一步步打造,首先让我们来看下继承原本的写法:

<script type="javascript">        var Person = function(name, age) { this.name = name;this.age = age; } Person.prototype.SayHello = function () { alert(this.name + "," + this.age); }; var Programmer = function (name, age, salary) { Person.call(this, name, age); this.salary = salary; }; Programmer.prototype = new Person(); var pro = new Programmer("kym", 21, 500); pro.SayHello(); </script>

我们看到,在实际上,继承的根本就在于这一步Programmer.prototype=new Person()。也就是说把Person加到原型链上。这一点在Javascript学习笔记7——原型链的原理 已经有过比较详尽的解释。

那也就是说,我们实现的关键就在于原型链的打造。

在上文中,我们用JSON来打造了一个原型,其原型链是p.__proto__=Person。那么我们希望在这个上封装继承,那么原型链应该是p.__proto__.__proto__=SuperClass,也就是说Person.__proto__=SuperClass。但是按照我们上面代码的继承方法,原型链关系是Person.__proto__=SuperClass.prototype。

这个和我们在上文中一样,我们的办法就是借助一个辅助函数,将原来的函数内的属性赋给X,然后令X.prototype=SuperClass即可,也就是说我们将子原型进行一个封装。

好,就按照这个思路,我们来实现利用原型链的继承关系的封装。

<script type="javascript">        var Factory = { Create: function (className, params) { var temp = function () { className.Create.apply(this, params); }; temp.prototype = className; var result = new temp(); return result; }, CreateBaseClass: function (baseClass, subClass) { var temp = function () { for (var member in subClass) { this[member] = subClass[member]; } }; temp.prototype = baseClass; return new temp(); } }; var People = { Create: function (name, age) { this.name = name; this.age = age; }, SayHello: function () { alert("Hello,My name is " + this.name + ".I am " + this.age); } }; var Temp = { Create: function (name, age, salary) { People.Create.call(this, name, age); this.salary = salary; }, Introduce: function () { alert(this.name + "$" + this.age + "$" + this.salary); } }; var Programmer = Factory.CreateBaseClass(People, Temp); var pro = Factory.Create(Programmer, ["kym", 21, 500]); pro.SayHello(); </script>

这样就完成了我们对继承关系的封装。当然,我们也可以不单独写一个变量:

var Programmer = Factory.CreateBaseClass(People, { Create: function (name, age, salary) { People.Create.call(this, name, age); this.salary = salary; }, Introduce: function () { alert(this.name + "$" + this.age + "$" + this.salary); }});

当然,这全凭个人爱好了,个人认为第一种办法相对更清晰一些,但是第二种办法则更优雅。

Javascript学习笔记十——网页运行原理

当我们打开一个网页的时候,浏览器会首先创建一个窗口,这个窗口就是我所知道的window对象,也就是整个Javascript运行所依附的全局变量。

为了加载网页文档,当前窗口又需要创建一个Document对象,然后把打开的网页加载到Document下。网页就是在这个加载的过程中,一边加载一边呈现,所以我们当网速非常慢的时候可以看到,网页从上到下一点点地打开。

当我们用<script src=’’>引入其他的JS时,浏览器可能会派遣其他线程去下载,但是浏览器也会等待需要的JS文件下载完成,然后再有主线程按顺序加载JS其他的代码。在Web标准下,限制对同一个域名最多只允许使用两个线程可以同时加载内容,当然可以通过修改注册表来强迫Windows模块突破这一限制。

同时,许多网站会把js放到不同的子域名下,这样就可以使浏览器开启更多的线程并行加载这些资源,从而更加充分地利用网络带宽。

当整个页面都加载结束后,浏览器开始触发window对象或者body对象的onload事件,其实window对象和body对象的load事件是想通的,这也就意味着两个事件只能有一个起作用。当然,在常规意义上,也没有同时设置两者的需求。

到此结束,然后JS引擎就暂停工作,等待着下一次的触发。因此我们可以说:“JS总是被动触发的”。

Javascript学习笔记十一——包装DOM对象

我们在日常的应用中,使用Javascript大多数时间都是在用DOM ,以致于很多人都有一种看法就是DOM==JS,虽然这种看法是错误的,但是也可以说明DOM的重要性。

这就导致了我们在写JS的时候,首先会考虑的是这个方法在页面上会产生什么样的变化之类的问题,用架构的思想来说:我们可以说这样把用户界面和业务逻辑掺杂到了一起。我们也知道,这样对于一个稍大的项目来说,满脑袋都是DIV,都是CSS是做不好东西的。

那么怎么办?我们还是用对象的角度,从逻辑上来考虑这个问题,让我们忘记那些DOM对象。

我们来举一个例子吧:

对于某个回复,可能是回复本贴,也可能是举报。那么暂时让我们忘记那些DOM对象,让我们想清楚逻辑:

点击“回复本贴”时,隐藏举报窗口,打开回复窗口。

点击“举报本贴”时,隐藏回复窗口,打开举报窗口。

OK,也就是说整个逻辑包含两个对象,一个是举报窗口对象,一个是回复窗口对象,每个对象有两个方法,一个是打开,一个是隐藏。由于某个页面可能会有很多这样的对象,每个对象都应该是被创建的一个原型,于是就应该有这样的代码:

<script type="text/javascript"> var Comment = function (x, y) { var x = x; var y = y; }; Comment.prototype.Create = function () { }; Comment.prototype.Hide = function () { }; var Report = function (x, y) { var x = x; var y = y; }; Report.prototype.Create = function () { }; Report.prototype.Hide = function () { };</script>

至于逻辑就是:

buttonCommert.onclick = function () { GetReport(“id”).Hide(); HideOthers(); // 关闭本页面其它的回复窗口 var c = new Comment("1","1"); c.Create();}

举报按钮也近似如此。

好了大致逻辑如此,我们需要的只是实现原型中的创建和隐藏方法。

var Comment = function (x, y) { var x = x; var y = y; var ConfirmComment = function () { //Ajax提á交?评à论? };};Comment.prototype.Create = function () { var com = document.createElement("div"); document.getElementById("XXXX").appendChild(com); com.x = x; com.y = y; com.style.left = "XXpx"; com.style.top = "YYpx"; var txt = document.createElement("input"); txt.nodeType = "text"; com.appendChild(txt); var btn = document.createElement("input"); btn.nodeType = "button"; btn.onclick = ConfirmComment(); com.appendChild(btn);};

以上皆为伪代码,只是提供一种封装DOM的思路。

在工程中,将DOM对象包装成符合我们自己业务逻辑的Javascript对象是一种非常好的做法,这也是企业JS库形成的一个过程。

说句极端话,如果足够成熟后,也许页面中写JS看不到DOM,而皆为包装好的JS对象,笑谈尔….

Javascript学习笔记十二——Ajax入门

Ajax:Asynchronous Javascript And XML。写个简单的例子:

<body>    <form id="form1" runat="server">    <div>        <asp:Label ID="LabelTime"runat="server">asp:Label>    </div>    </form>    <script type="text/javascript">        if (!window.XMLHttpRequest) {            window.XMLHttpRequest = function () {               return new ActiveXObject("Microsoft.XMLHTTP");            };        }        function UpdateClock() {            var request = new XMLHttpRequest();            request.open("post", "TimeTest.aspx", false);            request.send("");            document.getElementById("LabelTime").innerText = request.responseText;        }        setInterval(UpdateClock, 1000);    </script></body>

而在另一个页面写下当前时间,这样就形成了一个钟表。

代码很简单,就是操纵一个XMLHttpRequest对象来获取服务器时间,然后更新时间。上面的代码在与服务器交互时,并没有页面整体刷新,而是局部刷新。

但是上面的代码在request.open时,最后一个参数为false,表示发出的XMLHttpRequest是同步的,由于Javascript是单线程的,所以在等待请求的过程中,线程会被阻塞,如果请求时间过长,浏览器会停止响应。

虽然Javascript是单线程的,但是XMLHttpRequest具备异步处理请求的能力。代码如下:

<body>    <form id="form1" runat="server">    <div>        <asp:Label ID="LabelTime"runat="server">asp:Label>    </div>    </form>    <script type="text/javascript">        if (!window.XMLHttpRequest) {            window.XMLHttpRequest = function () {               return new ActiveXObject("Microsoft.XMLHTTP");            };        }        function AsynRequest() {            var request = new XMLHttpRequest();            request.open("post", "TimeTest.aspx", true);            request.onreadystatechange = function () {                if (request.readyState === 4) {                    UpdateClock(request.responseText);                }            };            request.send("");        }        function UpdateClock(time) {            document.getElementById("LabelTime").innerText = time;        }        setInterval(AsynRequest, 1000); </script></body>

哎,没有智能提示的日子真不好过。

Javascript学习笔记十三——关于响应事件

具体的事情记不清了,某一天下班后听到两个同事在说关于关于事件的问题。

这里简单说下:

<input type="button" runat="server"  value="Click Me" id="ButtonTest" />

很普通的一个按钮,我们要为其添加点击事件,有两种方法:

A.

<input type="button" runat="server" onclick="Alert()" value="Click Me" id="ButtonTest" />    <script type="text/javascript">        function Alert() { alert("Click Me"); } </script>

B.

<input type="button" runat="server" value="Click Me" id="ButtonTest" />    <script type="text/javascript">        document.getElementById("ButtonTest").onclick = function () { alert("Click Me"); } </script>

我们称A方法为静态绑定,B方法为动态绑定。我们来看A方法,他近乎可等于这样的效果:

<input type="button" runat="server" onclick="Alert()" value="Click Me" id="ButtonTest" />    <script type="text/javascript">        document.getElementById("ButtonTest").onclick = function () { Alert(); } </script>

也就是说,当我们静态绑定一个事件的时候,实际上是系统默认为我们做了一个匿名函数,然后把我们的方法体包于其中。由于这样,就涉及到了this的问题。

我们来看这样一段代码,这也是我在公司初学JS的时候,公司的JS小牛抛给我的问题。

<input type="button" runat="server" onclick="Alert1()" value="Test1" id="ButtonTest1" />    <input type="button" runat="server" onclick="Alert2(this)" value="Test2" id="ButtonTest2" />    <script type="text/javascript">        function Alert1() { alert(this.value); } function Alert2(obj) { alert(obj.value); } </script>

当试验这样一段代码时,便会发现第一个按钮会弹出”undefined”,第二个按钮弹出”Test2”。原因就是如我上面所说,在按钮一中,方法等于声明了一个匿名函数,然后讲Alert1()闭包于其中,也就是说,Alert1()是无法找到其调用者的,所以这时,他会去找window对象的value属性,结果发现未定义,我们可以这样证明:

<input type="button" runat="server" onclick="Alert1()" value="Test1" id="ButtonTest1" />    <input type="button" runat="server" onclick="Alert2(this)" value="Test2" id="ButtonTest2" />    <script type="text/javascript">        value = "window"; function Alert1() { alert(this.value); } function Alert2(obj) { alert(obj.value); } </script>

运行上面的代码,就可以很好地理解问题了!

原创粉丝点击