Javascript是一门极易上手却难于精通的语言,有人说javascript是一门基于对象的语言,并非一门面向对象的语言,可是这门语言的发明者明确告诉了咱们,这是一门面向对象的语言。javascript就像是面向对象中的90后,总不被大多数人所接受,它是基于原型的面向对象的语言。并非一般的基于类的。
1、核心javascript
一、数据类型 javascript做为一门解释型的脚本语言,它是弱类型的,它有三种基本数据类型——boolean,number,string。特殊的值:null,undefined。除了这些基本类型javascript还支持一种复合数据类型——object(对象)。
布尔类型有2个值:true和false。
数字是64位的浮点值,与Java的Double相似。它没有整型。除法运算可能带来小数位的结果。数字包括特殊值NaN(不是一个数字)和Infinity(无穷大)。
字符串(string)是从零到多个Unicode字符组成的。没有单独的字节类型。一个字节被描绘成一个长度为1的字符串。字符串被'符号或"符号引用到一块儿,单引号和双引号能够替换使用,但必须先后匹配使用。
函数是一种特殊的对象。由于它包含了一段能够执行的代码。
二、对象是词典 在 JavaScript 中,对象只是一组名称/值对,就是说,将 JavaScript 对象视为包含字符串关键字的词典。咱们可使用熟悉的“.”(点)运算符或“[]”运算符,来得到和设置对象的属性,这是在处理词典时一般采用的方法。如下代码段:
var userObject = new Object();
userObject.lastLoginTime = new Date();
alert(userObject.lastLoginTime);
的功能与下面的代码段彻底相同:
var userObject = {}; // equivalent to new Object()
userObject[“lastLoginTime”] = new Date();
alert(userObject[“lastLoginTime”]);
咱们还能够直接在 userObject 的定义中定义 lastLoginTime 属性,以下所示:
var userObject = { “lastLoginTime”: new Date(), "userName": "NanGe" };
alert(userObject.lastLoginTime);
用for语句能够实现对象一个枚举的能力:
for (var n in userObject ) {
if (userObject.hasOwnProperty(n)) {
document.writeln("<p>" + n + ": " + userObject + "</p>");
}
}
这个结果应该是:
<p>lastLoginTime: new Date()</p>
<p>userName: NanGe</p>
JavaScript的对象模式是JSON的数据交换格式的基础。 新的成员能够在任什么时候候被附着增长到任何对象上。如:
userObject .password = "666666";
三、数组 JavaScript中数组也是哈希表对象,当你声明了一个数组,你不须要声明它的大小,数组会自增加,这很像Java的向量(vectors)或者是ArrayList。有2种方式建立一个新数组:
var myArray = [ ];
var myArray = new Array();
数组不是强类型,它能够包括数字、字符串、布尔值、对象、函数和数组,你能够在一个数组中混合使用字符串、数字和对象。数组的第一个索引是0。
四、灵活的函数 在大部分编程语言中,函数和对象一般是不一样的东西,在javascript中,函数即对象,只是它有点特别——它是具备与它关联的可执行代码的对象。先来看一个普通函数:
function func(x) {
alert(x);
}
func(“blah”);
这是一般的建立函数的方法;可是,你还能够经过建立一个匿名函数对象,而后将它赋值给一个变量。如:
var func = function(x) {
alert(x);
};
func("blah");
甚至你还能够这样,使用Function构造函数:
var func = new Function("x", "alert(x);");
func("blah");
此示例代表函数实际上只是支持函数调用操做的对象。最后一个使用 Function 构造函数来定义函数的方法并不经常使用,但它展现的可能性很是有趣,由于你可能注意到,该函数的主体正是 Function 构造函数的 String 参数,而且最后一个参数才是函数的主体,以前的参数全是函数的参数。这意味着,您能够在运行时构造任意函数。
为了能进一步说明函数即对象,你能够像其余任何javascript对象同样,对函数设置或者添加属性。好比:
functiion func(x) {
alert(x + func.text);
}
func.text = "Hello World!";
func["text2"] = "Hello World .... again!";
alert(func["text"]); //display "Hello World!"
alert(func.text2); //display "Hello World .... again!"
func("Hello "); //display "Hello Hello World!"
做为对象,函数还能够赋给变量、做为参数传递给其余函数、做为其余函数的值返回,并能够做为对象的属性或数组的元素进行存储等等。下面的例子将对其进行说明:
// assign an anonmyous function to a variable
var greet = function(x) {
alert("Hello " + x);
}
greet("nange");
// passing a function as an argument to another
function square(x) {
return x * x;
}
function operateOn(num, func) {
return func(num);
}
alert(operateOn(16, square)); // display 256
// function as return value
function makeIncrement() {
return function(x) { return x + 1;};
}
var inc = makeIncrement();
alert(inc(7)); // display 8
// function stored as array element
var arr = [];
arr[0] = function(x) { return x * x; };
arr[1] = arr[0](2);
arr[2] = arr[0](arr[1]);
arr[3] = arr[0](arr[2]);
alert(arr[3]); // display 256
// function as object propertie
var obj = {"toString": function() { return "this is an object!"; }};
alert(obj); // display "this is an object!"
向对象中添加方法是一件很是容易的事情,只须要记住名称,而后将函数赋值给该名称便可,咱们能够方便的使用匿名函数来完成这件事(固然了,你也能够不使用匿名函数)。以下所示:
var myDog = {
"name" ; "HuaHua",
"bark" : function() { alert("Woof !"); },
"displayFullName" : function() {
alert(this.name + "the beautiful dog !");
},
"otherMethod" ; function() {
// implementation beyond the scope of this aticle
}
};
myDog.displayFullName(); // display "HuaHua the beautiful dog !"
myDog.bark(); // display "Woof !"
上面的例子中,displayFullName方法中的“this”值的就是myDog对象,由于它位于myDog对象中,那么当前的对象就是myDog。可是"this"的值不是静态的,经过不一样对象调用"this"时,它的值也会更改,以便指向相应的对象。以下所示:
function callWhichName() {
alert(this.name);
}
var nange = {
"name" : "nange",
"sayIt" : callWhichName
};
var zhangsan = {
"name" : "zhangsan",
"sayIt" : callWhichName
};
var lisi = {
"name" : "lisi",
};
nange.sayIt(); // display "nange"
zhangsan.sayIt(); // display "zhangsan"
callWhichName.call(lisi); // display "lisi"
上面的例子中,最后一段代码表示的是,将函数做为对象的方法进行调用的另一种方式。为何函数"callWhichName"能够调用"call"方法?请记住,JavaScript 中的函数是对象。每一个函数对象都有一个名为 call 的方法,它将函数做为第一个参数的方法进行调用。就是说,做为函数第一个参数传递给 call 的任何对象都将在函数调用中成为“this”的值。
有一点须要注意的是,必定不要直接调用包含"this"(却没有所属对象,即全局对象以外的对象)关键字的函数,不然将可能影响全局命名空间,由于此时的"this"指的是全局对象(在浏览器中即window),这极可能会产生灾难性的后果。例如,下面的代码将改变javascript全局函数isNaN的行为。必定不要这样作!
alert("NaN is NaN:" + isNaN(NaN)); // dispaly true
function x() {
this.isNaN = function() {
return "not any more!";
};
}
x();
alert("NaN is NaN:" + isNaN(NaN)); // dispaly "not any more!"
到这里,咱们已经介绍了如何建立对象,包括它的属性和方法。但若是注意上面的全部代码段,你会发现属性和方法是在对象定义自己中进行硬编码的。但若是须要更好地控制对象的建立,该怎么作呢?例如,您可能须要根据某些参数来计算对象的属性值。或者,可能须要将对象的属性初始化为仅在运行时才能得到的值。也可能须要建立对象的多个实例(此要求很是常见)。
在Java中,咱们一般是采用类来实例化对象实例,但在javascript中有所不一样,javascript没有类,这时,函数在与"new"关键字一块儿使用时,函数就变成了构造函数。下一节将对其进行说明。
五、构造函数 前面提到过,javascript的OOP就像OOP中的90后,它比较异类,由于没有类的概念。当一个函数用于初始化对象的时候就被叫作构造函数。一个函数构造函数带着"new"前缀被调用。如:
new Constructor(parameters...)
根据习惯,咱们将构造函数的首字母大写。
调用构造函数时,将会返回一个新的对象(除非明确的使用了return语句进行替换),new前缀改变了this的含义,this将引用这个返回的对象。来看一个例子:
function Dog(name) {
this.name = name;
this.respondTo = function(name) {
if (this.name == name) {
alert("Woof !");
}
}
}
var spot = new Dog("Spot");
spot.respondTo("Rover"); // display none
spot.respondTo("Spot"); // display "Woof !"
咱们观察上面的例子能够发现,每个Dog的实例对象,都有本身的name实例副本,这是咱们所但愿的,由于每一个实例都须要一个name属性来表示其状态,但同时,每一个实例对象也都有一个respondTo方法的实例副本,这就是一个浪费,由于每一个实例其实均可以共享同一个方法,这应该怎么办呢? javascript为咱们提供了原型对象,就能够解决这个问题。下一节对其进行介绍。
六、原型(Porototype) 以前咱们提到过,javascript是基于原型的面向对象的编程语言。因此在javascript的面向对象编程中,原型对象是个核心概念。在 JavaScript 中,每一个函数都有名为“prototype”的属性,用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数自己。这是一种循环引用。下图就描述了这种循环引用的关系:
下面的代码能进一步说明问题:
var spot = new Dog("Spot");
alert(Dog.prototype.isPrototypeOf(spot)); // display true
alert(spot.constructor == Dog.prototype.constructor); // display true
alert(spot.constructor == Dog); // display true
alert(spot.hasOwnProperty("constructor")); // display false why?? because constructor property doesn't belong to spot.
alert(Dog.prototype.hasOwnProperty("constructor")); // display true
从上面的例子中,你可能注意到:对 hasOwnProperty 和 isPrototypeOf 方法的调用。这些方法是从哪里来的呢?它们不是来自 Dog.prototype。实际上,在 Dog.prototype 和 Dog 实例中还能够调用其余方法,好比 toString、toLocaleString 和 valueOf,但它们都不来自 Dog.prototype。就像在Java中,全部类都继承自Object类同样,在javascript中,Object.prototype 是全部原型的最终基础原型。(Object.prototype 的原型是 null。)在此示例中,请记住 Dog.prototype 是对象。它是经过调用 Object 构造函数建立的(尽管它不可见)。
Dog.prototype = new Object();
所以,正如 Dog 实例继承 Dog.prototype 同样,Dog.prototype 继承 Object.prototype。这使得全部 Dog 实例也继承了 Object.prototype 的方法和属性。
每一个 JavaScript 对象都继承一个原型链,而全部原型都终止于 Object.prototype。注意,迄今为止你看到的这种继承是活动对象之间的继承。它不一样于继承的常见概念,后者是指在声明类时类之间的发生的继承。所以,JavaScript 继承动态性更强。它使用简单算法实现这一点,以下所示:当你尝试访问对象的属性/方法时,JavaScript 将检查该属性/方法是不是在该对象中定义的。若是不是,则检查对象的原型。若是还不是,则检查该对象的原型的原型,如此继续,一直检查到 Object.prototype。以下图所示:
JavaScript 动态地解析属性访问和方法调用的方式产生了一些特殊效果:
- 继承原型对象的对象上能够当即呈现对原型所作的更改,即便是在建立这些对象以后。
- 若是在对象中定义了属性/方法 X,则该对象的原型中将隐藏同名的属性/方法。例如,经过在 Dog.prototype 中定义 toString 方法,能够改写 Object.prototype 的 toString 方法。
- 更改只沿一个方向传递,即从原型到它的派生对象,但不能沿相反方向传递。
下面的代码将说明上面这些效果:
function Dog() { }
var redDog = new Dog();
var greenDog = new Dog();
Dog.prototype.greet = function() {
return "Hello My Host";
};
alert(redDog.greet()); // display "Hello My Host"
greenDog.greet = function() {
return "Hello My Host .... again";
};
alert(greenDog.greet()); // display "Hello My Host .... again"
alert(redDog.greet()); // display "Hello My Host"
七、变量做用域 JavaScript的变量做用域有三个特色:做用域是基于其特有的做用域链的、没有块级做用域、函数中声明的变量在整个函数中都有定义。看下面一些例子:
var rain = 1;
function rainman(){
var man = 2;
function inner(){
var innerVar = 4;
alert(rain);
}
inner();
}
rainman(); // display 1
做用域链:JavaScript须要查询一个变量x时,首先会查找做用域链的第一个对象,若是以第一个对象没有定义x变量,JavaScript会继续查找第二个对象有没有定义x变量,若是第二个对象没有定义则会继续查找,以此类推。
上面的代码涉及到了三个做用域链对象,依次是:inner、rainman、window。
- 函数内部,局部变量的优先级比同名的全局变量优先级高
var rain = 1; // assign global variable rain
function check(){
var rain = 100; // assign local variable rain
alert( rain ); // display 100
}
check();
alert( rain );
这一点也是JavaScript相比其它语言较灵活的部分。仔细观察下面的代码,你会发现变量i、j、k做用域是相同的,他们在整个rain函数体内都是全局的。
function rainman(){
// i, j, k as three local variable in function rainman
var i = 0;
if ( 1 ) {
var j = 0;
for(var k = 0; k < 3; k++) {
alert( k ); // display 0 1 2
}
alert( k ); // display 3
}
alert( j ); // display 0
}
function rain(){
var x = 1;
function man(){
x = 100;
}
man();
alert( x ); // display 100
}
rain();
上面得代码说明了,变量x在整个rain函数体内均可以使用,并能够从新赋值。因为这条规则,会产生“匪夷所思”的结果,观察下面的代码:
var x = 1;
function rain(){
alert( x ); // display 'undefined',not 1
var x = 'rain-man';
alert( x ); // display 'rain-man'
}
rain();
这是因为在函数rain内局部变量x在整个函数体内都有定义( var x= 'rain-man',进行了声明),因此在整个rain函数体内隐藏了同名的全局变量x。这里之因此会弹出'undefined'是由于,第一个执行alert(x)时,局部变量x仍未被初始化。
因此上面的rain函数等同于下面的函数:
function rain(){
var x;
alert( x );
x = 'rain-man';
alert( x );
}
function rain(){
x = 100; // assigned a global variable x and initailize it to 100
}
rain();
alert( x ); // display 100
var x = 100 ;
alert( window.x );//弹出100
alert(x);
八、理解闭包(高手的必经阶段) 闭包是当内部函数绑定到它的外部函数的本地变量时所发生的运行时现象。很明显,除非此内部函数以某种方式可被外部函数访问,不然它没有多少意义。示例能够更好说明这一点。
假设须要根据一个简单条件筛选一个数字序列,这个条件是:只有大于 100 的数字才能经过筛选,并忽略其他数字。那么咱们一般能够编写以下代码:
function filter(pred, arr) {
var len = arr.length;
var filtered = []; // shorter version of new Array();
// iterate through every element in the array...
for(var i = 0; i < len; i++) {
var val = arr[i];
// if the element satisfies the predicate let it through
if(pred(val)) {
filtered.push(val);
}
}
return filtered;
}
var someRandomNumbers = [12, 32, 1, 3, 2, 2, 234, 236, 632,7, 8];
var numbersGreaterThan100 = filter(
function(x) { return (x > 100) ? true : false; },
someRandomNumbers);
// displays 234, 236, 632
alert(numbersGreaterThan100);
可是,如今要建立不一样的筛选条件,假设此次只有大于 300 的数字才能经过筛选,则能够编写下面这样的函数:
var greaterThan300 = filter( function(x) { return (x > 300) ? true : false; }, someRandomNumbers);
而后,也许须要筛选大于 50、2五、十、600 如此等等的数字,但做为一个聪明人,你会发现它们所有都有相同的谓词“greater than”,只有数字不一样。所以,能够用相似下面的函数分开各个数字:
function makeGreaterThanPredicate(lowerBound) {
return function(numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}
这样,您就能够编写如下代码:
var greaterThan10 = makeGreaterThanPredicate(10);
var greaterThan100 = makeGreaterThanPredicate(100);
alert(filter(greaterThan10, someRandomNumbers));
alert(filter(greaterThan100, someRandomNumbers));
经过观察函数 makeGreaterThanPredicate 返回的内部匿名函数,能够发现,该匿名内部函数使用 lowerBound,后者是传递给 makeGreaterThanPredicate 的参数。按照做用域的通常规则,当 makeGreaterThanPredicate 退出时,lowerBound 超出了做用域!但在这里,内部匿名函数仍然携带 lowerBound,甚至在 makeGreaterThanPredicate 退出以后的很长时间内仍然如此。这就是咱们所说的闭包:由于内部函数关闭了定义它的环境(即外部函数的参数和本地变量)。
开始可能感受不到闭包的功能很强大。但若是应用恰当,它们就能够很是有创造性地帮您将想法转换成代码,这个过程很是有趣。在 JavaScript 中,闭包最有趣的用途之一是模拟类的私有变量。
function Person(name, age) {
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
this.getAge = function() { return age; };
this.setAge = function(newAge) { age = newAge; };
}
参数 name 和 age 是构造函数 Person 的本地变量。Person 返回时,name 和 age 应当永远消失。可是,它们被做为 Person 实例的方法而分配的四个内部函数捕获,实际上这会使 name 和 age 继续存在,但只能严格地经过这四个方法访问它们。以下:
var nange = new Person("nange", 23);
alert(nange.getName()); // display "nange"
alert(nange.getAge()); // display 23
nange.setName("nange2");
nange.setAge(24);
alert("now person is :" + nange.getName() + ", " + nange.getAge()); // display "now person is : nange2, 24"
九、返回值 JavaScript没有void类型,因此每一个函数都必须返回一个值,除了构造器以外默认值是undefined,构造器的默认返回值是this。
十、语句 语句包括 var, if, switch, for, while, do, break, continue, return, try, throw, and with,它们大多数与C类的语言相同。
var语句是一个或多个变量名称的列表,它们以逗号隔开,带着可选的初始化表达式。
var a, b = window.document.body;
若是var语句出如今全部的函数以外那么就声明了一个全局的变量。若是出如今一个函数内,就声明了一个局部的变量。
在if语句,while语句,do语句, 和符合逻辑的操做符中,JavaScript视为false,null,undefined, ""(空字符串),和数字0做为假(false)。其它全部值被视为真(true)。
在switch语句中的case标记能够是表达式,它们没必要非得是常数,也能够是字符串。
有2种形式的for语句,第一种像这样 (init; test; inc)形式。第二种是对象重复器。
for (name in object) {
if (object.hasOwnProperty(name)) {
value = object[name];
}
}
上面执行在object中的每个name,名字产生的次序是不肯定的。
with语句不该该被使用。
2、浏览器中的Javascript
待续。。。