您的位置:首页 > Web前端 > JavaScript

JavaScript介绍

2017-06-10 23:19 316 查看
    Javascript不像主流语言如C、Java那么备受重视,但它确实是一门重要的语言。因为它与浏览器的结合使它成为世界上最流程的编程语言之一,它的许多特性都借鉴自其他语言:语法借鉴自Java,函数借鉴自Schema,原型借鉴自Self,正则表达式借鉴自Perl。

一、命名规则:

    变量命名与Java命名规则相同,由字母、数字、下划线和$符号组成,区分大小写,不能以数字开头,不能包含保留字,这些保留字是:abstract、boolean、break、byte、case、catch、char、class、const、continue、debugger、default、delete、do、double、else、enum、export、extends、false、final、finally、float、for、function、goto、if、implements、import、in、instanceof、int、interface、long、native、new、null、package、private、protected、public、return、short、static、super、switch、synchronized、this、throw、throws、transient、true、try、typeof、var、volatile、void、while、with。还有一些本应放入保留字中但没有放入诸如undefined、NaN和Infinity等。

二、语法:与Java语法大部分相同,只有部分稍有区别:

      1、循环:while循环、do while循环以及for循环与Java大体一致,只有for in循环比较特殊,JavaScript不支持像Java中的增强for循环,但支持此种格式的for in循环,用于遍历对象中的属性,包括对象对应的原型中的属性:

for
4000
(attr in obj){
}

      2、try catch语句:大致与Java相同,只是在JavaScript中throw抛出的异常一般是一个对象字面量,它包含name属性和message属性,并且一个try语句只会有一个catch捕获块。

throw {name:'TypeError',message:'requires numbers'};
try{
}catch(e){
}

    3、return语句:每个函数(方法)都有一个返回值,可以显示指定返回值,如果不指定,默认返回undefined。

    4、"=="与"===":都是用于比较两个值是否相等。

      "==="的比较规则:

      1>只要类型不同就返回false

      2>如果类型相同时:

        ①如果类型是基本类型,值相等则返回true,否则返回false;

        ②如果类型是对象类型,是同一个对象则返回true,否则返回false;

        ③如果两值都是null或者两值都是undefined则返回true,否则返回false;特殊地,NaN与任何值包括它本身NaN比较都返回false;

      "=="的比较规则:

      1>如果类型相同,按"==="的规则进行比较

      2>如果类型不同时,比较之前先转换类型:

        ①如果一个是null,一个是undefined则返回true;

        ②如果是数字和其它基本类型比较,先将其它基本类型转为数字再与另一个数字进行比较,true转为1,false转为0;

        ③如果是数字和对象类型比较,将对象类型使用valueOf()或toString()方法转为基本类型再进行比较。valueOf()优先于toString()方法,特殊地Date使用toString()方法。

三、变量类型:

    Javascript是弱类型语言,统一使用var声明定义变量(在函数中如果使用var定义变量则该变量是局部变量,如果没有var只有变量名,则该变量是全局变量),要想知道变量是哪种具体类型可使用typeof运算符(注意不是函数)返回类型字符串(number、string、boolean、undefined,function或object,其中object又可细分为null、数组、日期等)。Javascript的类型主要分基本类型和对象类型两大类:

   
1、基本类型:
number、string、boolean、undefined。

var a=3;
var b='hello,JavaScript!';
var c=true;
var d=undefined;
   ①number类型是64位的浮点数,和Java中的double类型一样。JavaScript中因为没有整型的说法,所以1和1.0的值相同。
    ②NaN:Not a Number,表示一个不是数字的数值。NaN本身是个数值,但不等于任何值,包括它自己。可以使用isNaN()函数检测。

   
③Infinity:表示所有大于1.79769313486231570e+308的值。

   
④string类型变量用单引号或双引号括起来,发明JavaScript时Unicode是16位的字符集,所以JavaScript中所有字符都是16位。

   
⑤string类型中的转义字符用来把那些正常情况下不被允许的字符插入到特殊字符串中,比如反斜线、引号或控制字符。\u约定字符编码,例如"A"==="\u0041"。

   
⑥string类型的字符串有一个length属性(注意不是方法),返回包含的字符数。

   
⑦boolean类型中,除了false之外,null、undefined、空字符串''以及数字0也都被当成false处理,除这几个值之外的值JavaScript都当作true处理。

   2、对象字面量:       

    JavaScript对象字面量是属性的容器,由{}括起来的若干属性组成对象,属性名与属性值之间用冒号分隔,属性与属性之间用逗号分隔。每个对象字面量都会连接到原型对象Object.prototype,相当于Java中每个子类都会继承自一个父类,对对象修改时不会修改原型对象的属性,但对对象访问属性时会通过“委托”逐层向上查找属性,直到找到对应属性为止。

var people={id:1,name:'Tom',age:30,sex:'male','':'未知属性'};

   ①属性名是包括空字符串''在内的任意字符串,如果属性名不是空字符串和保留字,可以不用引号括起来,否则一定需要用引号括起来。

    ②属性值是除undefined之外的任何值,可以嵌套,也就是说属性值可以又是一个对象。

    ③访问对象的属性时,如果属性名必须用引号括起来,那么只能使用:对象名['属性名']访问;否则还可以使用:对象名.属性名进行访问。当然优先选择第二种方式,因为它更紧凑且可读性更好。

    ④如果尝试获取一个并不存在的属性将会返回undefined,运算符||可用来设置默认值,例如:

people.height||175    //将people对象的属性height的默认值设为175,如果获取不到该值则取175
   ⑤如果从undefined对象中获取属性会导致TypeError异常,常用的解决方案是:
if(people && people.height){
}
 
 ⑥给对象的属性赋值:如果该属性存在则将该属性值更改,但原型中属性不会被更改。如果该属性不存在则给该对象新增属性。
    ⑦可以使用方法:对象名.hasOwnProperty('属性名')判断该属性是否在该对象中定义,该方法不会去检查原型中的属性。

    ⑧对象中属性的顺序是无序的,所以通过for in循环得到的属性顺序也是不固定的。

    ⑨delete运算符可以删除对象中非原型定义的属性,如果原型中也有该属性,删除之后再访问该属性时则显示原型中定义的属性值。

   
3、数组字面量:


    JavaScript没有数组这样的数据结构,但提供一种类数组的对象,它们都会连接到原型对象Array.prototype,它把数组的下标转变为字符串作为该对象的属性,这样访问该对象的属性时就像像Java一样在通过数组下标访问数组元素。由于JavaScript是弱类型语言,所以不要求数组字面量中必须是相同类型的元素。例如一个长度为7的数组,含各种类型的元素:

var arrays=['abc',98.6,true,null,undefined,{name:'Tom'},['fish','sheep']];

   ①length属性,默认是数组元素的个数,但可以随意设置该属性。当设置的length值比实际元素的个数多时不会分配更多的内存空间,但当设置的length值比实际元素的个数少时,下标大于等于length值的元素会被删除。

    ②数组添加元素,可以使用

arrays[arrays.length]='';
或者
arrays.push('');

   其实是给数组对象添加了属性名是整数的属性值,但是如果给数组对象添加属性名不是整数的值时,比如添加arrays['setVal']而不是arrays[8]时,数组属性length不会发生变化。也就是说此时不是给数组添加了元素,而是给整个数组对象添加了属性。

    ③数组删除元素,因为数组元素也是对象所以可以使用delete进行删除。

delete arrays[2];
    但是用delete删除之后数组的length值不变,对应的已删除的元素的值变为undefined。为了避免这种情况可以使用splice()方法进行删除。
arrays.splice(2,1);
此时数组的属性length值已减1。
    ④判断是否为数组,可以使用

Object.prototype.toString.apply(arrays)==='[object Array]'

四、函数:

    在JavaScript中,函数也是对象,所以它们可以像其他变量一样被使用。函数对象都会连接到原型对象Function.prototype,而Function.prototype又会连接到原型对象Object.prototype。

    1、函数的声明定义:

    ①常规方式:使用function 方法名(形式参数){}定义函数。例如:

function test(a,b){
return a+b;
}
    ②匿名方式:将一个匿名函数赋值给一个函数字面量。例如:
var a=function(a,b){
return a+b;
};
       2、函数的调用:使用:函数名(实际参数)的格式进行函数调用,函数被调用时会隐形接收两个附加参数:this和arguments。arguments会将值传给形式参数,由于JavaScript是弱类型的,所以不进行类型检查,任何类型的参数都可以传。也不会进行参数数量检查。如果实际参数比形式参数的数量多,多出的参数忽略;如果实际参数比形式参数的数量少,少的参数用undefined代替。this的值取决于调用模式,JavaScript中共有4种调用模式,分别是:
    ①方法调用模式:当函数被保存为对象的一个属性时,称这个函数为方法。因为此方法作为对象的属性,所以可以使用.属性名或['属性名']调用该方法。此时的this是包含该方法的对象,在该方法内部通过this可以访问调用本方法的对象的属性值。

var people={id:1,name:'Tom',age:30,sex:'male',speak:function(){alert("我是"+this.name+",是一个程序猿");}};
people.speak();
people['speak']();

    ②函数调用模式:当函数并非一个对象的属性时,它就被当作函数来调用。此时的this是全局对象window,这是语言设计上的一个错误。

function speak(){
alert("我是"+this+",是一个程序猿");
}
speak();
    解决方案是在调用该方法时传入正确的this的值,而该值是由外部的函数使用方法调用模式获取到的正确的对象。
var people={id:1,name:'Tom',
1215f
age:30,sex:'male',mouth:function(){var that=this;speak(that);}};
function speak(that){
alert("我是"+that.name+",是一个程序猿");
}
people.mouth();
    ③构造器调用模式:当在函数调用时在函数名之前加new关键字时就成为构造器调用模式。通常被使用构造器调用模式调用的函数名首字母大写,此时的this是新创建的对象。
var Entity = function(status){
this.status=status;
}
var entity = new Entity("status0");
alert(entity.status);

    ④Apply/Call调用模式:

    apply方式调用采用:方法名.apply(this要指向的对象,实际参数数组对象)的方式调用。apply的两个参数都必须是对象,第一个参数指定this的值,第二个参数指定调用该方法时的实际参数的数组。

var people={id:1,name:'Tom',age:30,sex:'male'};
var array=['程序猿','码农'];
function speak(job1,job2){
alert("我是"+this.name+",是一个"+job1+",也叫"+job2);
}
speak.apply(people,array);

    call方式调用采用:方法名.call(this要指向的对象,实际参数)的方式调用。第一个参数指定this的值,第一个参数后的其它参数是调用该方法时的实际参数。

var people={id:1,name:'Tom',age:30,sex:'male'};
function speak(job1,job2){
alert("我是"+this.name+",是一个"+job1+",也叫"+job2);
}
speak.call(people,'程序猿','码农');


    3、函数的特性

    ①参数arguments:函数可以通过变量arguments获取到调用函数时传递过来的实际参数值,包括超出形式参数数量的多余的参数值。

    ②作用域:JavaScript不支持块级作用域,即{}之外的程序可以访问{}之内的变量(这里的{}不包括函数的{}),但支持函数级作用域,即函数的{}外的程序不能访问函数{}之内的var声明的变量(未加var声明的变量视为全局变量,所以是可以访问的)。

    ③回调:回调函数是一个作为变量传递给另外一个函数的函数,它在主体函数执行完之后执行。例如:

function test(a,b,callback){
var c = a+b;
callback(c,a);
}
function test1(a,b){
var d = a*b;
alert(d);
}
test(2,4,test1); //12
或者:
function test(a,b,callback){
var c = a+b;
callback(c,a);
}
test(2,4,function(a,b){
var d = a*b;
alert(d);
});


    ④级联:当函数返回值为this时,则可以在单独一条语句中依次调用同一个对象的多个方法,例如a,b,c,d分别是obj的4个方法,则可以:

obj.a().b().c().d();
   ⑤ 闭包:闭包就是一个在函数a(外部函数)中的函数b(内部函数),这个内部函数b会引用外部函数a中的局部变量c,并且这个内部函数b是外部函数a的返回值。这样当调用外部函数a并将返回值赋给另一变量d时,由于返回值是内部函数b,而内部函数b又引用外部函数a的局部变量c,所以该变量c会持续存在于内存中而不会被回收直到变量d被回收,闭包的主要目的就是为了让外部函数a的外部获取变量c但同时又不允许外部函数a的外部修改变量c。例如:
function a(){
var c=3;
function b(){
c+=4;
return c;
}
return b();
}
var d = a();
alert(d);  显示7
alert(c);  //报错未定义

   ⑥柯里化:通过利用闭包的特性(让局部变量存在于闭包中而使其持续存在于内存中),将含多个参数的函数逐级分解,最终分解为逐级嵌套返回的只含一个参数的函数,目的是对函数细节实现模块化管理,便于更灵活使用该函数的某部分。例如:

原函数为:

var add=function(a,b,c,d){
return a+b+c+d;
}
var result = add(1,2,3,4);
alert(result);

    如果a,b,c参数值不变,d参数值变化,那么调用该方法时,都必须调用含4个参数的add方法,并且每次都要将a,b,c参数重新传值,例如:
var result1=add(1,2,3,4);
var result2=add(1,2,3,5);
var result3=add(1,2,3,6);
柯里化之后的函数为:
var add=function(a){
return function(b){
return function(c){
return function(d){
return a+b+c+d;
};
};
};
}
var result=add(1)(2)(3)(4);
alert(result);
    如果a,b,c参数不变,d参数变化,那么调用该方法时,则不需要每次都将a,b,c参数传值。只需向a,b,c传一次值即可,例如:
var add1 = add(1)(2)(3);
var result1=add1(4);
var result1=add1(5);
var result1=add1(6);

因此,柯里化也常称为“局部套用”。

五、正则表达式:

    1、正则表达式的创建:    

    JavaScript中的正则表达式与Java类似但又有不同。有两种方法可以创建正则表达式对象:

    ①正则表达式字面量:将正则表达式放在//之间。例如:

var my_regex=/"(?:\\.|[^\\\"])*"/g;
    ②使用RegExp构造器:该构造器接收一个字符串并将该字符串编译为一个RegExp对象。需要注意的是字符串中的\需要转义为\\,"需要转义为\"。上述正则表达式字面量又可表示为:
var my_regex=new RegExp("\"(?:\\\\.|[^\\\\\\\"])*\"",'g');

    其中在①最后有标识g,在②中第2个参数是g。g表示全局的,还可以是其他标识,这些标识和其对应的含义是:

标识符含义
g

(global)
标识该表达式将在输入字符串中查找所有可能的匹配,返回的

结果可以是多个,如果不加g最多只会匹配第一个。

每次调用array.exec()都从lastIndex开始查找新的匹配;

string.match() 则是返回所有的匹配。
string.replace()则替换所有匹配到的字符串。
i

(ignoreCase)
标识匹配的时候不区分大小写
m

(multiline)
若不指定 m,则:^ 只匹配字符串的最开头,$ 匹配字符串的

最结尾。

若指定 m,则:^ 匹配字符串每一行的开头,$ 在字符串每一

行的结尾。
    2、RegExp的属性:
    ①RegExp.prototype中的global属性表示是否开启全局匹配,是一个只读属性。

    ②RegExp.prototype中的ignoreCase属性表示是否开启忽略大小写,是一个只读属性。

    ③RegExp.prototype中的multiline属性表示是否开启多行模式,是一个只读属性。

    ④RegExp.prototype中的lastIndex属性表示exec下一次匹配的起始索引,只有在全局模式g下才会生效。

    ⑤RegExp.prototype中的source属性返回当前正则表达式对象的模式文本的字符串,该字符串不会包含正则字面量两边的斜杠以及任何的标志字符。

    ⑥RegExp.prototype的test(str):如果正则是一个空字符串,则匹配所有字符串。

    ⑦RegExp.prototype.toString():返回一个字符串,其值为该正则对象的字面量形式。

    3、RegExp.exec()返回的数组属性:

    ①index:匹配到的字符位于原始字符串的位置。

    ②input:原始字符串。 

   
4、一些特殊字符的含义:


字符含义
[\b]
匹配一个退格符(backspace)
\0
匹配一个null字符,不要在此后面跟小数点。
\cX
X是A-Z的一个字母。匹配字符串中的一个控制字符。例如/^cM/匹配字符

串中的control-M。
\b
字边界标识匹配一个零宽单词边界(zero-width word boundary),如一

个字母与一个空格之间。 (不要和 [\b] 混淆)

 例如,/\bno/ 匹配 "at noon" 中的 "no",/ly\b/ 匹配 "possibly yesterday."

中的 "ly"。
\B
匹配一个零宽非单词边界(zero-width non-word boundary),如两个字

母之间或两个空格之间。

例如,/\Bon/ 匹配 "at noon" 中的 "on",/ye\B/ 匹配 "possibly yesterday."

中的 "ye"。
(?:x)
非捕获组,只做匹配但不捕获内容,所以会带来微弱的性能优势,不会被编号
六、原型与继承:

   
1、实现示例:


function Person(name, sex) {
this.name = name;
this.sex = sex;
}

Person.prototype = {
name:'无名氏',
sex:'未知',
nation:'火星',
getName: function() {
return this.name;
},
getSex: function() {
return this.sex;
},
getNation: function() {
return this.nation;
}
}

var people = new Person("Tom", "man");
alert(people.getName());    //使用构造对象时的name值
alert(people.getNation());  //使用原型中的nation值
    我们把函数Person(也就是创建自定义对象的函数)称为构造函数,Person.prototype称之为Person的原型,可以看出JavaScript通过构造函数和原型的方式模拟实现了类的功能。prototype本质上是一个JavaScript对象,并且每个函数都有一个默认的prototype属性。prototype原型中的属性可以被原型所属的类的自定义对象引用。
    如果将People理解为父类,要定义一个子类Employee继承父类原型中的属性。那么:

function Employee(employeeID,name,sex){
this.employeeID = employeeID;
this.name = name;
this.sex = sex;
}

Employee.prototype = new Person();   //将Employee的原型指向父类的对象
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};

//此时Employee的对象就可以调用Person中的方法
var boss = new Employee("001", "Jim", "man");
alert(boss.getEmployeeID());   //使用构造对象时的employeeID值
alert(boss.getName());   //使用构造对象时的name值
alert(boss.getNation());   //使用原型中的nation值
    2、变量this:表示当前对象。如果在全局作用域内使用this,则返回当前页面对象window。如果在函数(方法)中使用this,则返回调用该函数的对象。可以使用call或apply重新指定this所代表的对象。
    ①在全局作用域内:

<script type="text/javascript">
alert(this === window);   // true
</script>
    ②在函数内:
var fruit = "apple";   //定义一个全局变量,等价于window.fruit = "apple";
function test() {
alert(this.fruit);
}
test();  //apple
 
  ③在方法内:
var obj = {
fruit: "orange",
test: function(){alert(this.fruit);}
};
obj.test(); // "orange"
 
  
3、对象的constructor属性:始终指向创建该对象的的构造函数。
var a = new Array(1, 56, 34, 12);
alert(a.constructor===Array);  //true
var b = [1, 56, 34, 12];
alert(b.constructor===Array);  //true
var c = new Function();
alert(c.constructor===Function);  //true
var d = function(){};
alert(d.constructor===Function);  //true

var Foo = function(){};
var obj = new Foo();
alert(obj.constructor.constructor === Function); //true

function Person(name) {
this.name = name;
}
var p = new Person("Tom");
alert(p.constructor===Person);  //true
 
  如果修改Person的prototype,Person创建的对象的constructor依然为Person
function Person(name) {
this.name = name;
}
//修改Person的原型,而不是全部覆盖其原型
Person.prototype.getName = function() {
return this.name;
};
var p = new Person("Tom");
alert(p.constructor===Person);  //true
    但如果覆盖掉Person的prototype,Person创建的对象的constructor则变为Object
function Person(name) {
this.name = name;
}
//覆盖Person的原型
Person.prototype = {
getName: function(){
return this.name;
}
};
var p = new Person("Tom");
alert(p.constructor===Person);  //false
alert(p.constructor===Object);  //true
 
  这是因为覆盖Person的prototype时实际做的操作是:
Person.prototype = new Object({
getName: function(){
return this.name;
}
});
    所以对象的constructor指向的是Object而非Person,可以用以下方法修正这个错误:
Person.prototype.constructor=Person;
 
  特殊地:构造函数的原型的constructor也始终指向构造函数本身。
alert(String.prototype.constructor===String);   //true
alert(Array.prototype.constructor===Array);   //true
alert(Date.prototype.constructor===Date);   //true
alert(Number.prototype.constructor===Number);   //true
alert(Object.prototype.constructor===Object);   //true

function Person(name) {
this.name = name;
}
alert(Person.prototype.constructor==Person);  //true


参考:Douglas Crockford《JavaScript语言精粹》

            http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息