JavaScript入门简介JavaScript是一门面向对象的动态语言,他一般用来处理以下任务:修饰网页生成HTML和CSS生成动态HTM L内容生成一些特效提供用户交互接口生成用户交互组件验证用户输入自动填充表单能够读取本地或者远程数据的前端应用程序,例如http:/ /web-engineering.info/JsFrontendApp-Book通过Nodejs实现像JAVA,C#,C++一样的 服务端程序实现分布式WEB程序,包括前端和服务端当前浏览器所支持的JavaScript的版本被称为“ECMAScript的5.1” ,或简单的“ES5”,但接下来的两个版本,称为“ES6”和“ES7”(或“ES2015”和“ES2016”,新版以本年命名),有很 多的附加功能和改进的语法,是非常值得期待的(并已部分被当前的浏览器和后端JS的环境支持)。此篇博文,引自《BuildingFro nt-EndWebAppswithPlainJavaScript》一书。JavaScript类型和常量JS有3个值类型: string,number和boolean,我们可以用一个变量v保存不同类型的值用来和typeof(v)比较,typeof(v) ===”number”。JS有5个引用类型:Object,Array,Function,Date和RegExp。数组,函 数,日期和正则表达式是特殊类型的对象,但在概念上,日期和正则表达式是值类型,被包装成对象形式体现。变量,数组,函数的参数和返回值都 可以不声明,它们通常不会被JavaScript引擎检查,会被自动进行类型转换。变量值可能为:数据,如string,number,b oolean对象的引用:如普通对象,数组,函数,日期,正则表达式特殊值null,其通常用作用于初始化的对象变量的默认值特殊值und efined,已经声明但没有初始化的初始值string是Unicode字符序列。字符串常量会被单引号或双引号包裹着,如“Hello world!”,“A3F0′,或者空字符串”"。两个字符串表达式可以用+操作符连接,并可通过全等于比较:if(firstNam e+lastName==="JamesBond")字符串的字符数量可以通过length属性获得:console.log( "Helloworld!".length);//12所有的数字值都是在64位浮点数字。整数和浮点数之间没有明确的类型区别 。如果一个数字常量不是数字,可以将其值设置为NaN(“notanumber”),它可以用isNaN方法来判断。不幸的是,直到E S6才有Number.isInteger方法,用于测试一个数是不是一个整数。因此在还不支持它的浏览器中,为确保一个数字值是一个整数 ,或者一个数字的字符串被转换为一个整数,就必须使用parseInt函数。类似地,包含小数的字符串可用与parseFloat方法转换 。将一个数子n转换成字符串,最好的方法是使用String(n)。就像Java,我们也有两个预先定义好的布尔型值,true与fals e,以及布尔运算符符号:!(非),&&(与),||(或)。当非布尔型值与布尔型值比较时,非布尔型值会被隐式转换。空字符串,数字 0,以及undefined和null,会被转换为false,其他所有值会转换为true。通常我们需要使用全等符号符号(===和!= =)而不是==和!=。否则,数字2是等于的字符串“2”的,(2==“2″)istrueVAR=[]和vara= newArray()都可以定义一个空数组。(二胡:推荐前者)VARO={}和varo=newObejct() 都可以定义个空对象(二胡:还是推荐前者)。注意,一个空对象{}不是真的空的,因为它包含的Object.prototype继承属性。 所以,一个真正的空对象必须以Null为原型,varo=Object.create(null)。表1类型测试和转换变量作用 域在JavaScript的当前版本ES5,有两种范围变量:全局作用域和函数作用域,没有块作用域。因此,应该避免声明在块内声明变量。 functionfoo(){for(vari=0;i<10;i++){...//dosomethin gwithi}}我们应该这样写functionfoo(){vari=0;for(i=0;i<10;i++ ){...//dosomethingwithi}}所有变量应在函数的开始声明。只有在JavaScript的下一个 版本ES6中,我们可以用let关键词声明一个块级变量。严格模式从ES5开始,我们可以使用严格模式,获得更多的运行时错误检查。例如, 在严格模式下,所有变量都必须进行声明。给未声明的变量赋值抛出异常。我们可以通过键入下面的语句作为一个JavaScript文件或sc ript元素中的第一行开启严格模式:’usestrict’;通常建议您使用严格模式,除非你的代码依赖于与严格的模式不兼容的库。不 同类型的对象JS对象与传统的OO/UML对象不同。它们可以不通过类实例化而来。它们有属性、方法、键值对三种扩展。JS对象可以直接通 过JSON产生,而不用实例化一个类。varperson1={lastName:"Smith",firstName:"To m"};varo1=Object.create(null);//anemptyobjectwithnosl ots对象属性可以以两种方式获得:使用点符号(如在C++/Java的):person1.lastName=“Smith”使 用MAP方式person1["lastName"]=“Smith”JS对象有不同的使用方式。这里有五个例子:记录,例如,var myRecord={firstName:”Tom”,lastName:”Smith”,age:26}MAP(也称为“关联 数组”,“词典”或其他语言的“哈希表”)varnumeral2number={“one”:”1″,“two”:”2″,“ three”:”3″}非类型化对象varperson1={lastName:"Smith",firstName:"T om",getFullName:function(){returnthis.firstName+""+this. lastName;}};命名空间varmyApp={model:{},view:{},ctrl:{}};可以由一 个全局变量形式来定义,它的名称代表一个命名空间前缀。例如,上面的对象变量提供了基于模型–视图–控制器(MVC)架构模式,我 们有相应的MVC应用程序的三个部分。正常的类数组可以用一个JavaScript数组文本进行初始化变量:vara=[1,2,3 ];因为它们是数组列表,JS数组可动态增长:我们可以使用比数组的长度更大的索引。例如,上面的数组变量初始化后,数组长度为3,但我们 仍然可以操作第5个元素a[4]=7;我们可以通过数组的length属性得到数组长度:for(i=0;ith;i++){console.log(a[i]);}//123undefined7`我们可以通过Array. isArray(a)来检测一个变量是不是数组。通过push方法给数组追加元素:a.push(newElement);通过spl ice方法,删除指定位置的元素:a.splice(i,1);通过indexOf查找数组,返回位置或者-1:if(a.inde xOf(v)>-1)…通过for或者forEach(性能弱)遍历数组:vari=0;for(i=0;igth;i++){console.log(a[i]);}a.forEach(function(elem){conso le.log(elem);})通过slice复制数组:varclone=a.slice(0);Mapsmap(也称为“散列 映射”或“关联数组’)提供了从键及其相关值的映射。一个JSmap的键是可以包含空格的字符串:varmyTranslation ={"myhouse":"meinHaus","myboat":"meinBoot","myhorse": "meinPferd"}通过Object.keys(m)可以获得map中所有的键:vari=0,key="",keys=[ ];keys=Object.keys(myTranslation);for(i=0;i++){key=keys[i];alert(''Thetranslationof''+key+''is''+my Translation[key]);}通过直接给不存在的键赋值来新增元素:myTranslation["mycar"]="m einAuto";通过delete删除元素:deletemyTranslation["myboat"];通过in搜索map: `if("mybike"inmyTranslation)...`通过for或者forEach(性能弱)和Object. keys()遍历map:vari=0,key="",keys=[];keys=Object.keys(m);for( i=0;i}Object.keys(m).forEach(function(key){console.log(m[key]);} )通过JSON.stringify将map序列化为JSON字符串,再JSON.parse将其反序列化为MAP对象来实现复制: varclone=JSON.parse(JSON.stringify(m))请注意,如果map上只包含简单数据类型或(可 能嵌套)数组/map,这种方法效果很好。在其他情况下,如果map包含Date对象,我们必须写我们自己的clone方法。Functi onsJS函数是特殊的JS的对象,它具有一个可选的名字属性和一个长度属性(参数的数目)。我们可以这样知道一个变量是不是一个函数:i f(typeof(v)==="function"){...}JS函数可以保存在变量里、被当作参数传给其他函数,也可以被其 他函数作为返回值返回。JS可以被看成一个函数式语言,函数在里面可以说是一等公民。正常的定义函数方法是用一个函数表达式给一个变量赋值 :varmyFunction=functiontheNameOfMyFunction(){...}functiont heNameOfMyFunction(){...}其中函数名(theNameOfMyFunction)是可选的。如果省略它,其 就是一个匿名函数。函数可以通过引用其的变量调用。在上述情况下,这意味着该函数通过myFunction()被调用,而不是通过theN ameOfMyFunction()调用。JS函数,可以嵌套内部函数。闭包机制允许在函数外部访问函数内部变量,并且创建闭包的函数会记 住它们。当执行一个函数时,我们可以通过使用内置的arguments参数,它类似一个参数数组,我们可以遍历它们,但由于它不是常规数组 ,forEach无法遍历它。arguments参数包含所有传递给函数的参数。我们可以这样定义一个不带参数的函数,并用任意数量的参数 调用它,就像这样:varsum=function(){varresult=0,i=0;for(i=0;i urnresult;};console.log(sum(0,1,1,2,3,5,8));//20prototype原型链 可以访问函数中的每一个元素,如Array.prototype.forEach(其中Array代表原型链中的数组的构造函数)。var numbers=[1,2,3];//createaninstanceofArraynumbers.forEac h(function(n){console.log(n);});我们还可以通过原型链中的prototype.call方法 来处理:varsum=function(){varresult=0;Array.prototype.forEa ch.call(arguments,function(n){result=result+n;});retur nresult;};Function.prototype.apply是Function.prototype.call的一个变种, 其只能接受一个参数数组。立即调用的JS函数表达式优于使用纯命名对象,它可以获得一个命名空间对象,并可以控制其变量和方法哪些可以外部 访问,哪些不是。这种机制也是JS模块概念的基础。在下面的例子中,我们定义了一个应用程序,它对外暴露了指定的元素和方法:myApp. model=function(){varappName="Myapp''sname";varsomeNonE xposedVariable=...;functionModelClass1(){...}functionMode lClass2(){...}functionsomeNonExposedMethod(...){...}return {appName:appName,ModelClass1:ModelClass1,ModelClass2:Model Class2}}();//immediatelyinvoked这种模式在WebPlatform.org被当作最佳实践提及 :https://docs.webplatform.org/wiki/tutorials/javascript_best_prac tices定义和使用类类是在面向对象编程的基础概念。对象由类实例化而来。一个类定义了与它创建的对象的属性和方法。目前在JavaSc ript中没有明确的类的概念。JavaScript中定义类有很多不同的模式被提出,并在不同的框架中被使用。用于定义类的两个最常用的 方法是:构造函数法,它通过原型链方法来实现继承,通过new创建新对象。这是Mozilla的JavaScript指南中推荐的经典方法 。工厂方法:使用预定义的Object.create方法创建类的新实例。在这种方法中,基于构造函数继承必须通过另一种机制来代替。当构 建一个应用程序时,我们可以使用这两种方法创建类,这取决于应用程序的需求。mODELcLASSjs是一个比较成熟的库用来实现工厂方 法,它有许多优点。(基于构造的方法有一定的性能优势)ES6中构造函数法创建类在ES6,用于定义基于构造函数的类的语法已推出(新的关 键字类的构造函数,静态类和超类)。这种新的语法可以在三个步骤定义一个简单的类。基类Person定义了两个属性firstName 和lastName,以及实例方法toString和静态方法checkLastName:classPerson{constru ctor(first,last){this.firstName=first;this.lastName=last ;}toString(){returnthis.firstName+""+this.lastName;}s taticcheckLastName(ln){if(typeof(ln)!=="string"||ln.trim( )===""){console.log("Error:"+"invalidlastname!");}}}类的静态 属性如下定义:Person.instances={};一个子类定义的附加属性和可能会覆盖超类的方法:classStudent extendsPerson{constructor(first,last,studNo){super.const ructor(first,last);this.studNo=studNo;}//methodoverride ssuperclassmethodtoString(){returnsuper.toString()+"("+ this.studNo+")";}}ES5中构造函数法创建类在ES5,我们可以以构造函数的形式定义一个基于构造函数的类结构,下 面是Mozilla的JavaScript指南中推荐的编码模式。此模式需要七个步骤来定义一个简单的类结构。由于这种复杂的模式可能很难 记住,我们可能需要使用cLASSjs之类的库来帮助我们。首先定义构造函数是隐式创建一个新的对象,并赋予它相应的值:function Person(first,last){this.firstName=first;this.lastName= last;}这里的this指向新创建的对象。在原型中定义实例方法:Person.prototype.toString=fun ction(){returnthis.firstName+""+this.lastName;}可以在构造函数中定 义静态方法,也可以用.直接定义:Person.checkLastName=function(ln){if(typeof (ln)!=="string"||ln.trim()===""){console.log("Error:invalid lastname!");}}定义静态属性:Person.instances={};定义子类并增加属性:functionS tudent(first,last,studNo){//invokesuperclassconstructorP erson.call(this,first,last);//defineandassignadditionalp ropertiesthis.studNo=studNo;}通过Person.call(this,…)来调用基类的构 造函数。将子类的原型链改为基类的原型链,以实现实例方法的继承(构造函数得改回来)://Studentinheritsfrom PersonStudent.prototype=Object.create(Person.prototype);//a djustthesubtype''sconstructorpropertyStudent.prototype.constru ctor=Student;通过Object.create(Person.prototype)我们基于Person.pro totype创建了一个新的对象原型。定义覆盖基类方法的子类方法:Student.prototype.toString=func tion(){returnPerson.prototype.toString.call(this)+"("+th is.studNo+")";};最后通过new关键字来实例化一个类varpers1=newPerson("Tom"," Smith");JavaScript的prototypeprototype是函数的一个属性(每个函数都有一个prototype属性 ),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。__proto__是一个对象拥有的内置属性(prototype 是函数的内置属性。__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。每个对象都有个constructor属性, 其指向的是创建当前对象的构造函数。工厂模式创建类在这种方法中,我们定义了一个JS对象Person,并在其内部定义了一个create 方法用来调用Object.create来创建类。varPerson={name:"Person",properties :{firstName:{range:"NonEmptyString",label:"Firstname",writ able:true,enumerable:true},lastName:{range:"NonEmptyString", label:"Lastname",writable:true,enumerable:true}},methods:{getFullName:function(){returnthis.firstName+""+this.lastName;}},create:function(slots){//createobjectvarobj=Object.create(this.methods,this.properties);//addspecialpropertyfordirecttypeofobjectObject.defineProperty(obj,"type",{value:this,writable:false,enumerable:true});//initializeobjectObject.keys(slots).forEach(function(prop){if(propinthis.properties)obj[prop]=slots[prop];})returnobj;}};这样,我们就有了一个Person的工厂类,通过调用create方法来实例化对象。varpers1=Person.create({firstName:"Tom",lastName:"Smith"}); |
|