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

JavaScript总结

2016-05-30 10:00 513 查看
JavaScript是一种面向对象的动态语言,它包含类型、运算符、标准内置(built-in)对象和方法。它的语法来源于Java和C,所以这两种语言的许多语法特性同样适用于JavaScript。需要注意的一个主要区别是JavaScript不支持类,类这一概念在JavaScript通过对象原型(objectprototype)得到延续(有关ES6类的内容参考这里
Classes
)。另一个主要区别是JavaScript中的函数也是对象,JavaScript允许函数在包含可执行代码的同时,能像其他对象一样被传递。
Number
(数字)
String
(字符串)
Boolean
(布尔)
Symbol
(符号)(第六版新增)
Object
(对象)
Functio
4000
n
(函数)
Array
(数组)
Date
(日期)
Math
(数学对象)
RegExp
(正则表达式)
Null
(空)
Undefined
(未定义)
Error
(错误)类型
一、number详解Javascript中采用全浮点计算(双精读64位)注意!在某些运算中会出错eg:0.1+0.2=0.30000000000000004我们也可以使用JavaScript提供的Math内置对象,和内置函数parseInt();此外JavaScript还提供了parseFloat(),但是它只支持解析十进制数字在parseInto("123",10)//123-->10的含义是十进制
[code]parseInt("010",10);//10
parseInt("010");//8如果没有第二个参数,那么以0开头就是八进制0x开头就是16进制
parseInt("0x10");//16
[/code]
如何把二进制转为整数值?
parseInt("10011",2);
[code]当第一个参数不是数字型字符串
parseInt("hello",10);//NaN[/code]
NaN是一个特殊值用它与任何数字计算结果都是NaN
此外还有两个特殊值Infinity和-Infinity(正无穷和负无穷)用内置函数isFinite()可以判断一个变量是否为Infinity-InfinityNaN二、字符串JavaScript中的字符串是一个Unicode字符序列每一个编码单元由一个16位二进制数表示。每一个Unicode字符由一个或两个编码单元来表示。
[code]"hello".charAt(0);//"h"
"hello,world".replace("hello","goodbye");//"goodbye,world"
"hello".toUpperCase();//"HELLO"
"hello".length;//5
[/code]
三、其他类型(null和undefined)
[/code]
null的本质是一个空值,必须使用null关键字才能访问
undefined是一个未定义类型的对象(也是一个对象),它表示一个未初始化的值,也就是还没有被分配值。(JavaScript允许声明变量但不对其赋值,一个未被赋值的变量就是undefined类型)
布尔型:(true/false)其他类型对其转化
flase,0,"",NaN,null,undefined都会在JavaScript需要一个布尔值变量的时候隐式转换为false(其他的都会转化为true)
四、变量
在JavaScript中声明一个变量用var
vara;
varname="simon";
在JavaScript中的语句块中是没有作用域的
五、运算符
JavaScript的算术操作符包括+
-
*
/
%——
求余(与模运算不同)。赋值使用
=
运算符,此外还有一些复合运算符,如
+=
-=
,它们等价于
x=xopy
[/code]
+可以用来连接字符串
[code]"3"+4+5;//345
3+4+"5";//75
1===true;//false
123==="123";//false
123=="123"//true
1==true;//true
[/code]
六、控制结构
if()...elseif()...else()

while()

do...while()

for(;;)/for(:)

[/code]
短路与:varname=o&&o.getName();

短路或:varname=otherName||"default";

三元运算符:varallowed=(age>18)?"yes":"no";

多重分支时可以使用
基于一个数字或字符串的switch
语句:(在
switch
的表达式和
case
的表达式是使用
===
严格相等运算符进行比较的:)
[/code]
switch(action){
case'draw':
drawIt();
break;
case'eat':
eatIt();
break;
default:
doNothing();
}
[/code]
switch(1+3){
case2+2:
yay();
break;
default:
neverhappens();
}
[/code]
七、对象
对象的创建方式:
1.varobj=newObject();
2.varobj={}--->对象字面量
[code]varobj={
name:"carrot",
"for":"Max",
details:{
color:"orange",
size:12
}
}
[/code]可以用链式访问:obj.details.color;//orange可以用中括号访问:obj[detail][color];
对象原型:(像是Java中的一个类)
functionPerson(name,age){[/code]
this.name=name;[/code]
this.age=age;[/code]
}[/code]
创建一个Person的对象:
varYou=newPerson("zhangjiahao","23");[/code]
可用You.name="ZJH"来赋值[/code]
可用varname=You.name;来取值[/code]
obj["name"]="Simon";
varname=obj["name"];
中括号的方式的优点:中括号内都是字符串,所以可以用于运行时计算,且可以使用关键字,但在后期被解释时无法优化。[/code]
obj.for="Simon";//语法错误,因为for是一个预留关键字
obj["for"]="Simon";//工作正常
八、数组
JavaScript中的数组是一个特殊的对象。
与普通对象的区别在于,对于数组中的‘属性’(元素)只能通过中括号来访问。同时数组比普通对象多了一个length属性,该属性值始终比最大索引大一(并非等于数组中的元素个数)
数组的创建:
vara=['dog','cat','hen'];//数组字面量[/code]
varb=newArray('dog','cat','hen');//传统方式[/code]
如果访问了一个不存在的数组索引会得到undefined[/code]
数组的遍历:
for(vari=0;i<a.length;i++)//传统方法--->不推荐,每次循环都会计算length,效率不高[/code]
for(vari=0,len=a.length;i<len;i++)//改进方法--->先循环开始前,先缓存a.length[/code]
for(vari=0,item;item=a[i++])//有局限,每次循环都会判断item是否空值或是否定义(是否为真)但是在数组内容中出现假值会影响循环[/code]
for(variina)//这种循环不单单会遍历出元素,也会遍历出自定义添加的array属性(向
Array.prototype
添加了新的属性)
[/code]
ECMAScript5中添加了forEach()方法(IE暂不支持)[/code]
[code]//Array.forEachimplementationforIEsupport..
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
if(!Array.prototype.forEach){
Array.prototype.forEach=function(callback,thisArg){
varT,k;
if(this==null){
thrownewTypeError("thisisnullornotdefined");
}
varO=Object(this);
varlen=O.length>>>0;//HacktoconvertO.lengthtoaUInt32
if({}.toString.call(callback)!="[objectFunction]"){
thrownewTypeError(callback+"isnotafunction");
}
if(thisArg){
T=thisArg;
}
k=0;
while(k<len){
varkValue;
if(kinO){
kValue=O[k];
callback.call(T,kValue,k,O);
}
k++;
}
};
}
[/code]还有a.push(item)方法用于在数组后面添加元素
[code]a.push(item);
方法名称描述
a.toString()
返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。
a.toLocaleString()
根据宿主环境的区域设置,返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。
a.concat(item1[,
item2[,...[,itemN]]])
返回一个数组,这个数组包含原先
a
item1、item2、……、itemN
中的所有元素。
a.join(sep)
返回一个包含数组中所有元素的字符串,每个元素通过指定的
sep
分隔。
a.pop()
删除并返回数组中的最后一个元素。
a.push(item1,
...,itemN)
item1、item2、……、itemN
追加至数组
a
a.reverse()
数组逆序(会更改原数组
a
)。
a.shift()
删除并返回数组中第一个元素。
a.slice(start,
end)
返回子数组,以
a[start]
开头,以
a[end]
前一个元素结尾。
a.sort([cmpfn])
依据
cmpfn
返回的结果进行排序,如果未指定比较函数则按字符顺序比较(即使元素是数字)。
a.splice(start,
delcount[,item1[,...[,itemN]]])
start
开始,删除
delcount
个元素,然后插入所有的
item
a.unshift([item])
item
插入数组头部,返回数组新长度(考虑
undefined
)。
[/code]
九、函数
对JavaScript的学习最重要的就是对象和函数两个部分。(函数也是对象)
函数:
functionadd(x,y){
vartotal=x+y;
returntotal;
}
[/code]一个JavaScript函数可以有0个或者多个变量,如果有return,个返回return的值;否则返回undefined。在使用函数时,函数中的变量也不一定强制与定义函数时的变量一致,如:add();此时x,y为undefined;若add(1,2,3)此时3将会被忽略。JavaScript允许你创建匿名函数:
varavg=function(){
varsum=0;
for(vari=0,j=arguments.length;i<j;i++){
sum+=arguments[i];
}
returnsum/arguments.length;
};
[/code]
闭包:
vara=1;
varb=2;
(function(){
varb=3;
a+=b;
})();
a;//4
b;//2
[/code]递归:
functioncountChars(elm){
if(elm.nodeType==3){//文本节点
returnelm.nodeValue.length;
}
varcount=0;
for(vari=0,child;child=elm.childNodes[i];i++){
count+=countChars(child);
}
returncount;
}
[/code]闭包递归:
varcharsInBody=(functioncounter(elm){
if(elm.nodeType==3){//文本节点
returnelm.nodeValue.length;
}
varcount=0;
for(vari=0,child;child=elm.childNodes[i];i++){
count+=counter(child);
}
returncount;
})(document.body);
[/code]如上所提供的函数表达式的名称的作用域仅仅是该函数自身。这允许引擎去做更多的优化,并且这种实现更可读、友好。该名称也显示在调试器和一些堆栈跟踪中,节省了调试时的时间。
ps:需要注意的是JavaScript函数是它们本身的对象——就和JavaScript其他一切一样——你可以给它们添加属性或者更改它们的属性,这与前面的对象部分一样。
十、自定义对象
首先我们了解一些面向对象的基本概念和知识。
JavaScript
命名空间:注意:需要认识到重要的一点是:与其他面向对象编程语言不同的是,Javascript中的普通对象和命名空间在语言层面上没有区别。这点可能会让JavaScript初学者感到迷惑。
[code]varMYAPP=MYAPP||{};//全局命名空间
[/code]
MYAPP.event={};//子命名空间
[/code]下面是用于创建命名空间和添加变量,函数和方法的代码写法:
//给普通方法和属性创建一个命名空间
MYAPP.commonMethod={
regExForName="",
regExForPhone="",
validateName=function(name){
//对参数name做一些操作
},
validatePhoneNo=function(phoneNo){
//对参数phoneNo做一些操作
}
}
MYAPP.event={
addListener=function(el,type,fn){
//code
}
removeListener=function(el,type,fn){
//code
}
}
//使用addListner方法的写法
MYAPP.event.addListener("yourel","type",callback)
[/code]自定义对象类JavaScript是一种基于原型的语言,它并没有类似于其他C++/JAVA中的类声明语言。在JavaScript中,我们通常使用方法(function)来当做类。定义一个类跟定义一个函数一样easy
functionPerson(){}
//或
varPerson=function(){}
[/code]对象(类的实例)我们使用newobj()来创建一个新的实例。eg:
functionPerson(){}
varperson1=newPerson();
varperson2=newPerson();
[/code]构造器在实例化时构造器被调用,在JavaScript中,初始的函数就可以作为构造器使用,而不需要额外的定义构造器方法。类中的每个声明的函数都可以在被实例化后调用执行。(构造器常用于给对象的属性赋初始值)
functionPerson(){
alert('Personinstantiated');
}
varperson1=newPerson();
varperson2=newPerson();
[/code]属性(对象属性)属性就是当前类中的变量。通常为了正确继承,属性应当为定义在原型属性中。可以使用关键字this来调用类中的属性,this是对当前对象的引用。
functionPerson(name){
this.name=name;
alert("thisismyname"+this.name);
}
varperson1=newPerson("Alice");
varperson2=newPerson("Bob");
[/code]方法(对象属性)方法和属性相似,不同的是:一个是函数,另一个是可以被定义为函数。为定义一个方法,通常需要将一个函数赋值给类的原型属性,这个赋值给函数的名称就是用来给对象在外部调用它使用的。
functionPerson(name){
this.name=name;
}
Person.protopype.sayHello=function(){
alert("hello,i'm"+this.name);
}
varperson1=newPerson("Alice");
varperson2=newPerson("Bob");
person1.sayHello();
[/code]在JavaScript中方法通常是绑定到一个对象中的普通函数,所以我们也可以在访问域之外对其调用访问。
functionPerson(name){
this.name=name;
}
Person.protopype.sayHello=function(){
alert("hello,i'm"+this.name);
}
varperson1=newPerson("Alice");
varperson2=newPerson("Bob");
person1.sayHello();//Alice
varhelloFunction=person1.sayHello;
helloFunction();//undefined
console.log(helloFunction===person1.sayHello);//logstrue
console.log(helloFunction===Person.prototype.sayHello);//logstrue
helloFunction.call(person1);
[/code]继承创建一个或者多个类的某专门版本类方式称为继承(JavaScript只支持单继承)。创建的专门版本的类通常称作子类。在JavaScript中通过赋予子类一个父类的实例并专门化子类来实现。
functionPerson(name){
this.name=name;
}
Person.prototype.walk=function(){
alert("i'mwalking!");
}
Person.prototype.sayHello=function(){
alert("Hello,I'm"+this.name);
}
//定义Student构造器
functionStudent(name,subject){
Person.call(this,name);//这里也可以用apply(),详细见总结末端
this.subject=subject;
}
//建立一个由Person.prototype继承而来的Student.prototype对象
//注意:常见的错误是使用"newPerson()"来建立Student.prototype.
//这样做的错误之处有很多,最重要的一点是我们在实例化时
//不能赋予Person类任何的FirstName参数
//调用Person的正确位置如下,我们从Student中来调用它
Student.prototype=Object.create(Person.prototype);
//设置constructor属性指向Student
Student.prototype.constructor=Student;
Student.prototype.sayHello=function(){
console.log("hello,I'm"+this.name+"I'mstudying"+this.subject);
}
Student.prototype.sayGoodBye=function(){
alert("GOODBYE");
}
//测试实例:
varstudent1=newStudent("Janet","AppliedPhysics");
student1.sayHello();//"Hello,I'mJanet.I'mstudyingAppliedPhysics."
student1.walk();//"Iamwalking!"
student1.sayGoodBye();//"Goodbye!"
//Checkthatinstanceofworkscorrectly
console.log(student1instanceofPerson);//true
console.log(student1instanceofStudent);//true
[/code]封装
在上一个例子中,Student类虽然不需要知道Person类的walk()方法是如何实现的,但是仍然可以使用这个方法;Student类不需要明确地定义这个方法,除非我们想改变它。这就叫做封装,对于所有继承自父类的方法,只需要在子类中定义那些你想改变的即可。
抽象
抽象是允许模拟工作问题中通用部分的一种机制。这可以通过继承或者组合来实现。
JavaScript通过继承来实现具体化,通过让类的实例是其他对象的属性值来实现组合。
[code]varfoo=function(){};
console.log('fooisaFunction:'+(fooinstanceofFunction));//logs"fooisaFunction:true"
cpnsole.log('foo.prototypeisanObject:'+(foo.prototypeinstanceofObject));//logs"foo.prototypeisanObject:true"
[/code]

多态

就像所有定义在原型属性内部的方法和属性一样,不同的类可以定义具有相同名称的方法;方法是作用于所在的类中。并且这仅在两个类不是父子关系时成立(继承链中,一个类不是继承自其他类)。
---------------------------------------------------------------------------------
总结:
在经典面向对象中,对象指的是数据以及操作数据的集合。与C++和Java不同,JavaScript是一种基于原型的编程语言,没有Class语句,而是把函数作为类。
eg:(a)
[code]functionmakePerson(first,last){
return{
first:first,
last:last
}
}
functionpersonFullName(person){
returnperson.first+""+person.last;
}
functionpersonFullNameReversed(person){
returnperson.last+""+person.first;
}
person1=makePerson("Kobe","Branyt");
personFullName(person1);
personFullNameReversed(person1);
[/code]显然上述方法十分麻烦,需要在全局命名空间中写很多的函数。既然函数本身就是对象,如果需要让一个函数
属于一个对象,那么不难得到:(b)
[code]functionmakePerson(first,last){
return{
first:fitst,
last:last,
fullName:function(){
returnthis.first+''this.last;
}
}
}
s=makerPerson('Kobe','Branty');
s.fullName();//KobeBranty
[/code]注意:this关键字,当使用在函数中时,this指的是当前的对象,也就是调用该函数的对象。如果一个对象使用了.或者{}来访问其下的属性时,那么该对象就成了this。如果并不存在某个对象,那么此时this指向全局对象。
[code]s=makePerson('Allen','Iverson');
varfullName=s.fullName;
fullName();//undefinedundefined
[/code]所以我们可以用this来改进上述makePerson函数:
[code]functionPerson(first,last){
this.first=first;
this.last=last;
this.fullName=function(){
returnthis.first+''+this.last;
}
this.fullNameReversed=function(){
returnthis.last+','+this.first;
}
}
vars=newPerson("Simon","Willison");
[/code]但此时创建一个Person对象的时候,我们都在其中创建了两个新的函数对象。所以我们可以将里面的函数拿出来:
functionpersonFullName(person){
returnthis.first+''+this.last;
}
functionpersonFullNameReversed(person){
returnthis.last+','+this.first;
}
functionPerson(first,last){
this.first=first;
this.last=last;
this.fullName=personFullName;
this.fullNameReversed=personFullNameReversed;
}
[/code]最好的方式:
functionPerson(first,last){
this.first=first;
this.last=last;
}
Person.prototype.fullName=function(){
returnthis.first+''+this.last;
}
Person.prototype.fullNameReversed=function(){
returnthis.last+','+this.first;
}
[/code]Person.porotype是一个可以被Person的所有实例共享的对象。他的名字叫做原型链(prototypechain)的查询链的一部分:当你试图访问一个Person中没有定义的属性时,解释器会先检查这个Person.porotype来判断是否存在这样一个属性。所以,在任何时候任何分配给Person.porotype的东西通过this对象构造的实例都是有用的。这也使得我们可以在任何时候改造原型中的一些东西,甚至是内置对象的原型。给
String
添加一个方法用来返回逆序的字符串:
vars="Simon";
s.reversed();//TypeErroronline1:s.reversedisnotafunction
String.prototype.reversed=function(){
varr="";
for(vari=this.length-1;i>=0;i--){
r+=this[i];
}
returnr;
}
s.reversed();//nomiS
[/code]原型组成链的一部分,那条链的根节点是Object.pototype,它包括toString()方法-->将对象转换成字符串调用的方法。这对于调用我们的Person对象很有用。
vars=newPerson("Simon","Willison");
s;//[objectObject]
Person.prototype.toString=function(){
return'<Person:'+this.fullName()+'>';
}
s.toString();//<Person:SimonWillison>
[/code]ps:apply()和call()
functiontrivialNew(constructor,...args){(...args是剩余参数2015新特性)
varo={};//创建一个新对象
constructor.apply(o,args);
returno;
}
[/code]
apply()
有一个姐妹函数,名叫
call
,它也可以允许你设置
this
,但它带有一个扩展的参数列表而不是一个数组。
functionlastName(){
returnthis.last.toUpperCase();
}
vars=newPerson("Simon","Willison");
lastName.call(s);
//与下面效果一致
s.lastName=lastName;
s.lastName();
[/code]

内部函数

JavaScript允许在一个函数内部定义函数,如果某个函数依赖于其他的一两个函数,而这一两个函数对其余的代码没有用处,可以将它们嵌套在会被调用的那个函数内部,这样做可以减少全局作用域下的函数的数量,这有利于编写易于维护的代码。
functionbetterExampleNeeded(){
vara=1;
functiononeMoreThanA(){
returna+1;
}
returnoneMoreThanA();
}
[/code]闭包闭包是JavaScript中必须提到的功能。首先我们来看一个内部函数应用的例子:
functionmakeAdder(a){
returnfuntion(b){
returna+b;
}
}
varx=makeAdder(5);
vary=makeAdder(20);
x(6);//11
y(7);//27
[/code]我们来观察一下,在上述代码运行过程中发生了什么:每当JavaScript执行一个函数时,都会创建一个作用域对象(Scopeobject),用来保存在这个函数中创建的局部变量。它和被传入函数的变量一起被初始化。这与那些保存的所有全局变量和函数的全局对象类似,但有一些区别:1.每次函数被执行时都会创建一个新的,特定的作用域对象;2.与全局对象(在浏览器中当做window对象来访问的)不同的是,不能从JavaScript代码中直接访问作用域对象,也没有可以遍历当前的作用域对象里面属性的方法。过程:当调用makeAdder时,解释器创建了一个作用域对象,它带有一个属性a(它被当做参数传入makeAdder函数中,然后makeAdder返回一个新创建的函数)。通常JavaScript的垃圾回收器会在这时候回收makeAdder创建的作用域对象,但是返回的函数却保留了一个指向那个作用域对象的引用。结果是这个作用域对象并不会被垃圾回收器回收。直到指向makeAdder的那个函数对象的引用计数为零。作用域对象组成了一个名为作用域链(scopechain)的链.它类似于原形(prototype)链一般,被JavaScript的对象系统使用。那么:一个闭包就是一个函数和在这个函数中被创建函数的作用域对象的组合。闭包允许你保存状态--所以他们通常可以替代对象来使用。内存泄漏使用闭包的一个缺点就是容易造成浏览器内存泄漏。JavaScript是一种具有垃圾回收机制的语言——对象在被创建的时候分配内存,然后当指向这个对象的引用计数为零时,浏览器会回收内存。宿主环境提供的对象都是按照这种方法被处理的。IE浏览器有自己的一套垃圾回收机制,这套机制与JavaScript提供的垃圾回收机制进行交互时,可能会发生内存泄露。
functionleakMemory(){
varel=document.getElementById('el');
varo={'el':el};
el.o=o;
}
[/code]在IE中,每当在一个JavaScript对象和一个本地对象之间形成循环引用时,就会发生内存泄露。这段代码的循环引用会导致内存泄露:IE不会释放被
el
o
使用的内存,直到浏览器被彻底关闭并重启后。
functionaddHandler(){
varel=document.getElementById('el');
el.onclick=function(){
el.style.backgroundColor='red';
}
}
[/code]解决方式

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