一件任务的完成,意味着另外任务的开始,工作就这样,周而复始,就像春夏秋冬一样,四季轮回,其实工作是做不完的,上一个工作任务也许并没有完成,而下一个任务已经有了安排,写代码,搞程序,其实里面有很多东西是不可控制的,一个问题,一个似乎觉得微不足道的问题,可能让人绞尽脑汁,没有源码的帮助,如果是单纯的二次开发,有的时候真不知道问题出在什么地方,我这一段时间就被一个乱码问题折腾的要死,各种编码尝试都不见效果,这叫我如何是好?是我的问题,是程序的bug?
程序员也要不断的学习,因为你不会知道你的下一个任务是什么,下一个任务是用什么语言,就像年初一样,我也不会知道自己会用JavaScript将写DEMO一样,还好,这么多年的学习以及这么多年的工作让自己有了些博客的“好习惯”,博客的内容可能不是那么的动人,也不可能像电视剧那样有故事情节,扣人心弦,但是却将自己的想法,心得记录下来,去跟同道中人分享,我其实不算一个多产的博主,网络上多产的博主那都是身经百战的大侠,我顶多是班门弄斧,我喜欢看博客,这些大侠的博客往往在告诉一些道理,或者那些容易出错的地方,甚至是一些自己的经验之谈,博客似乎在默默的充当着我们这些网虫交流的平台。
现在就是想将我学习JavaScript的一些心得记录下来。学习任何东西,都应知道这个东西的应用范畴,如果能了解这个东西的历史背景,那更好不过了,因为任何东西都不是凭空而来,能存活到现在的也可说是历尽千辛万苦,有人说过,存在即合理,也许我们不应该怀疑这句话,我也不算一个职业程序员,那么就用我的愚见说一下自己对学习这个东西的认知。
任何语言都有自己的数据类型,数据类型决定了如何存储数据以及如何使用数据,比如对String类型的数据如何操作,在JavaScript中数据类型可以从下图看出:
2,对象
JavaScript中的对象其实有种定义方法,可以通过函数,可以通过JSON格式的描述,记住一点JS中的对象是可以动态的添加方法和属性的。
var ob1 = {};
ob1.name = "liuyu";
ob1.say = function () {
alert("hello! " + this.name);
}
ob1.say();
var ob2 = { name: "liuyu",
say: function () {
alert("hello!!" + this.name);
}
};
ob2.say();
function person(name) {
this.say = function () {
alert(name);
};
}
var ob3 = new person("liuyu");
ob3.say();
3,奇怪的this
在OOP编程中,this这个词语对我们来说简直就是司空见惯,而且含义也很明确就是当前对象,对于C#/Java来说,就是我们new的这个对象,可以用一成不变来描述,但是在JavaScript中,这个This是变化的,可能是指向window对象,也可能是指向非window对象,下面的例子就可以知道:
var x = 1; function Test() { this.x = 0; }; Test(); alert(x); liuyu.getDom("test", "hello!"); var ob = new Test(); alert(x);
两次分别弹出的数据是0,1,这是为什么呢?原因就出现在this,当直接用Test方法,这this其实指向的就是window对象,当使用new了之后,this就是ob对象,这个是因为在JavaScript语言中有一个作用域的概念,任何一个变量都在自己的作用域内生存,当new了一个对象之后,那么就会在当前作用域对象上产生一个新的作用域。在JavaScript,全局环境本身就一个对象。在浏览器宿主中这个对象是window,而当Javascript用于其它非浏览器的宿主,如嵌入式的环境中,可能会是其它的对象。我们开始定义的变量都是在window这个作用域上的,上面的Test()方法就相当于window.Test(),是在window被省略而已,但是用了new之后,Test就不在是window的了。但是记住一点this始终指向当前的作用域对象。如果说这样还不算很清楚,那我就多说点,JS中对象的生成其实是分了三个步骤:
function person(name, age) {
this.name = name;
this.age = age;
}
var ob4 = new person("刘宇",25);
alert(ob4.name);
var ob5 = {};
ob5.__proto__ = person.prototype;
person.call(ob5, "liuyu", 25);//person.apply(ob5,[])参数为数组,更改作用域
alert(ob5.age);
先声明一个对象,ob5,然后将函数的原型赋给对象的内置属性__proto__,然后调用函数的call方法或者apply方法,这个方法就是初始化对象,将函数内部的this指向ob5,或者替换ob5,也就是说如果不用new的时候,函数中的this就是window,而new了之后函数中的this就成了ob5,在JS中this是可以改变的。
4,波粒二象性的函数
这个波粒二象性应该是物理学中的概念,我牵强的在这里借用下,因为JS中的函数可以充当很多角色,JS中的函数可以充当对象,也可以充当构造函数,甚至可以充当“类”(JS中并没有类的说法),就因为这个函数有很多的角色,我才借用了这个说辞。在Javascript中,函数是Function类的实例,Function间接继承自Object,所以,函数也是一个对象,因此,我们可以用赋值的方法创建函数,当然,我们也可以将一个函数赋给类的一个属性变量,那么,这个属性变量就可以称为方法,因为它是一个可以执行的函数。如下面的例子:
function persontest(name) {
alert(name);
};
persontest("liuyu");
function Shape() {
var z = 8;
var y = 1;
this.draw = function () {
alert(z);
};
};
var ob6 = new Shape();
ob6.draw();
var fun1 = function Shape() {
var z = 8;
var y = 1;
alert(z);
};
fun1();
5,原型
原型其实是一个对象,原型在JS中并不好理解,还是从例子说起:
function Person( name, age ){
this.name = name;
this.age = age;
}
Person.prototype = {
getName:function(){
return this.name;
},
getAge:function(){
returnthis.age;
}
}
var ob7 = new Person("liuyu", 25);
ob7.getName();
我们发现这个代码居然可以运行成功,但是我们在Person中并没有getName这个方法,代码居然毫无错误,居然有了正确的答案,真是发人深思,这必须归功于原型,每个函数都有一个prototype的属性,这个属性其实一个对象,成为原型对象,当一个对象在已经定义的函数中没找到方法或者属性,那么他就会去这个函数的原型对象中去找,以此类推,直到找到为止。现在分析一下ob7,ob7这个对象内部并没有定义getName方法,但是在Person.prototype这个原型对象中有,所有ob7,就在这个原型对象中找到了,并调用了。原型类似数据机构中的链表指针,指向了下一个对象,之所以能找到这个,还不仅仅如此,这是因为一个对象内部有一个内建的__proto__对象,还记得上面介绍过的对象创建三步骤么?第二部就用到了这个对象。对象的__proto__就是函数的原型对象,那么真正的寻找属性和方法就是依靠这个__proto__对象的,尽管这个对我们来说不可见,但是这是其内部实现原理。
function person(name, age) {
this.name = name;
this.age = age;
}
var ob4 = new person("刘宇", 25);
alert(ob4.name);
var ob5 = {};
ob5.__proto__ = person.prototype;
person.call(ob5, "liuyu", 25); //person.apply(ob5,[])参数为数组,更改作用域
alert(ob5.age);
alert(person.constructor);
alert(person.prototype.constructor);
alert(ob5.constructor);
alert(person === person.prototype.constructor);
alert(person === ob5.constructor); //IE和Chrome显示的结果不一样,从已有的知识判断后者是正确的。
上面可能是让人费解的话,下面我们就图文并茂的进行解释和说明:
var foo = {
x: 10,
y: 20
};
下例中b和c共享a的属性和方法,同时又有自己的私有属性。__proto__默认的也有指向。它指向的是最高级的object.prototype,而object.prototype的__proto__为空。
var a = {
x: 10,
calculate: function (z) {
return this.x +this.y+ z
}
};
var b = {
y: 20,
__proto__: a
};
var c = {
y: 30,
__proto__: a
};
// call the inherited method
b.calculate(30); // 60
理解了__proto__这个属性链接指针的本质,我们可以继续深入创建对象的函数我们称之为构造函数,这个和其它语言无异,constructor始终指向创建当前对象的构造(初始化)函数。每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数,一个对象比如说ob5之所以有constructor这个属性,那是因为这个constructor是在原型对象上的,看下面的例子:
function Foo(y){
this.y = y ;
}
Foo.prototype.x = 10;
Foo.prototype.calculate = function(z){
return this.x+this.y+z;
};
var b = new Foo(20);
alert(b.calculate(30));
function person(name, age) {
this.name = name;
this.age = age;
}
var ob4 = new person("刘宇", 25);
alert(ob4.name);
var ob5 = {};
ob5.__proto__ = person.prototype;
person.call(ob5, "liuyu", 25); //person.apply(ob5,[])参数为数组,更改作用域
alert(ob5.age);
function SetClass(clss) {
this.clss = clss;
};
var c = new SetClass("大二");
person.prototype = c;
var p = new person("liuyu", 25);
alert(p.constructor === person); // false
alert(person.prototype.constructor === person); // false
alert(person.prototype.constructor === SetClass); // true
6,闭包
闭包的定义非常晦涩——闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值(深连结)。简单来说,闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁。周爱民说得更清楚,闭包就是“属性表”,闭包就是一个数据块,闭包就是一个存放着“Name=Value”的对照表。就这么简单。但是,必须强调,闭包是一个运行期概念。
在Javascript中闭包(Closure),有两个特点:
- 作为一个函数变量的一个引用 - 当函数返回时,其处于激活状态。
- 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
关于闭包可以看我以前的博文:
7,自执行函数
自执行函数属于一种特殊的函数,在C#中,我们可能会将常用的功能封装为一个类,或者一个dll,那么在Javascript中其实也可以达到类似的效果,往往我们会给window对象附加一个作用域,或者对象,这个对象上包含我们自定义操作的所有方法,下面是我封装的一个简单的操作:
(function (window, undefined) {
window.liuyu = {};
window.liuyu.getDom = function (id,text) {
document.getElementById(id).innerHTML = text
}
})(window)
<div>
<ul><li id="1"></li> </ul>
<ul><li id="2"></li> </ul>
<select>
<option id="test">1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
调用代码 liuyu.getDom("test", "hello!");
执行后的效果:
算是耐住性子写完了,写这么一篇花费了不少时间,其实写的目的是记住这些和加深理解,所谓好记性不如烂笔头,最好亲自尝试下,总结是最好的老师,在总结的时候,在用代码模拟的时候可能会有很多意想不到的收获,也可能会有新的疑惑,有疑惑,只要去找,那么一定会有答案,每一次的疑惑都伴随着很多艰辛,只要过了这个,那么自己也就在不知不觉的进步,在东西的时候为了力求准确,可能会不断的搜索材料,为了说明清楚可能会采用不同的方法,什么图文并茂,什么代码示例……,写技术博客不必言情小说那么细腻动人,没有什么妙词佳句,有的时候也很晦涩难懂,但是我尽量将自己的想法和盘托出。写这篇博文看了不少资料,但水平有限,还望批评指正。