JavaScript 基础
一、JavaScript语言基础(介绍js的基本语法)
1、所有的web开发永远都是请求和响应。
2、javascript组成:
1)、核心语法(ECMAScript) ECMA:欧洲计算机制造协会的简称
2)、Dom(文档对象模型) document object model
3)、Bom(浏览器对象模型) browser object model
3、js简介
1)、javascript是一种基于对象和事件的脚本语言,由浏览器来执行。
2)、可以与用户执行信息交互
3)、安全性,不允许访问本地磁盘
4)、跨平台,只要浏览器支持js就可以
4、js代码必须写在<script></script>标签内,
js里面推荐字符串都用单引号'',
html和css属性使用双引号""。
5、弹出警告框:alert()
6、整个js代码从上到下依次执行。用双斜杠//来注释js代码。
7、当某个<script>标签内有语法错误的时候,该<script>块中的代码都不执行,
但是不影响其他<script>块中的代码执行。
8、链入一个外部js文件:
先创建一个js文件1.js,然后在html网页中加<script src="1.js" type="text/javascript"></script>
9、声明变量的时候不需要声明数据类型,统一使用var来声明变量。js是一种弱类型语言。
声明一个变量,给其赋值多种类型的值是合法的。例如:var a=10;a=false;a=sdsfsfsf;
10、以字母、下划线或$开头,中间可以包括字母、数字、下划线或$
11、数据类型:Bollean(布尔)、Number(数字)、String(字符串)、Undefined(未定义)、Null(空对象)、Object(对象类型)
12、typeof() 返回的是变量的数据类型
13、if-else、while、do-while、for、switch、continue、break语句与C#差不多
1)、声明变量后,使用前一定要赋初值,否则变量就是undefined。
var sum=0;
for(var i=1;i<=100;i++){
sum+=i;
}
alert(sum);
2)、当使用一个未声明的变量时,浏览器会报错。
3)、方法没有返回值的时候,接收到的返回值是undefined
4)、无论变量是undefined还是null都表示该变量不可用
判断变量是否可用:
if(typeof(x)!='undefined'&&x!=null){变量可用}else{变量不可用}
14、相等:==,先判断数据类型是否一样,如果不一样,通过数据转换能够转成相同的值,则返回相等
1)、如果==两边都是字符串类型,那么必须这两个字符串完全相同才会返回true
2)、如果==两边的两个变量指向了同一个对象,那么也返回true
3)、如果==两边一个是字符串类型,一个是数字类型,那么js引擎会尝试把其中的字符串类型尝试转换为数字类型后再做比较
4)、如果==两边一个是布尔类型,一个是数字类型,则也会尝试把其中的布尔类型尝试转为数字类型后再做比较
5)、如果==两边其中一个是string或者number类型,而另外一个object类型,那么判断相等之前会先讲object转换为string或number后然后在与另外一个值比较
15、完全相等:===,直接进行比较,不做数据转换。相等就是相等,不相等就是不相等。
alert(sum);
var n1='123';
var n2=123;
alert(n1==n2); //true
alert(n1===n2); //false
var n1=null;
var n2;
alert(n1==n2); //true
alert(n1===n2); //false
undefined==udenfined //true
null==null //true
NaN==NaN //fales
null==undefined //true
new String("a")=="a" 返回结果是true,而new String("a")==new String("a")是false
16、变量的作用域:
1)、js中不存在块级作用域范围,在页面中声明的变量,在整个页面中的任何一个地方都能够使用
2)、在方法内部任何一个地方声明的变量的作用域为整个方法内部,即在方法内部任何地方都可以使用该变量
3)、当重复声明变量的时候,js会自动忽略第一次以后的所有声明语句,但是赋值语句正常执行。
4)、声明变量的时候如果不写var关键字,表示该变量是一个整个页面都可以访问的变量(全局变量)
17、转义符:'\'
alert('姓名:小红\r\n 年龄:20')
18、数据类型转换:
1)、字符串转化为数字,从给定字符串中找到第一个不能转化为数字的字符串,将其之前的字符串转化为数字
var n='123';
n=parseInt(n);
alert(n+1);//124
var n='123sssdafg';
n=parseInt(n);
alert(n+1);//124
var n='15';
n=parseInt(n,8); //把'15'按八进制转化为数字为13
alert(n);//13
2)、把任意类型转化位数字
Number('fales')=0;
Number('true')=1;
Number('undefined')=NaN;
Number('null')=0;
Number('new object()')=NaN;
3)、把任意类型转化为布尔类型
Boolean("")=false;
Boolean("Hi")=true;
Boolean(5)=true;
Boolean(null)=false;
Boolean(0)=fales;
Boolean(new object())=true;
Boolean(NaN)=false;
4)、把任意类型转换会String类型
var n=123;
n.toString();//"123"
String(n);//"123"
var n=null;
n.toString();//报错,null对象不能点出来toString()方法,不能调用任何方法
String(n);//"null" 通过该方法可以得到null对象就是一个null值,可以把任何类型的数据转换为string类型
19、判断字符串是不是NaN只能用IsNaN()来判断,不能用n==NaN来判断
20、eval():把字符串当作js代码来解析执行。可以用于拼接代码。不安全。
var n='var x=1;x++;alert(x);';
eval(n); //2
x++;
alert(x); //3
21、逻辑运算符:与C#中逻辑运算符多出来的功能就是能够直接将判断结果赋值给变量。
&& 逻辑与
|| 逻辑或
var r="a"||"";
alert(r); //a 逻辑或,第一个为真,第二个就不用判断了直接返回第一个为真的值
r=null ||15;
alert(r); //15 逻辑或,第一个为假,再判断第二个,如果第二个为真则返回第二个为真的值
r="a" && 0;
alert(r); //0 逻辑与,第一个为真,再判断第二个,如果第二个为假则返回第二个为假的值
r=null && 15;
alert(r); //null 逻辑与,第一个为假,第二个就不用判断了,直接返回第一个为假的值
r="12123" || 12;
alert(r); //12123 逻辑或,第一个为真,第二个就不用判断了直接返回第一个为真的值
r="12123" && 12;
alert(r); //12 逻辑与,第一个为真,第二个也为真,直接返回最近的第二个为真的值
22、调试:程序如果有错,按F12打开开发人员工具,点控制台console就能看到错误信息。
单步调试:打开开发人员工具,然后点调试器,开始调试,在需要调试的代码前面设置断点,然后再网页上刷新一下,程序就会运行到断点的位置,
选中要监视的变量,右键菜单选择添加监视,然后按F11单步调试,在监视窗口可以看到变量的值的变化情况。
调试的时候需要注意:1)、浏览器不能禁用js 2)、实在不能调尝试将浏览器的各种插件卸载清空重启一下 3)、同时只能使用用个调试器调试(不要几个浏览器或vs同时用)
23、函数:
命名规则:驼峰,首字母小写,之后的首字母大写。
//定义一个函数
function sayHi(){
alert('Hi');
}
//调用一个函数
sayHi();
24、函数重载:在js中没有函数重载
因为js中没有函数重载的概念,所以遇到同名函数的时候,会用最后一次定义的函数覆盖前面的函数定义。
下面例子调用的是第二个sayHi()。
//定义一个函数
function sayHi(){
alert('Hi');
}
//再定义一个同名函数
function sayHi(name){
alert(name);
}
//调用一个函数,此处调用的是sayHi(name)函数,返回结果为undifined
sayHi();
所有的方法都无需定义形参,定义形参的目的是为了使用方便,无论是否定义了形参,在实际调用的时候,所有参数都包含在arguements对象中。
function add(){
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return sum;
}
var result= add(1,1);
alert(result);
因为函数有预解析(在同一个标签内)的功能,所以在执行之前会将所有的函数进行预解析。在预解析的时候会使用最后定义的函数覆盖前面定义的重名函数。
var x=1,y=0,z=0;
function add(n){n=n+1;return n;}
y=add(x); //结果是4,因为预解析的时候最后定义的函数覆盖了前面定义的函数,这里调用的是最后定义的函数
function add(n){n=n+3;return n;}
z=add(x); //结果是4
当函数内部声明的变量与外部声明的变量重名时优先使用内部声明的变量
var uname='000';
function getName(){
alert(uname); //undefined 变量也有预解析,这里已经知道函数内部声明了一个uname变量,只能预解析声明,不能预解析赋值,所以这里的uname变量是一个未赋值的,执行的时候就会返回undefined
var uname="111";
alert(uname); //111 直接返回赋值的值
}
getName();
alert(uname); //000 函数内部的变量作用域只能在函数内部,这里是调用的最前面声明的变量
25、函数可以被当作函数来调用,也是一个对象,具有对象的基本方法。
function myFunction(){
for(var i=0;i<10;i++){
alert(i);
}
}
myFunction(); //函数调用
alert(myFunction.toString());//将函数当作字符串打印出来,返回函数的源代码
alert(myFunction.length);//函数定义的形参个数
var f1=function (){alert('这是一个匿名函数');}; //把函数赋值给了一个变量
f1(); //调用匿名函数
f1=100;
alert(f1);
f1=function (x,y) {return x+y;}; //把一个有形参的匿名函数赋值给变量
var r=f1(10,20); //调用有形参的匿名函数
alert(r);
//将一个函数当作另外一个函数的形参
function f1 (fn) {
alert('这是f1函数');
fn();
}
f1(function () {alert('直接传递的匿名函数');}) //将一个匿名函数当作形参传入另一个函数
f1(f12); //讲一个函数当作形参传给另一个函数
function f12() {
alert('12');
}
(function (x,y) {alert(x+y);})(10,20);//定义匿名函数的同时直接调用匿名函数
通过new Function()的方式定义匿名函数,可以将函数代码用字符串来表示,同时在函数中使用的变量,不会作为页面的全局变量出现,在函数外部也访问不到
var f1=new Function('x','y','z','alert(x+y+z);');
f1(10,20,30);
匿名函数不具备预解析功能
var x=1,y=0,z=0;
var add=function (n){n=n+1;return n;}
y=add(x); //结果是2,因为匿名函数不具备预解析功能,在这里相当于一个赋值语句,遵循从上到下的执行逻辑
var add=function (n){n=n+3;return n;}
z=add(x); //结果是4
26、数组:数组也可以作为另外一个数组的元素 var data=[1,2,3,[4,5,6]]; 这个数组的长度是4
//申明一个数组,这种写法叫做数组字面量
var numbers=[1,2,3,4,5]; //申明数组的时候同时初始化
alert(numbers.length); //数组长度
//循环遍历数组
for(var i=0;i<numbers.length;i++){
alert(numbers[i]);
}
//当调用数组对象的ToString()方法,会返回一个由逗号隔开的数组字符串
alert(numbers.toString());
//申明一个空数组
var data=[];
//将1-50之间的所有偶数放到data数组中
for(var i=1;i<=50;i++){
if(i%2==0){
data[data.length]=i;
}
}
alert(data.toString());
//清空数组,给数组赋值一个空数组或者让其长度为0
data=[];
data.length=0;
//数组的其他申明方式
var arr=new Arry(); //申明空数组
var arr=new Arry(1,2,3); //插入一个长度为3包含三个元素的数组
var arr=new Arry(5);//申明一个长度为5的数组
//将一个数组按从大到小顺序排序
var data=[9,1,2,5,4,6,7,3,8];//声明一个无序数组
fnSortArray(data); //调用排序的函数
alert(data.toString()); //弹出数组字符串
//定义一个函数,其功能是使用冒泡排序将无序数组按从大到小顺序排序
function fnSortArray(arrayObject){
var tmp,i,j;
for(var i=0;i<arrayObject.length;i++){
for(var j=arrayObject.length-1;j>i;j--){
if(arrayObject[j]>arrayObject[j-1]){
tmp=arrayObject[j];
arrayObject[j]=arrayObject[j-1];
arrayObject[j-1]=tmp;
}
}
}
}
//计算整数数组中的平均值
var data=[9,1,2,5,4,6,7,3,8];
var result= fnAverage(data);
alert(result);
function fnAverage(arrayObject){
var sum=0,i;
for(i=0;i<arrayObject.length;i++){
sum+=arrayObject[i];
}
return sum * 1.0/arrayObject.length;
}
//求一个数组中的最大值
var data=[9,1,2,5,4,6,7,3,8];
var result=fnMax(data);
alert(result);
function fnMax(arrayObject){
var max=arrayObject[0],i=1;
for(;i<arrayObject.length;i++){
if(arrayObject[i]>max){
max=arrayObject[i];
}
}
return max;
}
//将一个字符串数组['张三','李四','王五']输出以“|”分割的字符串
var arr=['张三','李四','王五'];
//var result=arr.join('|'); //调用js内置的join函数
var result=fnJoin(arr,'|')
alert(result);
//自己定义一个类似join的函数
//1、遍历数组中的每个元素 2、使用指定的分割符来拼接字符串
function fnJoin(arr,separator){
var str="",i;
for(i=0;i<arr.length-1;i++){
str=str+arr[i]+'|';
}
str=str+arr[arr.length-1]; //最后一个单独拿出来最后再拼接
}
//将一个数组中的元素进行前后反转
var data=[9,1,2,5,4,6,7,3,8];
//data.reverse(); //调用js内部的翻转函数
fnReverse(data);
alert(data);
function fnReverse(data){
var tmp,i;
for(i=0;i<data.length/2;i++){
tmp=data[i];
data[i]=data[data.length-1-i];
data[data.length-1-i]=tmp;
}
}
27、数组相关方法:详见http://www.w3school.com.cn/jsref/jsref_obj_array.asp
toString()方法:将数组元素以字符串形式打印出来
join()方法:把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔
reverse()方法:将数组元素前后反转
concat()方法:连接两个或更多的数组,并返回结果 。两个数组连接的长度等于两个数组长度的和。
var str1=['张三','李四','王五'];
var str2=['张三1','李四1','王五1',['ZS','LS','WW']];
var result=str1.concat(str2);//返回的结果是['张三','李四','王五','张三1','李四1','王五1',['ZS','LS','WW']]。打印出来看起来长度是9,实际上长度为7。
sort()方法:对数组的元素进行排序,sort()方法只能按从大到小从前到后得顺序排序(顺序),不能进行倒序,如果想要倒着排序,可以先调用数组sort()方法,之后再调用数组reverse()方法
sort()方法排序默认是将数组元素当作字符串来排序的,例如:var num=[2,1,11,23,3,34,4,45];
num.sort();
alert(num); //返回结果为:1,11,2,23,3,34,4,45
//如果想要将数组元素当作数字来排序,需要如下调用
num.sort(function (x,y){return x-y;}); //从小到大排
//num.sort(function (x,y){return y-x;}); //从大到小排
alert(num);//结果为:1,2,3,4,11,23,34,45
以上例子为元素为数字的数组,如果是元素为字符串的数组,可以按照字符串的长度来排序,匿名函数应该这么写:function (x,y){return x.length-y.length;}
为什么要在调用的时候传一个匿名函数呢?以下用代码来解释,此处模拟了sort()内部的实现原理
//定义一个函数(利用冒泡排序),这个函数的功能就是对数组进行升序排序,将比较两个元素的过程进行封装,因为不能确定要比较的元素是数字还是字符串
//因为数组是引用类型,所以不需要设置返回值,函数里面数组值改变,外面跟着也变了 -->
function fnSortAsc(arrayObject,fn){
var tmp,i,j;
for(var i=0;i<arrayObject.length-1;i++){
for(var j=arrayObject.length-1;j>i;j--){
if(fn(arrayObject[j],arrayObject[j-1])<0){
tmp=arrayObject[j];
arrayObject[j]=arrayObject[j-1];
arrayObject[j-1]=tmp;
}
}
}
}
//定义一个比较数字的函数
function fnCompareTwoValues(obj1,obj2){
//如果Obj1大于obj2则返回一个大于0的值,如果obj1小于obj2返回小于0,如果相等则返回0
return obj1-obj2;
}
var data=[2,1,4,3,6,5];
fnSortAsc(data,fnCompareTwoValues);
alert(data);
var arrName=['BigSkyDog','EatMoodChildSon','DrinkBeerChildSon','apple'];
//定义一个比较字符串长度的函数
fnSortAsc(arrName,function (x,y){
return x.length-y.length;
})
alert(arrName);
push()方法:向数组的末尾添加一个或更多元素,并返回新的长度
pop()方法:删除并返回数组的最后一个元素,即将数组得最后一个元素删除,然后再返回
shift()方法:删除并返回数组的第一个元素,即将数组的第一个元素删除,然后再返回
unshift()方法:向数组的开头添加一个或更多元素,并返回新的长度
slice()方法:从某个已有的数组返回选定的元素
splice()方法:删除元素,并向数组添加新元素
valueof()方法:返回数组对象的原始值
28、键值对数组(常说的json格式):键为数字的数组是一个特殊的键值对数组,数组都可以用for-in循环来遍历
var dict=new Array();
dict['dtg']='大天狗';
dict['cmtz']='茨木童子';
dict['jttz']='酒吞童子';
//根据键查找值
alert(dict['dtg']);
//打印数组的长度
alert(dict.length); //返回结果为0,因为数组长度只统计键为数字的元素
//如果要遍历键值对数组,因为数组长度为0,不能用for()循环方式,所以这时候可以用for-in循环(类似于C#中的foreach循环)
for(var key in dict){
alert('键:'+key+' 值是:'+dict[key]);
}
键值对数字也有一个简单的写法(对象字面量):
var dict={'dtg':'大天狗','cmtz':'茨木童子','jtz':'酒吞童子'};
因为是对象字面量,所以 除了用dict['dtg']的方式能取到值'大天狗',还可以用dict.dtg的方式取值,同时也可以用dict.ydj='妖刀姬'的方式向键值对数组中新增元素。
以下都是正确的键值对数组的格式:
var arr={'dtg':'大天狗','cmtz':'茨木童子','jtz':'酒吞童子'}; //整体是个键值对集合
var arr=[{'dtg':'大天狗','cmtz':'茨木童子','jtz':'酒吞童子'},{'dtg':'大天狗','cmtz':'茨木童子','jtz':'酒吞童子'}];//整体是一个数组,然后里面的每个元素又是键值对集合var arr={'dtg':'大天狗','cmtz':'茨木童子','jtz':'酒吞童子','yys':{'dtg':'大天狗','cmtz':'茨木童子','jtz':'酒吞童子'}};//整体是一个键值对集合,最后一个键的值又是一个键值对集合
29、字符串:
length属性:获取字符串的字符个数,不管是中文字符还是英文字符都算作一个字符。
var msg='你好js';alert(msg.length);//返回长度为4
charAt()方法:获取指定索引位置的字符
var msg='你好js';alert(msg.charAt(1));//返回字符为‘好’
indexOf()方法:获取指定字符在字符串中第一次出现的位置
var msg='我为人人,人人为我';alert(msg.indexof('我'));//返回位置为0
alert(msg.indexof('我',2));//返回位置为8,参数2为从位置为2的地方开始找第一次出现元素‘我’的位置为8
循环找出字符串中的某个字符的位置:
var msg='我为人人,人人为我,人人为我,人人为我,人人为我';
var index=0,count=0,keywords='人人';
while((index=msg.indexOf(keywords,index))!=-1){
count++;
alert('第' + count+'次出现“人人”的索引的位置是:'+index);
index=index+keywords.length;
}
substr()与substring()方法:
var msg='我为人人,人人为我';
msg=msg.substr(0,4); //从第0个元素开始截取4个字符
alert(msg); //返回‘我为人人’
var msg='我为人人,人人为我';
msg=msg.substr(5); //从第5个元素开始截取到最后
alert(msg); //返回‘人人为我’
var msg='我为人人,人人为我';
msg=msg.substring(5,8); 从第5个元素位置开始截取到第8个元素位置为止(不包含第8个位置的元素)
alert(msg);//返回‘人人为’
练习:去掉字符最后的‘|’
var msg='大天狗|茨木童子|酒吞童子|';
//msg=msg.substr(0,msg.length-1); //用substr()从第0个元素位置开始向后截取13个元素
msg=msg.substring(0,msg.length-1);//用substring()从第0个位置截取到位置13的元素为止(不包含位置为13的元素)
alert(msg);
toUpperCase()方法:将小写都转换成大写
toLowerCase()方法:将大写都转换成小写
split()方法:根据分隔符返回一个字符串数组,分隔符必须要有,不带参数的话就是返回全部元素,参数是几返回几个元素,如果所填参数大于实际元素个数也是返回实际最大个数
不带参数:
var msg='大天狗|茨木童子|酒吞童子';
var arr=msg.split('|'); //以‘|’为分割符分割字符串
alert(arr);
alert(arr.length);
带参数:
var msg='大天狗|茨木童子|酒吞童子';
var arr=msg.split('|',2); //以‘|’为分割符分割字符串,并且只返回2个元素
alert(arr);
alert(arr.length);
分割符用正则表达式:
var msg='大天狗|茨木童子%酒吞童子@妖刀姬&姑获鸟';
var arr=msg.split(/\||%|@|&/); //以‘|’或者‘%’或者‘@’或者‘&’为分割符分割字符串
alert(arr);
alert(arr.length);
30、日期: var d=new Date();//声明一个日期对象
console.log(d.getDate());//获取当前是几号
console.log(d.getFullYear());//获取当前是哪一年
console.log(d.getMonth());//获取当前是第几个月,默认第0个月为1月,第1个月为2月,依此类推。。。第11个月为12月
console.log(d.getDay());//获取当前是周几
console.log(d.getHours());//获取当前是几点
console.log(d.getMinutes());//获取当前分钟
console.log(d.getSeconds());//获取当前秒
console.log(d.getTime());//获取从1970年1月1号到当前时间的秒数
31、JS中的对象
//这是一个函数,也可称为“函数对象”,创建函数的时候如果要当作“构造函数”用则首字母大写,如果是当作函数用首字母小写。
function Person(){
}
//对Person()函数可以直接调用,即:直接把Person()当做函数来使用
Person();//把Person()当作普通函数来适用
//把Person()函数当作“类”来使用,通过Person()函数来创建一个Person()类型P
//当调用Person函数的时候在前面加关键字new,表示要通过Person()函数创建一个对象,此时把Person()函数叫做“构造函数”(不是“类”)
var p=new Person();//创建一个Person类型的对象P
//添加属性并赋值
p.user_name='lp';
p.age='20';
p.email='lp@163.com';
//创建一个方法
p.sayHi=function(){
};
p.sayHi();//调用方法
//通过构造函数创建Person对象,只有给this关键字创建成员属性,以后创建该对象的时候才能调用到成员属性
function Person(name,age,email){
this.user_name=name;
this.user_age=age;
this.user_email=email;
this.sayHello=function(){
};
}
//创建对象
var p1=new Person('lp',20,'lp@163.com');
p1.sayHello();//调用方法
//对象也可以看作是键值对集合,可以遍历
for(var key in p1){
alert(key+' '+p1[key]);
}
//判断一个对象是否是Person类型,适用instanceof关键字,类似于C#中的is关键字(p1 is Person)
alert(p1 instanceof Person);
alert(p1 instanceof Object);
//通过对象字面量来直接创建对象,可以快速的封装数据
var p1={
user_name:'lx',
user_age:19,
say_hi:function(){
alert('大家好我叫:'+this.user_name);
},
say_hello:function(){alert('我的年龄是:'+this.user_age);}
};
p1.say_hi();
p1.say_hello();
32、函数对象中的prototype属性:原型
//每次创建一个对象都是一个完全独立的对象,对象与对象之间没有关系,新对象调用方法的时候调用的是新对象自己的方法,因为创建新对象的时候不仅仅是给对象变量赋值,同时也创建了新对象自己的方法。好处就是新对象与新对象之间互不影响,弊端每创建一个对象都会创建一个重复的方法,而方法函数是一个引用类型,过多的重复方法会占用存储空间造成存储资源的浪费。
function Person(name,age){
this.user_name=name;
this.user_age=age;
this.say_hi=function(){
alert('我叫 '+this.user_name+' 我今年 '+this.user_age+' 岁了!');
};
}
var p1=new Person('tj',24);
var p2=new Person('jj',22);
p1.say_hi();
p2.say_hi();
function Person(name,age){
this.user_name=name;
this.user_age=age;
}
//将对象的方法通过对象的prototype属性创建,这样new一个对象的时候,新对象只用给对象的变量赋值,调用方法的时候调用的就不是新对象的方法,而是所有Person类型的方法,这样可以节约存储资源。
//函数对象Person本身是不具备name,age属性的,而通过new Person()的新对象才具有name,age属性
//prototype属性是函数对象Person的,下面通过Person创建的p1,p2普通对象没有prototype属性,在这里Person对象就叫做原型对象
//在原型对象Person的prototype中加一个sayHi方法,想要往原型对象中加方法可以往原型对象的prototype中加。
Person.prototype.sayHi=function(){
alert('我叫 '+this.user_name+' 我今年 '+this.user_age+' 岁了!');
};
var p1=new Person('tj',24);
var p2=new Person('jj',22);
p1.sayHi(); //为什么在创建对象p1,p2的时候并没有prototype属性,但是又可以调用prototype中的sayHi方法呢,因为在创建p1,p2的时候虽然没有创建prototype但是创建了一个字段__proto__ ,而这个字段的作用就是让对象p1,p2指向原型对象Person的prototype,这样就能调用方法了。
//在prototype中也有一个__proto__字段,作用和上面一样。 如果在prototype中没有找到要调用的方法,这时候程序就会通过prototype的__proto__字段往它的上一层或上上一层找,最后找到了就调用成功,找到object对象也没找到就会报错。
p2.sayHi();
------------------------------------------------------------------------------------------------------------------------
//原型对象中的成员是只读的
function Person(name){
this.user_name=name;
}
Person.prototype.user_age=20;
Person.prototype.friends=['张三','李四']
Person.prototype.sayHi=function(){
alert('大家好我叫:'+this.user_name+' 我今年:'+this.user_age);
};
var p1=new Person('田七');
p1.user_age=19;//这里是给p1对象添加了一个user_age属性并且赋值,这里的赋值对原型对象中的user_age没有影响,因为原型对象中的成员是只读的,不能修改值,这里是值类型的成员(字符串,数字都是值类型)
p1.sayHi(); //这里打印出来的田七的年龄是19
var p2=new Person('王八');
p2.sayHi();//这里打印出来王八的年龄是20,因为原型对象中的成员只读
---------------------------------------------------------------------
var p1=new Person('田七');
p1.friends[0]='王五'; //这里friends是一个数组,为引用类型,在原型对象(可以看作是栈)中成员为一个地址,而这里修改的内容是地址对应的存储器(堆)中的内容,所以说变的不是成员而是成员地址指向的一块内容。
alert(p1.friends);//这里打印出来的是王五、李四。因为原型对象中friends成员指向的内容已经被p1对象改变了,所以这里p2再调用这个成员也发生改变。
var p2=new Person('王八');
alert(p2.friends);//这里打印的是王五,李四
--------------------------------------------------------------------------
var p1=new Person('田七');
p1.friends=['王五','李四'];//这里和前面age的原因一样,程序想改原型对象中的friends成员,但是原型对象中的成员不可变,于是程序就给p1对象新加了一个属于p1对象的friends属性,这里的赋值对原型对象中的friends成员没有影响。
alert(p1.friends);//这里打印出来的是王五、李四。
var p2=new Person('王八');
alert(p2.friends);//这里打印的是张三,李四。因为p1对象的赋值对原型对象成员friends无影响,所以p2调用原型对象的friends也无变化。
//通过原型可以实现扩展方法的功能,最好只扩展自己写的方法,不要去给系统方法添加原型成员(String等)
//下面这样写,语法没错,但是不建议给系统方法添加原型成员,因为这样容易引起系统混乱(注册的方法多了,又严格区分大小写,不好区分)
var msg='abcdefghijklmn';
String.prototype.addHHH=function(){
return this+'HHH'
};
msg=msg.addHHH();
alert(msg);
33、JS中的继承:下面例子中Student对象继承自Person对象,Person对象继承自Object,Object继承自null。
//1、在js中没有类的概念,继承是通过对象和对象来实现的。
function Person(name,age){
this.user_name=name;
this.user_age=age;
}
Person.prototype.sayHi=function(){
alert('我叫 '+this.user_name+' 我今年 '+this.user_age+' 岁了!');
};
function Student(sid){
this.student_id=sid;
}
var p1=new Person('张三',18);
//继承
Student.prototype=p1;
var s1=new Student('10001');
//s1.sayHi();
//要修改s1的属性值可以直接改
s1.user_name='王五';
s1.user_age=20;
s1.sayHi=function(){
alert('我叫 '+this.user_name+' id是: '+this.student_id+' 我今年 '+this.user_age+' 岁了');
};
s1.sayHi();
34、闭包:实际上就是通过作用域链来改变变量的作用域,JS中的面向对象都是用‘闭包’来模拟的
function Person(name){
this.user_name=name;
var user_age=18;
}
var p1=new Person('lgz');
alert(p1.user_name);
alert(p1.user_age);//这里是undefined,因为上面Person对象没有把user_age注册给this,也就是这里的p1。而在此处调用p1.user_age相当于给p1注册了一个user_age,但是又没有赋值,所以打印出来就是undefined
--------------------------------------------------
function Person(name){
this.user_name=name;
var user_age=18;
this.setAge=function(age){
user_age=age;
};
this.getAge=function(){
return user_age;
};
}
var p1=new Person('lgz');
//在上面Person对象中注册了类似于C#中给私有字段添加属性的过程,就可通过下面的方式访问到user_age成员了
p1.setAge(20);
alert(p1.getAge());
-------------------------------------------------------------------------------
function f1(){
var funs=new Array();
for(var i=0;i<10;i++){
funs[i]=function(){
alert(i);
};
}
return funs;
}
var myFuns=f1();
//下面打印结果是10次10
for(var n=0;n<myFuns.length;n++){
myFuns[n]();
}
//为什么上面会打印出10个10,而不是0到9呢?
//f1()中返回的是一个函数数组funs(数组中存储了10个alert(i)),接着又将f1()赋值给myFuns,myFuns也就是一个函数数组(数组中存储了10个alert(i)),当循环遍历myFuns的时候,调用myFuns[n]()会循环执行数组中的每一个alert(i),而在调用myFuns[n]()的时候f1()中的for()循环早就已经循环完毕了,这个时候i的值已经为10,所以说不管后面n为几,alert(i)的结果一直都为10。
35、函数中的this:1)、在一个函数内部this指的是哪个对象,取决于当前函数是哪个对象的函数。当函数注册给不同的对象时,函数内部的this不会一直为某个对象而是会随函数所属的变化而变化。
2)、如果将函数
//下面的f1()中的this实际上就是window对象
function f1(){
alert(this);
}
f1();//打印结果为window,因为在页面上定义的函数默认就是注册给window对象,所以直接f1()调用函数和window.f1()这样调用是完全一样的。
--------------------------------------------------
var name='window中的name';
function Person(){
this.name='Person中的name';
this.getName=function(){
return function(){
return this.name;
};
};
}
var p=new Person();
alert(p.getName()());//这里打印出来的是window中name。因为Person对象中return function(){}并没有注册给this.getName,所以return function(){}中的name指的就是window.name
-----------------------------------------------------------
//如果想要打印出Person中的name,需要像如下 这样写
var name='window中的name';
function Person(){
this.name='Person中的name';
this.getName=function(){
alert(this.name);
};
}
var p=new Person();
alert(p.getName()());
//或者这样写
function Person(){
this.name='Person中的name';
//先将代表Person对象的this赋给that,再return that.name就可以了,这里的that就是Person对象
this.getName=function(){
var that=this;
return function(){
return that.name;
};
};
}
var p=new Person();
alert(p.getName()());
36、js编写事件:
<body>
<input onclick="alert(new Date().toLocaleTimeString());" type="button" name="name" value="显示当前时间"/>
<a onclick="alert('hello');" href="http://www.baidu.com">百度</a> //单击超链接之后弹出hello,页面跳转至百度,将js代码写到onclick事件
<a onclick="alert('hello');return false;" href="http://www.baidu.com">百度</a> //单击超链接之后弹出hello,页面不跳转,将js代码写到onclick事件
<a href="javascript:alert('hello');">百度</a> //单击超链接之后弹出hello,将js代码直接写到href里,其中javascript:是一种解析js代码的协议
</body>