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依然存在,没被回收,被闭包了。








  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值