javascript学习笔记

◆javascript的基本介绍


1.js 是用于web开发的脚本语言

      脚本语言是什么?

      1)脚本语言往往不能独立使用,它和html/jsp/php/asp/asp.net配合使用

      2)脚本语言有自己的变量,函数,控制语句(顺序,分支,循环)

      3)脚本语言实际上是解释性语言(即在执行的时候直接对源码进行执行)

      4)java程序:java--->class--->jvm              js--->浏览器(js引擎来解释执行)


2.js 在客户端(浏览器)执行




客户端向服务器发送请求,下载html和js文件,然后在客户端执行操作。

3.因为js是有浏览器来解释执行的,因此这里有个问题,不同类型的浏览器可能对js的支持不一样。


◆js的开发工具选择

1.记事本

2.eclipse(myeclipse)

注意:javascript严格区分大小写,每一句语句最后一定要以  ;   结尾


案例1

需求:打开网页后,显示hello world


<html>
<head>
<!--js代码一般是放在  head标签间的,但实际上也可以在别的位置-->
<script language="javascript">
    window.alert("hello!");
</script>
</head>
<body>
</body>
</html>


问题

1.js代码可以放在head里,也可以放在body里

2.js必须用 

 <script language="javascript">

js代码

</script>

注意:如果没有用script包起来,浏览器会将其视为普通文本。

3.在一个html文件中(jsp/php/asp)可以出现多对<script>片段,浏览器将会按照先后顺序依次执行


案例2:

对前面的程序,改进成一个简单的家法运算程序


<html>
<head></head>
<body>
<!--js代码一般是放在  head标签间的,但实际上也可以在别的位置-->
<script language="javascript">
    //js中变量的定义(在js中变量用var表示,不管实际类型)
var num1=456;
var num2=89;
var result=num1+num2;
window.alert("结果是"+result);//alert函数,是

</script>

</body>
</html>


◆js的变量的类型究竟怎样决定:

1.js是弱数据类型语言(即定义变量的时候无需指定类型)

即:在定义变量的时候,统一使用var表示,甚至可以去掉var这个关键字

2.js中的变量的数据类型,是由js引擎来决定的。

var name="creabine";   //name是字符串类型

var kk=2  //kk是数字类型

var yy   //yy是undefined


如果name="233"  //这时name自动变成数字类型


◆即,js引擎可以根据数据的值来判断数据类型(可以这么说么?)


◆js的命名规范(变量,函数的命名)

1.使用大小写字母,数字,_,$,可以命名

2.不能以数字开头

3.不能使用js的关键字和保留字

4.区分大小写

5.单行注释使用    //         多行注释使用  /**/


◆js的数据类型

1.基本数据类型,分为:

     (1)Number  数值


1).整形常量(十进制、八进制、十六进制)

注意此例:

var a=123;
var b=034;//十进制中不以0开头,而这里的034以0开头,js将认为034为八进制数,此八进制数转换为十进制数(4*8^0+3*8^1+0*8^2=28)的结果为28,故c=a+b=123+28=151。
var c=a+b;
window.alert(c);


2).实型常量

      12.32、19.458、5E3(即5*10^3,这里的e大小写都可以,也可以写作5e7)

var a=12.32;

特殊数值:

        1.NaN    (not a number)

例:

        var a="abc";

window.alert(parseInt(a)); //parseInt(a)这个函数为:解析一个字符串并转为整数。这里的变量a=abc,a并不是数值类型,故无法转为整数,会报错为NaN。也可以用这个技巧去判断一个值是不是数字。


2.infintiy(无穷大)

例:

window.alert(6/0);  //6/0就是无穷大


◆有两个函数可以用于判断NaN,infinity,分别是

isNaN()   检查某个值是不是数字   

isFinite() 检查某个值是否为有穷大的数

下面只举例isNaN:

window.alert(isNaN("abc"));  //判断abc是否不是数字,确实不是数字,返回true

window.alert(isNaN("123"));  //判断123是否不是数字,是数字,返回false


     (2)String  字符串型

"a book of JavaScript"   、 "a" 、  "dkfjkd"fdf "fd f"

例:

var a="abc";

var b="abcddd";

car c="fdskfjkls\"gfg\"faksjf"; //字符串类型的值,要用“”包起来,而当值中有“”引号时,用反斜杠\ 来转意,这时输出的时候,并不会输出反斜杠\,会输出它旁边的双引号,

window.alert(c);

     (3)Boolean 布尔型:即true和false

举例:

var a=true;

var b=false;

js中非0的数,都为true,包括小数;js中字符串也是true。

if(1.1){

window.alert('ok');

}

             

通过typeof可以看到变量的具体数据类型是什么,举例:


<html>
<head>
<script language="javascript">
var v1="abc";
var v2=890;
window.alert("v1是"+typeof v1);
window.alert("v2是"+typeof v2);


v1=567;//js是动态语言,变量类型是可以动态变化的,这里赋值567,v1又变成了数字类型。
window.alert("v1是"+typeof v1);
</script>
</head>
<body>
</body>
</html>


2.复合类型,分为

     (1)数组

     (2)对象


3.特殊类型


    1.null   空值

   var a=null;


    2.undefine   未定义

var tt;
window.alert(tt);//弹框说明未定义

   

◆js数据类型的转换

1.自动转换

var a=123; //a是数值

var a="hello";  /a是字符串


2.强制转换   parseInt()  parseFloat()

如:

var a=12345;

a=parseInt(a); //使用系统函数强制转换,转为Int,整数。


var b=90;  //b是数值

b=b+"";  // b+“”空值,把b转为字符串  



◆js的运算符

  +  -  *   /    %(取模,即两数相除取余,常用于判断两个数是否能够整除)


例:

var a=80;
var b=30;
var c=a%b;
window.alert(a%b);//取模主要用于整数型,小数会不明确
if (c==0){
window.alert("a能被b整除");
}else{
window.alert("a不能被b整除");
}


  ◆注意:++运算符  --运算符的意义

举例:举了++,--同理。

var a=57;
var b=++a;//b=++a  等价于  [a=a+1;b=a]; 而  b=a++  等价于[b=a;a=a+1;]  简单来说,就是++在前,就是a先自加再赋值;++在后,就a先赋值,a再自加。两种处理得到的a的值是不同的  
window.alert(b);
window.alert(a);


◆  +=左加    -+左减    /=左除     %=左取模

例子:

var a=56; var b=90;
a-=34;//a=a-34; a=22
b%=a;//b=b%a; 此时b=90,a=22,取模结果为2
window.alert(b);
window.alert(a);


介绍 window.prompt()  和  document.writeln()  两个函数

var val=window.prompt('请输入值');//该函数为弹出一个框输入值
var val2=window.prompt('请再输入一个值');


document.writeln('你的输入是'+(val+val2));//这里是字符串拼接
document.writeln('你的输入是'+(parseFloat(val)+parseFloat(val2)));//这里把字符串转为数值,然后进行加法运算。


◆关系运算符

== 等于    >大于      < 小于      >=大于等于    <= 小雨等于     != 不等于  


案例:

var a=window.prompt('请输入数字a');
var b=window.prompt('请再输入数字b');


a=parseFloat(a);//这里把a转为小数,因为不知道输入的是整数还是小数,统一转为小数
b=parseFloat(b);


if(a==b){
window.alert('a=b');
}else if(a<b){
window.alert('a<b');
}else{
window.alert('a>b');
}


◆逻辑运算符

1.与  &&


重要的案例:

var a=90;
var b=9;
if(a>b && a++>90){//这里a>b为true,故继续执行a++>90,等价于[a>90; a++],而a>90为false,所以输出no;a++之后a=91,所以输出a=91.
window.alert('ok');
}else{
window.alert('no');
}
window.alert('a='+a);


案例说明:

if(逻辑表达式1 && 逻辑表达式2){

}

如果 逻辑表达式1为true,则会继续执行逻辑表达式2

如果 逻辑表达式1为false,则不会执行逻辑表达式2(因为与需要1、2同时为true才为true,1为false,没必要判断2了)


2.或  ||

if(逻辑表达式1||逻辑表达式2){

}

如果,逻辑表达式1为true,则不再执行逻辑表达式2(因为只要1为true,就为true了,没必要判断2)

如果,逻辑表达式1为false,才会执行逻辑表达式2


案例1:

var a=90;
var b=9;
if(a>b || a++>90){//修改a b之间的大于小于号,可以测试a++的执行与否
window.alert('ok');
}else{
window.alert('no');
}
window.alert('a='+a);


这里有一个非常重要的知识点:

//在js中||究竟返回什么值
//结论:||  将返回第一个不为false的值(对象亦可),或者返回最后一个值(若全部都是false的话)

//返回的结果,不一定是布尔值,也可能是对象

案例:
var a=4;//js中,非0的数字即为true,按上述结论,即返回4
var b=90;
window.alert(a||b);
var d=0;
window.alert(d||b);//此时d为false,b为true, b是第一个不为false的值,所以返回90
var f=null;
window.alert(d||f);//此时d,f都为false,返回最后一个值,即f的值,0

var obj=new Object();//这里说明,||所返回的值,不一定是布尔型,还可能为对象
var g=d||f||obj;
window.alert(g+"类型"+typeof g);




3.非  !

if(! 逻辑表达式){

}

如果  逻辑表达式  为true, 【!逻辑表达式】为false,

如果  逻辑表达式  为false, 【!逻辑表达式】为true。


特别说明:在逻辑运算中,0、“”、false、null、undefined、NaN均表示false;其他都认为是true。

例如:

var a=0;//这里的0换成  “”、false,null,undefined,NaN,均一样。

if(a){//这里a=0,表示false,即不执行window.alert('ok') 语句;若改为if(!a),则为true,执行window.alert('ok')语句。

window.alert('ok')

}



◆js也有位运算和移位运算,其规范和java一致,这里没太搞懂,虽然不是重点,实际开发用的少。

案例:

var a=4>>2;
window.alert(a);//结果是1
var b=-4>>2;
window.alert(b);//结果是-1


◆javascript的控制语句


1.顺序控制

对编程而言,不控制其流程就是顺序执行

2.分支控制

1)单分支

基本语法:

if(条件表达式){

//执行语句;

}

2)双分支

基本语法

if(条件表达式){

执行语句1;

}else{执行语句2;

}

案例:

编写一个程序,可以输入人的年龄,如果年龄大于18岁,则输出“你年龄大于18,能上这个网站了=w=”,否则输出“你还未满18岁不能看这种奇怪的东西”

代码:

var age=window.prompt("请输入你的年龄");
if(age>=18){
window.alert("你成年了,能上这个网站了=w=");
}else{window.alert("你还未满18岁不能看这种奇怪的东西");
}


3)多分支

基本语法:

if(条件表达式1){

//执行

}else if{

//执行

}else if...{//可以有多个else if

}

else{//最后可以没有这个单独的else

}


举例:

如果是男,就去男厕所,如果女,就上女厕所,否则不上厕所。

代码:

var sex=window.prompt("请输入性别");
if(sex=="男"){
window.alert("上男厕所");
}else if(sex=="女"){
window.alert("上女厕所");
}else{
window.alert("不上厕所");

}


注意:代码会按照顺序依次进行判断,一旦找到一个满足条件的执行完了之后,就结束整个多分支,后边的都不会再去判断。


◆switch      如果选择是具体的值,用switch;如果是个范围,用if..else

基本语法:

switch(表达式){

case  常量1;

//执行语句

break;

case  常量2;

//执行语句

break;

...

default;

//执行语句

}


案例:

//switch 语句,它的值可以是js允许的任何值
var a=890;


switch(a){
case 890://注意这里是冒号  :
window.alert("890");
break;//break 作用是跳出整个switch。如果这里没有break,会不再判断,继续向下执行,直到遇到break。也就是说,如果去掉这里的break,接下来不会管case 90,会直接执行window.alert("90");然后遇到break,再跳出循环
case 90://注意这里是冒号  :
window.alert("90");
break;
default://注意这里是冒号  :
window.alert("没有匹配的值");
break;
}
window.alert("end");

针对该案例,我们得出以下结论:

1.js的switch语句的数据类型可以是js支持的任何类型,数值,字符串,null,undefined,甚至变量都可以,但数组和对象除外。

2.case后面的值得数据类型可以是任意的,数组和对象除外。

3.break的作用是跳出整个switch

4.如果没有匹配值,则执行default。


如:

var flag=1;//因为1不和任何case匹配,所以执行default,执行之后没有遇到break,所以继续向下执行。
switch(flag){
default:
window.alert("默认");
case "a":
window.alert("今天星期一");
case "b":
window.alert("今天星期二");
case "1.1":
window.alert("1.1");
//如果这里有break,就肯定只显示1.1。这里没有break,但是也没有回去执行,以为这个已经在最后边了,不会再折回去执行。
}


◆for循环

基本语法:

for(循环初值;循环条件;步长){

语句;//循环体

}

案例:

for(var i=0;i<10;i++){
document.writeln("你好</br>");
}

for循环的流程图:



             结束---------------|   (接上图)


◆while循环    判断是否为真,为真就执行,为假就跳出来。

基本语法:

while(条件表达式){

//执行语句;

}

案例:输出hello10次

var i=0;
while(i<10){
document.writeln("hello</br>");
i++;
}


◆  do......while

基本语法:

do{

//执行

}while(条件表达式){


}

案例:

var i=0;
do{
document.writeln("hello</br>");
i++
}while(i<10);



注意:while是先判断再执行,do...while是先执行一次再判断是否循环


练习题:


//请编写一个程序,可以接收一个整数n,(1)计算1+2+...+n的值,(2)计算1!+2!+...+n!的值

//第一题
var n=window.prompt("请输入一个整数n");
n=parseInt(n);//要强制转换,不然默认n是字符串
var result=0;
for(var i=1;i<=n;i++){
result=i+result;
}
document.writeln("结果是"+result);
//第二题
var result2=0;
var temp=1;
for(var i=1;i<=n;i++){//阶乘和的算法想了半天
for( var j=1;j<=i;j++){
temp=temp*j;
}
result2=result2+temp;
temp=1;
}
document.writeln("结果是"+result2);


在IE8中,我们可以通过工具来对js代码进行调试,尤其是页面比较复杂的情况下很有用。

首先要在IE浏览器--Internet选项---高级---取消“禁止脚本调试”



F11:一句一句执行,若遇到函数,则跳入函数中一句依据执行。

F10:按过程执行,若遇到函数,则把整个函数当一个语句执行,不调入函数

shift+F11:从一个函数中跳出

右边监视窗口可以看到变量的值的变化。

右下断点可以看设置的断点情况。


练习:

请编写一个程序,可以接收一个整数N层数,打印出1.金字塔的一半;2.完整的金字塔图形;3.完整的菱形;4.空心菱形。


代码:

//打印金字塔小练习
var n=window.prompt('请输入一个整层数n');
n=parseInt(n);
//1.金字塔的一半
for(var i=1;i<=n;i++){
for(var j=1;j<=i;j++){
document.writeln("*");
}
document.writeln("<br/>");
}
//2.完整的金字塔
for(var i=1;i<=n;i++){
for(var j=1;j<=n-i;j++){
document.writeln("&nbsp");
}
for(var j=1;j<=2*i-1;j++){
document.writeln("*");
}
/*for(var j=1;j<=n-i;j++){
document.writeln("&nbsp");
}这里是*号后边的空格,可以不打,显示上是一样的*/
document.writeln("<br/>");


}
//3.空心金字塔
for(var i=1;i<=n;i++){//这里分行
for(var k=1;k<=n-i;k++){
document.writeln("&nbsp");
}//这个for循环输出每行最开始的空格
for(var j=1;j<=2*i-1;j++){//这里输入每行除了一开始的空格之外的东西
if(i==n){//当i=n,即最后一行的时候,全部输出*
document.writeln("*");
}else if (j==1||j==(2*i-1)){//这里用  或  当j=1,即输出每行的第一个*号,然后结束,不运行 或 后边的东西;当j不为1时,即每行输出第一个*号后,开始判断最后一个*号的位置,即j=2*i-1时输出最后一个*号
document.writeln("*");
}else{
document.writeln("&nbsp");//其他时候都输出空格
}
}
document.writeln("<br/>");//每结束一行,换行一次
}
//4.画菱形
for(var i=1;i<=n;i++){//菱形的上半部分的三角形,包括菱形最宽的一行
for(var j=1;j<=n-i;j++){
document.writeln("&nbsp");
}
for(var j=1;j<=2*i-1;j++){
document.writeln("*");
}
document.writeln("<br/>");
}
for(var i=n-1;i>0;i--){//这里是下边的部分,因为最宽的第n行已经在上边输出过了,所以下边从n-1开始。这里的i相当于下半个三角形从下往上数的第i行。i由上至下的值从n-1到1。
for(var j=1;j<=n-i;j++){
document.writeln("&nbsp");
}//这里正常输入每行前边的空格
for(var j=1;j<=2*i-1;j++){
document.writeln("*");
}//这里通过i计算出每行的*号数量并输出
document.writeln("<br/>");
}
//5.空心菱形【还没搞定,代码显示的不对。】
for(var i=1;i<=n;i++){//这里分行
for(var k=1;k<=n-i;k++){
document.writeln("&nbsp");
}//这个for循环输出每行最开始的空格
for(var j=1;j<=2*i-1;j++){//这里输入每行除了一开始的空格之外的东西
if (j==1||j==(2*i-1)){//这里用  或  当j=1,即输出每行的第一个*号,然后结束,不运行 或 后边的东西;当j不为1时,即每行输出第一个*号后,开始判断最后一个*号的位置,即j=2*i-1时输出最后一个*号
document.writeln("*");
}else{
document.writeln("&nbsp");//其他时候都输出空格
}
}
document.writeln("<br/>");//每结束一行,换行一次
}
for(var i=n-1;i>0;i--){//这里是下边的部分,因为最宽的第n行已经在上边输出过了,所以下边从n-1开始。这里的i相当于下半个三角形从下往上数的第i行。i由上至下的值从n-1到1。
for(var j=1;j<=n-i;j++){
document.writeln("&nbsp");
}//这里正常输入每行前边的空格
for(j=1;j<=2*i-1;j++){
document.writeln("*");
if(i==1){
document.writeln("*");
}else if (j==1&&j==(2*i-1)){//这里用  或  当j=1,即输出每行的第一个*号,然后结束,不运行 或 后边的东西;当j不为1时,即每行输出第一个*号后,开始判断最后一个*号的位置,即j=2*i-1时输出最后一个*号
document.writeln("*");
}else{
document.writeln("&nbsp");//其他时候都输出空格
}
}
document.writeln("<br/>");
}


◆JS函数

1.为什么需要函数:将相同的功能封装在函数里,不同的页面之间可以随意调用,减少代码冗余,更精简

2.函数的基本概念

为完成某一功能的代码(语句、指令)的集合

3.基本语法:

function 函数名(参数列表){

//代码

return 值//有的函数有返回式,有的没有,没有就不写。

}

说明:

1.参数列表:表示函数的输入

2.函数主体:表示为了实现某一功能代码块

3.函数可以有返回值,也可以没有



入门案例:

<script language="javascript" type=text/javascript>//这里两个最好都写,兼容性好
<!--
//输入两个数,在输入一个运算符(+、-、*、/),得到结果
var num1=window.prompt("请输入第一个数字num1");
var num2=window.prompt("请输入第二个数字num2");
var operator=window.prompt("请输入运算符(+、-、*、/)");
num1=parseFloat(num1);
num2=parseFloat(num2);


//如何调用函数
document.write("res="+ji_suan(num1,num2,operator));//这里的函数参数是数字,所以上边一定要先转换成数字


//自定义函数.
//输入两个数,在输入一个运算符(+、-、*、/),得到结果
function ji_suan(num1,num2,operator){//特别强调,参数名前不要带 var
var res=0;
if(operator=="+"){
res=num1+num2;
}else if(operator=="-"){
res=num1-num2;
}else if(operator=="*"){
res=num1*num2;
}else{
res=num1/num2;
}
return res;//返回
}
//-->//这里的小括号包起来,也是为了兼容性好
</script>


注意:如果其他的html文件也想使用这个函数要怎么办呢



把上面的函数单独提出,写到js,然后再需要的地方引入

代码:

myfunction.js:

//自定义函数.
//输入两个数,在输入一个运算符(+、-、*、/),得到结果
function ji_suan(num1,num2,operator){//特别强调,参数名前不要带 var
var res=0;
if(operator=="+"){
res=num1+num2;
}else if(operator=="-"){
res=num1-num2;
}else if(operator=="*"){
res=num1*num2;
}else{
res=num1/num2;
}
return res;//返回
}


在需要的文件中引入:

<script language= "javascript" src="js文件的路径"></script>//这里一定要用</script>结尾,否则无法引入。



◆函数调用的方式

1.普通调用

函数名(实际参数...);

2.通过指向函数的变量去调用

var myvar=函数名;

myvar(实际参数);

3.关于接受函数返回值的问题:

   var myvar=test("abc");//这里把test("abd")赋值给了myvar,所以会直接弹窗一个abc
//如果test函数没有返回值,但是你又接受了,则返回的就是undefined;

//如果有返回值,则返回值是什么,就是什么。在test代码中加入return 90;则会弹窗出90
window.alert(myvar);


◆js函数的调用过程,内存分析


案例:

//abc是一个函数,它接收一个数值,如果接受的数字大于3,那么-1再赋值给自己
function abc(num1){
if(num1>3){
abc(--num1);//递归
}
document.writeln(num1);
}


若这样调用:

abc(5);


则返回的结果为:3 3 4

分析图:


个人理解:在这个函数运行的时候,其实运行了很多次,直到num1=3的时候(即最右边的方框),函数运行到最后一次,if失效,此时输出了3,之后回到左边的框,也就是上一层函数,if结束之后,还有一个doc..未运行,此时的num1是num1=4自减之后的结果,即为3,再输出一个3;然后再回到左边num1=5的框,运行该框的doc...此时该层的num1是num1=5自减之后的结果,即为4。至此,函数全部运行结束,故输出为3 3 4.


注意:

1.函数的参数个数可以任意指定。

2.参数可以是多个,每个参数的类型可以是任意的。

3.js函数天然支持参数个数可变。

如:

//编写一个函数,可以接受任意多个数,并计算他们的和
function abc2(){

//在js中有一个 arguments,可以访问所有传入的值
//window.alert(arguments.length);这样可以访问个数
//那么我们可以用for循环来遍历所有的参数
for(var i=0;i<arguments.length;i++){
window.alert(arguments[i]);
}
}


可以这样调用:

window.alert("abc2(45,90,900);");
abc2(45,90,900);

window.alert("abc2(4,\"hello world\")");
abc2(4,"hello world");

window.alert("abc2();");
abc2();

4.js支持动态创建函数。


练习:(视频第26讲结尾处)

1.金字塔封装

2.页面输入九九乘法表


◆数组

1.为什么需要数组

看一个需求:

王大爷养乌龟的问题:王大爷有6只乌龟,体重高分别是3kg、5kg、1kg、3.4kg、2kg、50kg。请问六只乌龟的总体重是多少?平均体重是多少?


2.解决方法:

使用数组,这种数据类型(引用类型/复杂类型/复合类型),数组的基本概念:用于存放一组数据。

特别强调:js中的数组,可以存放各种类型的数据(数值,字符串...)


王大爷养乌龟(快速入门):

//数组的遍历
for(var i=0;i<weights.length;i++){
document.writeln(weights[i]);
all_weight+=weights[i];
}
avg_weight=all_weight/weights.length;
document.writeln("总体重是"+all_weight);
document.writeln("平均体重是"+avg_weight.toFixed(2));//toFixed()函数可以控制一个数字的小数位数
window.alert(avg_weight.constructor);//若想知道某个变量的数据类型,可以使用这个来看数据类型。


3.数组的细节:

1)基本用法:

var 数组名=[元素1,元素2,...]

元素的值可以是任意类型:  var a=[1,2.5,sfd,true]


数组在内存中的存在形式: 


2)js中的数组是引用传递

举例说明:

var myarr=[456,90,900];
function abc2(arr){
arr[0]=35;
}
abc2(myarr);


for(var i=0;i<myarr.length;i++){
document.writeln(myarr[i]);
}//输出结果为多少? 答案是35  90  900

原理图:


3)数组的引用

基本用法:

数组名称[下标]      下标从0开始

比如:

var a=[23,dsad,4.6];


我们访问a[2],则会输出4.6;因为下标从0开始。

如果我们访问a[3](没有第四个元素),则会输出undefined(实际上并没有弹窗)

结论:不能访问不存在的元素     


4)js的数组可以动态增长,即已经定义好的数组,可以随时增加元素个数。

var a=[2,3];
alert("数组的大小是"+a.length);
a[2]=56;//动态的增长,数组变成了a=[2,3,56]
alert(a[2]);
alert("数组的大小是"+a.length);

5)字符串分割生成一个字符串数组,split()函数

比如:

var str="hello world creabine 陈磊";
var arr=str.split("",2);//split(“以什么分割字符串”,显示前几个【若不写则全部显示】)
for(var i=0;i<arr.length;i++){
document.write(arr[i]+" ");
}


6)遍历数组还有这个方法,不过很少用,了解即可,不用深究

var arr=[45,90,0];
//arr["gg"]=9000;//js的数组下标不仅可以用数字,还能用字符串,但是一般不用这个,了解即可
for(var key in arr){
window.alert(key+"="+arr[key]);
}
//这里的key是数组的下标,从0开始


//练习,运动会上,五个小孩比赛轮滑,他们划100米,分别用时10s,12s,5.7s,9s,14s,编写一个程序,计算它们的平均时间。在基础上,实现自主输入用时,并且显示平均值为2位小数。


//var time=[10,12,5.7,9,14];
//自主输入数字数组的方法
var a=window.prompt("请分别输入他们的时间,以,号间隔");
var time=a.split(",");//将输入的字符串转化为字符串数组
for(i=0;i<time.length;i++){
time[i]=parseFloat(time[i]);
}//遍历数组的所有元素并将他们的数据类型转为浮点型
var sum=0;
var avg=0;
for(var i=0;i<time.length;i++){
sum+=time[i];//遍历数组求和
}
avg=sum/time.length;//求平均
window.alert("平均时间为"+avg.toFixed(2));//显示2位小数


◆多维数组:二维数组

多维数组我们只介绍二维数组:一个数组的元素还可以是数组,这就构成了二维数组。二维数组中的元素数组之间的长度,可以不相同。


举例:

//二维数组
/*
var arr=[["creabine",123,4.5],["a","b","c"],[89,0]];//二维数组的元素数组长度可以不同
//二维数组的遍历方法
for(var i=0;i<arr.length;i++){//遍历每个元素
//输出每个元素arr[i][数组]的元素)
for(var j=0;j<arr[i].length;j++){
document.writeln(arr[i][j]+"&nbsp;");//arr[i][j]是arr[i]这个数组的j号元素
}
document.writeln("<br/>");//输出了一个元素之后,换行
}
//若要直接访问"c",那应该怎么写
window.alert(arr[1][2]);
*/


//请输入如下图形
/*
0 0 0 0 0 0
0 0 1 0 0 0
0 2 0 3 0 0
0 0 0 0 0 0
*/

代码:
/*
var arr=[[0,0,0,0,0,0],[0,0,1,0,0,0],[0,2,0,3,0,0],[0,0,0,0,0,0]];
for(i=0;i<arr.length;i++){
for(j=0;j<arr[i].length;j++){
document.writeln(arr[i][j]+"&nbsp;");
}
document.writeln("<br/>");
}
*/

◆矩阵转置

//矩阵转置,即行列互换


var arr=[[2,4,6,8],[8,9,0,-1],[9,6,2,1]];
//定义一个新的数组
var arr2=[];
//初始化,定下来有多少行(旧数组的列就是新数组的行)
for(var i=0;i<arr[0].length;i++){//循环旧数组的列,有4列
arr2[i]=[];//给新数组arr2创造元素数组,也就是新矩阵的行,循环旧数组的列,得到新数组的行,4行。这里开辟的arr2数组的储存空间,所以下边的arr2[j][i]=arr[i][j];才能够实现访问并储存。这里的初始化很重要,要先初始化一个空间给新的数组,不然下边的转置无法实现。
}
//可以动态的添加数据
//遍历旧的数组
for(var i=0;i<arr.length;i++){
for (var j=0;j<arr[i].length;j++){
arr2[j][i]=arr[i][j];//转置是行列互换,直接换i,j即可
}
}
//成功,遍历arr2数组就是一个转置数组
for(var i=0;i<arr2.length;i++){
for (var j=0;j<arr2[i].length;j++){
document.writeln(arr2[i][j]+"&nbsp;");
}
document.writeln("<br/>");
}


◆js基本语法:排序,用的很少

冒泡排序:

原理图:


原理:从最下边的一个数开始,依次跟头上的数字比较大小,大的就呆在上边,再跟上边一个比较,再换位置。如图第一列,最下边的35跟88比较,88大,维持原位;然后88跟16比较,88大,换到16上边;依次比较,直到88跟90比较,90大,90在上边;然后90再分别与56,79比较,都比他们大,所以90最大,排到第一了。得到第一趟跑完的结果。然后再从最下边开始,跑第二趟,直到排序完成。


案例:

//冒泡排序
var arr=[5,0,-56,900,12,9000,-123];
//大的排序次数(arr.length-1), 第一次排序,找出第一大的放在第一位;第二次排序,找出第二大的放在第二位;以此类推,只要arr.length-1次就够了,倒数第二大的已经在倒数第二位了,最小的不用动了。
for(var i=0;i<arr.length-1;i++){//这里的i可以看作已经跑了i趟,一开始i=0,表示还没跑,跑完一趟之后i=1,表示已经跑了1趟了。
//小的排序
for(var j=0;j<arr.length-1-i;j++){//共有arr.length个数,拿其中一个数,来跟其他数比较,就要比较arr.length-1次。如果已经跑了i趟,那么前i个数已经固定位置了,不需要再比较了,所以只需要比较arr.length-1-i次就可以了。
if(arr[j]>arr[j+1]){//两两比较,若左边的比右边的大,交换
//交换
var temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}//排序结束
for(var i=0;i<arr.length;i++){
document.writeln(arr[i]+"&nbsp;");//输出所有元素
}


冒泡排序的优化案例:

//冒泡排序
var arr=[1,4,6,8,9,90,800];

var flag=false;
//大的排序次数(arr.length-1), 第一次排序,找出第一大的放在第一位;第二次排序,找出第二大的放在第二位;以此类推,只要arr.length-1次就够了,倒数第二大的已经在倒数第二位了,最小的不用动了。
for(var i=0;i<arr.length-1;i++){//这里的i可以看作已经跑了i趟,一开始i=0,表示还没跑,跑完一趟之后i=1,表示已经跑了1趟了。
document.writeln("大循环....<br/>");//看看跑了多少趟大循环,在有优化的时候,只要跑一趟,若没有优化,则要跑arr.length-1,也就是6趟。
//小的排序
for(var j=0;j<arr.length-1-i;j++){//共有arr.length个数,拿其中一个数,来跟其他数比较,就要比较arr.length-1次。如果已经跑了i趟,那么前i个数已经固定位置了,不需要再比较了,所以只需要比较arr.length-1-i次就可以了。
if(arr[j]>arr[j+1]){//两两比较,若左边的比右边的大,交换
//交换
var temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=true;//flag本来被赋值为false,如果发生了交换,那么falg变为true,也就是,用这个flag的值来判断是否有发生交换。
}
}
//排序优化
if(flag){
flat=false;//这里是,如果falg的值为true,也就是发生了交换,那么把falg的值重置为false。
}else{//如果falg的值是false,说明这一趟,没有发生交换,就说明,已经排序成功了,之后的趟不需要再跑了,直接break跳出循环即可。
break;
}
}//排序结束
for(var i=0;i<arr.length;i++){
document.writeln(arr[i]+"&nbsp;");//输出所有元素
}



◆js查找:

1.顺序查找:取出来一个一个的比对

2.二分查找:

注意:二分查找有一个前提:该数组必须是有序的。若不是有序数组,则不能使用二分查找。


//二分查找思路:找到数组的中间数(midVal),和你要查找的数(findVal)进行比较,如果midVal>findVal,则说明findVal在数组的左边,就把该数组二分了(就只在左边查找了。)
var arr=[1,4,6,8,9,90,800];
function binarySearch(arr,findVal,leftIndex,rightIndex){
//该函数的变量分别是:数组,要查找的值,数组最左边的脚标,数组最右边的脚标


//防止无穷递归       没懂,为啥???
if(leftIndex>rightIndex){
//提示找不到
document.writeln("找不到");
return;
}
//找到中间这个值
var midIndex=Math.floor((leftIndex+rightIndex)/2);//找到中间的那个脚标。
//leftIndex+rightIndex)/2的值有可能是小数,所以这里使用Math.floor()函数,进行向下取整。
var midVal=arr[midIndex];//由中间的脚标得到中间的值

//与中间值进行比较
if(midVal>findVal){//若中间值大于要找的值,说明要找的值在数组中间值的左边,所以在左边找
//在左边找
binarySearch(arr,findVal,leftIndex,midIndex-1);//这里再次调用函数,讲左边的部分再二分,此时最右的脚标变为了midIndex-1,因为midVal>findVal,所以midVal不等于findVal,他不用参与下一次二分,直接从midIndex-1这个脚标的值开始
}else if(midVal<findVal){//原理同上
//在右边找
binarySearch(arr,findVal,midIndex+1,rightIndex);//原理同上
}else{//也就是midVal=findVal的时候,说明找到了
document.writeln("找到  下标为"+midIndex);//打印出找到的值得下标
return;
}
}


//测试:
binarySearch(arr,8,0,arr.length-1);







                  js面向(基于)对象编程

澄清概念:

1.js中,基于对象  等同于  面向对象

2.js中,按照标准的说法,是没有类(class)的,没有class这个关键字。但是它去了一个新的名字,叫  原型对象,因此   类   等同于  原型对象。


◆为什么需要对象?

问题提出:

/*
张老太养了两只猫:一只叫小白,3岁,白色。另一只叫小花,10岁,花色。编写程序,当输入猫的名字,就显示猫的名字,年龄,颜色。若用户输入的小猫名字错误,则显示张老太没有这只猫。
*/
//传统放大可以设置多个变量,一个一个赋值,比较麻烦。

//新的解决方法,把猫的属性集中起来,创建一种新的数据类型(原型对象/类)
//用面向对象的方法来解决上面的问题


//这里就是一个Cat类。
function Cat(){
}
//不同的用法,Cat()可以作为函数,也可以作为类。
Cat();//函数
var cat1= new Cat();//类,此时cat1就是一个对象(实例)
cat1.name="小白";
cat1.age=3;
cat1.color="白色";
//从上面的代码我们可以看出:1.js中的对象属性可以动态的添加。2.属性没有限制
window.alert(cat1.name+cat1.age+cat1.color);


◆类(原型对象)与对象的区别和联系:

1.类是抽象的,概念,代表一类事物

2.对象是具体的,代表一个实体

3.对象是以类(原型对象)为模板创建出来的。


◆创建对象的方式有五种:

1.工厂方法--使用new Object创建对象并添加相关属性;

2.使用构造函数来定义类(原型对象)

3.使用prototype

4.构造函数及原型混合方式

5.动态原型方式



目前我们先讲   使用构造函数来定义类(原型对象),然后再创建对象实例。


基本语法:

function 类名(或原型对象名)  (){

}


创建对象

var 对象名=new 类名();


现在对对象我们特别说明:

1.js中一切都是对象

function Person(){}
var a=new Person();
window.alert(a.constructor);//a.constructor,打印出对象实例的构造函数
window.alert(typeof a);//typeof a,打印出a的类型

//其实 a  b  c  都是类
var b=123;
window.alert(b.constructor);
window.alert(typeof b);
var c="123";
window.alert(c.constructor);
window.alert(typeof c);


◆如何判断一个对象实例是不是某个类型


//思考,如何判断一个对象实例是不是Person类型?

//这里的程序接上边的
//方法一:
if(a instanceof Person){//a instanceof Person 可以判断a是不是Person类型,如果是就返回true,就打印出来了。
window.alert("a是person");
}
//方法二:
if (a.constructor==Person){//a.constructor,打印出对象实例的构造函数判断是否是Person
window.alert("a是person 2");
}


补充说明:函数中带不带 var 的区别

//思考:下边两个例子,返回值为什么有区别
var abc=89;//定义一个全局变量
function test(){
//在下边的函数里,若不带var,则表示使用外边的全局变量abc,输出900
//若带上var,则表示在test()函数里边定义了新的局部变量abc,输出89
var abc=900;
}
test();
window.alert(abc);



◆访问对象的属性的方法有两种:

1.普通方式

对象名.属性名

2.动态访问,这里的属性名是字符串,可以拼接,达到动态访问的目的

对象名["属性名"]


例如:

function Person(){};
var p1=new Person();
p1.name="creabine";

window.alert(p1.name);
window.alert(p1["name"]);

var val="na"+"me";//拼接
window.alert(p1[val]);//拼接之后也能访问



对象引用问题说明:(图)



js还提供一种方式,主动释放对象内存


delete  对象名.属性名;//这样就会立即释放 对象的这个属性空间


案例:

function Person(){};
var a=new Person();
a.age=10;
a.name="小明";
var b=a;
b.name="小白";
window.alert(b.age+"名字"+b.name+"名字"+a.name);
//这里输出     10名字小白名字小白   因为:a和b都只是Persoon()的地址而已,两个变量都是它的地址,访问的都是同一个它,所以b.name="小白";修改之后,用a和b访问,得到的都是小白。


delete a.age;//删除a对象的age属性
window.alert(a.age+"名字a"+a.name);//删除之后age变成undefined


b=null;//将b置 空
window.alert(a.age+"名字a"+a.name);//a还能继续访问
window.alert(b.age+"名字b"+b.name);//b就不行了,不显示任何东西


◆this 

问题的提出:

function Person(){
}
var p1=new Person();
p1.name="顺平";
p1.age=60;
window.alert(p1.name+""+p1.age);
var p2=new Person();
window.alert(p2.name);//这里会输出什么(undefined)

//这里我们可以看到window.alert(p2.name);输出的是undefined


实际编程中,可能有这样的需求,当我们创建一个对象之后,就希望该对象自动拥有某些属性,比如上边创造了Person对象后,就希望该对象自动拥有name和age属性,这又怎么办?


使用this来解决:

案例:

//this的使用
function Person(){
this.name="abc";
this.age=90;//这里用this,相当于把name这个属性变成了全局变量,如果直接var name的话,就是这个函数里边的局部变量,这样下边的访问就访问不到,会undefined。
}//这样以后建立的实例都自动拥有name和age属性并且自动赋值。
var p1=new Person();
var p2=new Person();//这里的p1,p2是两个不同的实例,存在不同的地址,修改其中一个的属性不影响另一个,所以有下边的
window.alert(p1.name+""+p2.name);
p2.name="陈磊";
window.alert(p1.name+""+p2.name);

原理图:



可能有人会这样想问题:

function Person(){

var name="abc";//私有的,只能在内部使用

var age=900;  //私有的,只能在内部使用

this.show=function(){  //如果你一定要访问私有属性,则需要定义一个公开方法(特权方法)

window.alert("name="+name+"age"+age);

}

}

var p1=new Person();

window.alert(p1.name+""+p1.age);//错误的,无法访问,因为那两个是局部变量,会显示undefined

p1.show();//定义公开方法后,这样就可以访问了。



注意:谁调用函数,函数里的this就是谁。

案例1:

/*
function Person(){
this.name="abc1";
this.age=900;
}
function show1(){
window.alert("hello"+this.name);
}
var p1=new Person();
p1.abc=show1;
show1();//这里并不是p1调用函数,没指定是谁调用跟,就默认window,所以这里函数里边的this是window的this,所以只会输出hello,后边的this.name没东西,什么都不输出
*/


//2
function Person(){
this.name="abc1";
this.age=900;
}
var name="北京";
function show1(){
window.alert("hello"+this.name);
}
var p1=new Person();
p1.abc=show1;
show1();//这里同样也是直接调用函数,没指定是谁调用跟,就默认window,所以this是window的this,但是这次有定义name,故而输出hello北京



案例2:

function Person(){
this.abc=function(){
window.alert(this.v);
}
}
var p=new Person();//p指到Person的地址,
p.v="hello";//给他加了个属性v
p.abc();//p调用了这个函数,P就是this,this.v就是p.v,就是hello


注意事项:this不能放在类的外部使用,否则调用者就变成了window


◆对象-----成员函数(方法)

比如:我们希望对象不但有属性,还希望他有行为(行为在程序中要靠函数来实现)

1.添加speak函数,输出 我是一个好人

2.添加jisuan函数,可以计算从1+...+1000的结果

3.修改jisuan函数,该方法可以接收一个数n,计算从1+...+n的结果

4.添加add成员函数,可以计算两个数的和

代码:做了一部分

function Person(name,age){
//下边就是使用传入的实际参数,去初始化属性
this.name=name;
this.age=age;
//输出自己的名字,这里this.show就是一个公开的函数,函数名是show
this.show=function(){//方法一:这里就是给Person这个类定义了一个初始化的参数,这个参数就是一个函数,这样的话每创建一个Person的对象实例p1,p2,p3.....,他们各自都会有这样一个函数
document.write("名字="+this.name);
}
//添加一个jisuan函数,可以计算从1+...+1000的结果
//修改,可以接受一个数n来计算和
this.jisuan=function(n){
var sum=0;
for(var i=1;i<=n;i++){
sum+=i;
}
return sum;
}
}

var p1=new Person("呵呵",90);
p1.show();

var p1=new Person("heihei",12);
p1.show();

document.write("<br/>res="+p1.jisuan(100));



◆除了上边的方法给一个对象添加函数还有另外两种种方式:

方法二:

function 类名(){

this.属性;

}

var 对象名=new 类名();

function 函数名(){

//执行

}

对象名.属性=函数名;//这样就相当于把函数赋给了这个  对象名.属性, 此时这个属性名就表示一个函数了

//调用时

对象名.属性名();

具体案例:

function Person(){
this.name="abc";
this.age=900;
}
function show1(){
window.alert("hello"+this.name);
}
//创建一个p1对象
var p1=new Person();
//把show1函数给p1.abc这个属性,这个属性就代表了这个函数了
p1.abc=show1;//这里的show1不带括号,就是把函数本身交给这个属性,如果这里写show1(),那就是把函数返回的值给abc
p1.abc();//调用这个函数



方法三:

对象名.属性名=function 函数名(参数列表){

//代码;

}

调用

对象名.属性名(实际参数)

具体案例:

function Person(){
this.name="ccc";
this.age=900;
}
var p1=new Person();
p1.abc=function show1(){//直接给对象的abc属性添加函数
window.alert("hello"+this.name);
}
p1.abc();



第四种:

前面的几种方法,有一个问题:那就是每个对象会独占函数代码,这样如果对象过多,则会影响效率,js设计者提供了另一个方法:原型(prototype)法,这样多个对象可以共享一个函数代码,节省空间,提高效率

代码解释:

function Person(name,age){
//下边就是使用传入的实际参数,去初始化属性
this.name=name;
this.age=age;
//输出自己的名字,这里this.show就是一个公开的函数,函数名是show
this.show=function(){//使用这种方法的话,每创建一个Person的对象实例p1,p2,p3.....,他们各自都会有这样一个函数,这些函数相互独立存在,在某些情况下,这样会浪费空间。

}

原理图



如果不需要他们相互独立,为了节省空间,可以这样:

function Dog(){

}

var dog1=new Dog();

//在这里,使用prototype[类] 去绑定一个shout。虽然定义的dog1在这句话之前,但定以后dog1也可以访问类里边的shout

Dog.prototype.shout=function(){

window.alert("小狗");

}

dog1.shout();

var dog2=new Dog();

dog2.shout();//此时,这个shout属性被放在了Dog() 这个类里边,由Dog创建的实例dog1,dog2并不会自己加上这个shout属性,但是都可以通过相应的dog1.shout和dog2.shout来访问类里边的shout属性,dog1.dog2共用同一个shout。


原理图:



◆补讲   ==  号的作用

1.当  ==  的两边都是字符串的时候,则比较内容是否相等

2.若  ==  两边是数字,则比较数字的大小是否相等

3.若  ==  两边是对象, 或是 对象函数,则比较地址知否相等。若地址相等,则说明用同一个空间。



Object类

Object类是所有js类的基类,提供了一种创建自动一对象的简单方式,不需要程序员再定义构造函数

例如:

/*
//创建Person实例
function Person(){
}
var p1=new Person();
p1.name="sp";
*/

//初步体验Object类:通过Object直接创建对象,方便很多
var p1=new Object();
p1.name="sp";
window.alert(p1.constructor);


小例子:

//我们可以给类添加方法
var i=new Number(10);//这句话其实就等价于  var i=10;
Number.prototype.add=function(a){//这里是给Number这个类增加了一个方法add,add是个函数,输入一个值a,会反悔this+a
return this+a;
}
window.alert(i.add(10).add(30));//这里调用add的是i,所以this是i的this,所以返回10+a,即10+10,再+30,所以输出50


◆加深对于  类和对象  的认识

如何给类添加方法(如何给某类型的所有对象添加方法):

//请思考给js的Array对象扩展一个find(Val)方法,当一个Array对象调用该方法的时候,如果能找到val则返回其下标,否则返回-1(正常下标不可能是-1).(数据手册里边已经有了各种该方法,但是如果我们需要,可以自定义一个。)

代码:
//体验一下Array
var arr1=new Array(3);
arr1[0]="a";
arr1[1]="b";
arr1[2]="c";
//遍历该数组
for(i=0;i<arr1.length;i++){
document.write(arr1[i]+"&nbsp;");
}
document.write("<br/>");
//使用Array提供的方法,颠倒数据
arr1.reverse();
for(i=0;i<arr1.length;i++){
document.write(arr1[i]+"&nbsp;");
}
//现在我们一起看看如何给所有Array对象添加一个方法find(Val)
Array.prototype.find=function(val){
//遍历数组this
for(var i=0;i<this.length;i++){
if(val==this[i]){
return i;
}
}
return "找不到"//这里我还是没用上边要求的-1,返回了  找不到。
}
document.write("下标="+arr1.find("a"));



◆闭包

这个知识点我们讲封装的时候再说。




◆成员函数的细节:(这里有几个小练习没有做,第33讲43:33前后)

1.成员函数的参数可以是多个

function 函数名(参数1,2,3.....){

}

2.函数可以没有返回值,但是最多只能有一个返回值。

3.js中,函数里边的变量可以是动态的,所以不通过变量的个数来区分函数,只看函数名,当定义同名的函数的时候,后定义的会覆盖先定义的,故只认最后定义的那个。

例如:

function test(a){
window.alert(a);
}
function test(a,b){
window.alert(a+""+b);
}
//js的函数里边的变量可以是动态的,所以不通过变量的个数来区分函数,只看函数名,当定义同名的函数的时候,后定义的会覆盖先定义的,所以不论定义了多少次这里的test只认最后一个,
//test(23);//故而,这里会输出 23undefined。
window.test(3,"hello");//这里的test(23);没写是谁调用,所以默认就是window调用,所以test(23);和window.test(23);是等价的



面向对象变成的综合案例:代码很多,好好看看

采用面向对象的思想设计超级马里奥游戏人物(示意图)


游戏的分析:

1.如何通过按钮来控制图片马里奥的位置

2.设计相关的对象(马里奥x,y)


基本代码写好了,在mario文件里边

未完成的要求1.mario遇到边界给一个提示,功能未完成,应该不难

  2.再放一个公主的图片,让mario去抓公主,抓到公主则弹窗哈哈抓到了

1)我一开始自己做了判定是否在同一点

2)改为判定碰撞

3.还能在基础上再加个要求,让mario可以通过键盘的上下左右键来控制。



问题:怎么直接获取css的内容?

答:

//这里仅作测试:如何调用div的各种属性
function test(){
//window.alert("test");
var div2=document.getElementById("div2");
//取出width
window.alert(div2.style.width);//如果div通过style来写各种属性,那么就可以这样调用。但是,如果用css就不行(据说某些浏览器可以)
}


div2代码:

<br/>
<div id="div2" style="width:500px;">div2</div>
<br/>


◆构造函数

基本用法:

function 类名(参数列表){

属性=参数值;

.........

}

举例:

function Person(name,age){
this.name=name;//给了初始化的值,name和age
this.age=age;
}
//创建Person对象的时候,就会直接给名字和年龄
var p1=new Person("abc",80);
window.alert(p1.name+p1.age);
var p2=new Person("ccc",9);
window.alert(p2.name+p2.age);


特别说明:在初始化一个对象实例的时候,也可以给他初始化一个函数,让他的一个属性是个函数

案例:

function jisuan(num1,num2,oper){
if(oper=="+"){
return num1+num2;
}else if(oper=="-"){
return num1-num2;
}else if(oper=="*"){
return num1*num2;
}else{
return num1/num2;
}
}
//创建Person对象的时候,就会直接给名字和年龄和函数
function Person(name,age,fun){
this.name=name;//给了初始化的值,name和age和fun
this.age=age;
this.myfun=fun;
}
//创建Person对象的时候,就会直接给名字和年龄和函数
var p1=new Person("abc",80,jisuan);
window.alert(p1.name+p1.age);


window.alert(p1.myfun(5,6,"+"));//可以直接用这个属性所带的函数

var p2=new Person("hehe",5);//p2实例也可以选择的不传入函数,直接不写,或者写null均可


◆创建对象的又一种形式:

如果一个对象比较简单,我们可以直接创建对象,不要类了,同时还可以给它指定普通属性和函数属性:

案例:

//比较简单的对象可以直接创建对象实例而不要类
var dog={name:"Tom",age:8};
window.alert(dog.constructor);//可以看到他是有构造函数的
window.alert(dog.name+dog.age);//也可以正常调用


扩展:这样也可以加函数

案例:

//甚至也可以给函数,例如
var cat={
name:"Sam",
age:4,
fun1:function(){window.alert("i m cat")},
fun2:function(){window.alert("i m 3")}
};
window.alert(cat.name+cat.age);
cat.fun1();
cat.fun2();



有时,我们会看到这样一种调用方法:

函数名.call(对象实例);//这样调用,就是叫来这个对象实例,来调用该函数,该函数的this就是这个对象实例


案例:

//
var dog={name:"hello"};
function test(){
window.alert(this.name);
}


test();
window.test();
test.call(dog);//表示叫来dog对象,来调用这个test函数,这样test的this就是dog了,this.name就是dog.name了
//dog.test();//但是这里用dog.test();什么都没有,为什么???

//因为,dog.test();是在调用dog的私有函数,写在dog的定义里才行,而这里的test是全局函数,无法这样调用。详见文件object.html的第110行,this的使用案例。



◆js面向对象编程的三大特性

1.封装

js提供有以下几种控制方法和属性的访问权限:

1)公开级别:对外公开

2)私有级别:类本身可以访问,不对外公开

案例:

//封装属性 和 方法
function Person(name,agei,sal){
this.name=name;//公开的属性
var age=agei;//私有属性
var salary=sal;//私有属性
//在类中如何定义公开方法(特权方法),私有方法(内部方法)
//若我们希望操作上边的私有属性,则可以通过公开方法实现
this.show=function(){//公开方法
window.alert(age+" "+salary);
}
//私有方法,类本身可以访问对象的属性,但无法在外边使用,被封装了
function show2(){
window.alert(age+" "+salary);
}
}

var p1=new Person("sp",20,50000);
window.alert(p1.name+" "+p1.age);//这里访问公开的name就可以,访问私有的age就会失败。
p1.show();//通过公开方法来访问私有属性
p2.show2();//无法在类的外部使用私有方法。被封装了


强调:我们前面学习过,通过prototype给所有的对象添加方法,但是这种方式,不能访问类的私有属性和方法。

举例:

function Person(){
this.name="abc";
var age=90;
//公开方法
this.abc=function(){
window.alert("abc()");
}
//私有方法
function abc2(){
window.alert("abc2()");
}
}


//使用prototype
Person.prototype.fun1=function(){
window.alert(this.name);//name是公开属性,能够显示
//window.alert(age);//age是私有属性,无法显示
this.abc();//这里的函数abc();是公开的,可以调用
//abc2();//这里无法调用abc2();因为他是私有方法
}


var p1=new Person();
p1.fun1();


2.继承

1)为什么需要继承?

问题:

/*
//继承例子
function MdiStu(name,age){
this.name=name;
this.age=age;
this.show=function(){
window.alert(this.name+" "+this.age);
}
//计算学费
this.payFee=function(money){
window.alert("应缴"+money*0.8);
}
}
function Pupil(name,age){
this.name=name;
this.age=age;
this.show=function(){
window.alert(this.name+" "+this.age);
}
//计算学费
this.payFee=function(money){
window.alert("应缴"+money*0.5);
}
}
*/


上面的代码存在冗余的问题。

可以用继承(js是通过对象冒充来实现继承效果的)来解决这个问题:


//怎么解决上边代码冗余问题?  用继承
//抽象出一个学生类(即把上边的中学生,小学生的共性取出)
function Stu(name,age){
this.name=name;
this.age=age;
this.show=function(){
window.alert(this.name+""+this.age);
}
}

function MidStu(name,age){
window.alert(Stu);//这里的Stu是个函数
this.stu=Stu;//这里相当于把Stu函数给了这个stu属性,所以这个属性拥有了这个函数所设定的那些name,age等属性。
this.stu(name,age);//stu属性经过上边那句,已经成了一个函数,相当于调用了这个函数,函数运行,就有了值;若不运行,函数里边的name,age也是空的,就没用了。这里的this,show并不会执行,因为没叫他执行。

/*
//MidStu可以覆盖Stu父类的show,这里覆盖了,所以最下边的midStu.show();会显示MidStu show();,若注销这里,不覆盖改,则会使用父类的函数,下边就会输出"顺平",20
this.show=function(){
window.alert("MidStu show();");
}
*/
}

function Pupil(name,age){
this.stu=Stu;
this.stu(name,age);//js中实际上是通过对象冒充,来实现继承的,这句话不能少,就是通过它实现了继承
}

var midStu=new MidStu("顺平",20);
midStu.show();


特别说明:js通过对象冒充的方式可以实现多重继承的效果。即可以继承好几个函数。


◆js的重载和重写:  js不支持重载,但又天然支持重载

说js不支持重载(即不可以通过参数的个数来决定调用哪个一同名函数),是因为js函数的变量个数是可变的,区分js函数只看函数名而不看变量个数,所以无法拥有重名的函数,若重名只认最后一个;但是又因为js支持参数个数可变,参数类型可变,所以可以随时修改,所以说又是天然支持重载的。

如:

function abc(){

fi(argument.length==?){

}else if(argument.length==?){

}else{}

}


重写:子类可以重新写函数,来覆盖父类的某个方法。见上边的例子。




◆多态      js是动态语言,在执行过程中会动态的变化,所以可以说是天然支持多态

案例:

function Person(){

this.test1=function(){

window.alert("Person test1");

}

}

function Cat(){

this.test1=function(){

window.alert("Cat test1");

}

}

var v=new Person();

v.test1();

v=new Cat();//js是动态语言,值会随着赋值的变化而变化,你给v什么,他就是什么

v.test1();


多态经典案例:主人给动物喂食

代码:

function Master(){
//主人刚给动物喂食
this.feed=function(animal,food){
document.write("主人给"+animal.name+"喂"+food.name);
}
}


//写食物类
function Food(name){
this.name=name;
//...其他
}
function Fish(name){
this.food=Food;
this.food(name);
}
function Bone(name){
this.food=Food;
this.food(name);
}


//写动物类
function Animal(name){
this.name=name;
//....其他
}
function Cat(name){
this.animal=Animal
this.animal(name);
}


function Dog(name){
this.animal=Animal
this.animal(name);
}


//
var cat=new Cat("小猫咪");
var dog=new Dog("小狗");
var fish=new Fish("小鱼");
var bone=new Bone("小骨头");


var master=new Master();
master.feed(cat,fish);
master.feed(dog,bone);
master.feed(cat,bone);


◆补充讲解闭包:

解释:

1.闭包和gc(垃圾回收)是相关联的

2.闭包实际上是涉及一个对象的属性合适被gc处理回收的问题

3.怎样才能对对象的属性形成闭包:当一个类里边有个变量,被外部引用的时候,这个变量的储存空间就不会被马上回收,而是留着,以防外部再度引用

案例:

//闭包  closure
function A(){
var i=0;
function b(){
window.alert(i++);
}
return b;
}
//闭包<---->gc
//A();//这里如果直接这样调用A函数,此时内存会分配给i一个空间,当你执行完函数之后,因为没有其他变量指向i,所以gc会回收i的空间
var c=A();//但是如果这样调用,把调用A函数之后的结果赋值给c,运行结束之后,因为还有c指向函数,所以gc认为以后还可能会用到,就不去收回i的空间,i的值就还在
c();//输出0,但是执行之后i变成了1
window.alert("aa");
//..之后可能还有其他执行语句
//..
c();//不论执行了什么,这里还能输出1,说明执行过后的i依然存在,值是1
c();//输出2,i依然存在,没被回收,被闭包了。








阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。本人已使用新账号Creabine,此博客内容均已搬过去。 https://blog.csdn.net/CreabineChen/article/details/49902703
想对作者说点什么? 我来说一句

javascript 学习笔记

2009年04月21日 40KB 下载

JavaScript学习笔记

2011年11月13日 272KB 下载

个人Javascript学习笔记 精华版

2009年05月27日 71KB 下载

js 笔记 javascript 学习笔记

2011年11月28日 3KB 下载

没有更多推荐了,返回首页

不良信息举报

javascript学习笔记

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭