条件分支语句也叫switch语句
语法:
执行流程:switch…case…语句在执行时会依次将case后的表达式的值和switch后的表达式的值进行全等比较,如果比较结果为true,则从当前case处开始执行代码,当前case后的代码都会执行,可以在case后面加break,这样就可以确保只会执行当前case语句,而不会执行其他case语句。如果比较结果为false,则继续向下比较
如果所有的比较结果都为false,则执行default后的语句
switch语句和if语句的功能实际上有重复的,使用switch可以实现if的功能,同样使用if也可以实现switch的功能,使用时可根据自己的习惯选择
<script> //输出yi,er,san。使用break可以退出switch语句
num = 1;
switch (num) {
case 1:
console.log("yi");
case 2:
console.log("er");
case 3:
console.log("san");
}
</script>
<script>
var score = 60;
switch (score) {
case 62:
alert("合格");
break;
case 61:
alert("合格");
break;
case 60:
alert("合格");
break;
default:
alert("输入错误");
break;
}
</script>
当分数大于60时需要写很多次合格,所以可以省去大于60时case的执行语句
<script>
var score = 60;
switch (score) {
case 62:
case 61:
case 60:
alert("合格");
break;
default:
alert("输入错误");
break;
}
</script>
需要列出60-100间的所有数,太繁琐
<script>
var score = +prompt("请输入成绩:");
switch (parseInt(score / 10)) {
case 10:
case 9:
case 8:
case 7:
case 6:
alert("合格");
break;
default:
alert("不合格");
break;
}
</script>
<script> //最优解
var score = +prompt("请输入成绩:");
switch (true) {
case score>=60:
alert("合格");
break;
default:
alert("不合格");
break;
}
</script>
<script>
var day = +prompt("请输入数字:");
switch (day) {
case 1:
alert("星期一");
break;
case 2:
alert("星期二");
break;
case 3:
alert("星期三");
break;
case 4:
alert("星期四");
break;
case 5:
alert("星期五");
break;
case 6:
alert("星期六");
break;
case 7:
alert("星期天");
break;
default:
alert("非法输入");
break;
}
</script>
循环语句反复执行一段代码
while循环
语法:
while语句在执行时先对条件表达式进行求值判断,如果值为true,则执行循环体,循环体执行完毕以后,继续对表达式进行判断,如果为true,则继续执行循环体,以此类推,如果值为false,则终止循环
//像这种将条件表达式写为true的循环,叫做死循环
//该循环不会停止,除非浏览器关闭,死循环在开发中慎用
//可以使用break终止循环
var n=1;
while(true){
alert(n++);
if(n==10){break;}
}
创建一个循环,往往需要三个步骤
1、初始化一个变量:var i=0;
2、在循环中设置一个条件表达式:while(i<10)
3、定义一个更新表达式,每次更新初始化变量:i++;
do…while循环
语法:
执行流程:do…while语句在执行时,会先执行循环体,循环体执行完毕后,再对while后的条件表达式进行判断,如果结果为true,则继续执行循环体,执行完毕,继续判断,以此类推。如果值为false,则终止循环
while与do…while语句功能类似,不同的是while是先判断再执行,do…while是先执行再判断
do…while可以保证循环体至少执行一次,而while不会
var n=1000;
var i=0;
while(n<5000){
n*=1.05;
i++;
}
alert(i);
for语句也是一个循环语句,也称为for循环
在for循环中,为我们提供了专门的位置来放三个表达式
1、初始化表达式
2、条件表达式
3、更新表达式
语法:
for循环的执行流程:
1、执行初始化表达式,初始化变量(初始化表达式只会执行一次)
2、执行条件表达式,判断是否执行循环,如果为true,则执行循环语句,如果为false,终止循环
3、执行更新表达式,更新表达式执行完毕必须重复2
for循环中的三个部分都可以省略,也可以写在外面。如果在for循环中不写任何表达式,只写两个(;),此时循环是一个死循环,会一直执行下去for( ; ; )
var i=0;
for(;i<10;){
alert(i++)
}
var result=0;
for(var i=1;i<=100;i++){
if(i%2==1){
result+=i;
}
}
alert(result);
var count=0;
var result=0;
for(var i=1;i<=100;i++){
if(i%7==0){
count++;
result+=i;
}
}
alert(count);
alert(result);
var num=100;
for(var i=100;i<1000;i++){
var a=parseInt(i/100);
var b=parseInt((i%100)/10);
var c=parseInt(i%10);
if(a*a*a+b*b*b+c*c*c==i){
alert(i);
}
}
var num = +prompt("请输入一个大于1的数:");
var count = 0;
if (num <= 1) {
alert("输入不合法");
} else {
for (var i = 2; i < num; i++) {
if (num % i == 0) {
count++;
}
}
if (count > 0) {
alert(num + "不是质数");
} else {
alert(num + "是质数");
}
}
老师的代码:定义了一个状态flag=true(第40课,详细解析)
var num = +prompt("请输入一个大于1的数:");
var count = 0;
if (num <= 1) {
alert("输入不合法");
} else {
var flag=true;
for (var i = 2; i < num; i++) {
if (num % i == 0) {
flag=false;
}
}
if (flag) {
alert(num + "是质数");
} else {
alert(num + "不是质数");
}
}
var num=+prompt("请输入*的行列数:");
for(var i=0;i<num;i++){
for(var j=0;j<i;j++){
document.write("*");
}
document.write("*"+"
");
}
写完后发现点问题,本来是i代表列数,在i=0时,第一行不执行第二个for循环,而是直接输出一个*并换行,i=1时才开始执行第二个for循环,并且第二个for循环输出的图标比此时的行数少一的,缺少的一个由
前的一个图案补足
//可读性强的写法
var num=+prompt("请输入*的行列数:");
for(var i=0;i<num;i++){
for(var j=0;j<i+1;j++){
document.write("*");
}
document.write("
");
}
for(var i=1;i<=9;i++){
for(var j=1;j<=i;j++){
document.write(""j+ "*"+i+"="+ i*j +""); //为了让页面更加美观可以设置样式,添加标签,给设置样式
}
document.write("
");
}
<style>
span{display:inline-block;width:80px;}//span是一个行内元素,不能设置宽度,所以将它改变为行内块元素
</style>
var flag=true;
for(var i=2;i<=100;i++){
for(var j=2;j<i;j++){
if(i%j==0){
flag=false;
}
}
if(flag==true){
document.write(i+" ");
}
flag =true; //之前忘记重置flag,导致 输出的只有2,3
}
break关键字可以用来退出switch或循环语句(不能在if语句中使用break和continue)
break关键字,会立即终止离它最近的那个循环语句
for(var i=0;i<5;i++){
console.log("外层循环"+i);
for(var j=0;j<5;j++){
break;
console.log("内层循环"+j);
}
} //加上break后只会输出5个外层循环,内层循环不执行
可以为循环语句创建一个label,来表示当前的循环
label:循环语句
使用break语句时,可以在break后跟一个label,这样break将会结束指定的循环,而不是最近的
outer:
for(var i=0;i<5;i++){
console.log("外层循环"+i);
for(var j=0;j<5;j++){
break outer;
console.log("内层循环"+j);
}
} //仅输出外层循环0
continue关键词可以用来跳过当此循环,同样continue也是默认只会对离它最近的循环起作用
for(var i=0;i<5;i++){
if(i==2){
continue;
}
console.log(i);
} //输出0、1、3、4.跳过2
改进输出质数代码
var flag=true;
for(var i=2;i<=100;i++){
for(var j=2;j<i;j++){
if(i%j==0){
flag=false;
break; //只要有一个求模为0,便不用继续求剩下数字的模
}
}
if(flag==true){
document.write(i+" ");
}
flag =true;
}
测试程序的性能,在程序执行前,开启计时器
console.time(“计时器的名字”) 可以用来开启一个计时器,需要一个字符串作为参数,这个字符串会作为计时器的标识
console.timeEnd()用来停止一个计时器,需要一个计时器的名字作为参数
console.time("text")
var flag=true;
for(var i=2;i<=100;i++){
for(var j=2;j<i;j++){
if(i%j==0){
flag=false;
break; //只要有一个求模为0,便不用继续求剩下数字的模
}
}
if(flag==true){
document.write(i+" ");
}
flag =true;
}
console.timeEnd("text");
进一步改进质数输出代码,可将第二个for循环判断条件改为 j<根号i,因为根号后的整除数是重复的
例如:36
1、36
2、18
3、12
4、9
6、6
Math.sqrt() 对一个数进行开方
console.time("text")
var flag=true;
for(var i=2;i<=100;i++){
for(var j=2;j<Math.sqrt(i);j++){ //将i改为Math.sqrt(i),提高性能
if(i%j==0){
flag=false;
break;
}
}
if(flag==true){
document.write(i+" ");
}
flag =true;
}
console.timeEnd("text");
JS中的数据类型:String 字符串、Number 数值、Boolean 布尔值、Null 空值、Undefined 未定义、Object 对象
除object属于基本数据类型,以后我们看到的值只要不是上面5种,就全是对象
基本数据类型都是单一的值,值和值之间没有任何联系,都是独立的,不能成为一个整体
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性
对象的分类:
1、内建对象 由ES标准中定义的对象,在任何ES的实现中都可以使用,如:Math、String、Number、Function…
2、宿主对象 由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象,如BOM、DOM
3、自定义对象 由开发人员自己创建的对象
创建对象
使用new关键字调用的函数,是构造函数constructor
构造函数是专门用来创建对象的函数,使用typeof检查一个对象时,会返回object
var obj=new Object();
在对象中保存的值称为属性
向对象添加属性 语法: 对象 . 属性名 = 属性值
obj.name = "孙悟空"; //向obj中添加一个name属性
obj.gender = "男"; //向obj中添加一个gender属性
obj.age = 18; //向obj中添加一个age属性
读取对象中的属性 语法:对象 . 属性名
如果读取对象中没有的属性,不会报错,而是返回undefined
console.log(obj.name);
修改对象的属性值 语法:对象 . 属性名 = 新值
obj.name = "tom" ;
删除对象的属性 语法:delete 对象 . 属性名
delete obj.name;
属性名 对象的属性名不强制要求遵守标识符的规范,什么乱七八糟的名字都可以使用,但是使用时还是尽量按照标识符的规范去做
如果要使用特殊的属性名,不能采用 . 的方式来操作
需要采用另一种方式 语法:对象 [ " 属性名 " ] = 属性值
读取时也需要采用这种方式
obj["123"] = 789;
console.log(obj["123"]);
??
使用[ ]这种形式去操作属性,更加的灵活。在[ ] 中可以直接传递一个变量,这样变量值是多少就会读取那个属性
obj["123"]=789;
var n = "123" ;
console.log(obj[n]); //输出789。如果是["123"]就写死了
属性值 JS对象的属性值可以是任意的数据类型,甚至也可以是一个对象
var obj = new Object();
var obj2 = new Object();
obj.test=obj2;
obj2.name="Tom";
console.log(obj.obj2.name) //输出Tom
in运算符 可以检查一个对象中是否含有指定的属性,如果有则返回true,没有则返回false
语法: “ 属性名 ” in 对象
console.log("test2" in obj); //接上个例子,输出false
基本数据类型:string、number、boolean、null、undefined
引用数据类型:onject
JS中的变量都是保存在栈内存中的,基本数据类型的值直接在栈内存中存储,值与值之间是独立存在的,修改一个变量不会影响其他变量
对象是保存在堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用)
var obj = new Object();
var obj2 = new Object();
obj.name = "孙悟空" ;
obj=obj2;
obj.name = "猪八戒";
console.log(obj.name); //输出猪八戒
console.log(obj2.name); //输出猪八戒
如果两个对象保存的是同一个对象引用,当通过一个变量修改属性时,另一个也会受到影响
var obj = new Object();
var obj2 = new Object();
obj=obj2;
obj2 = null ;
console.log(obj); //输出object
console.log(obj2); //输出null
当比较两个基本数据类型的值时,就是比较值,而比较两个引用数据类型时,比较的是对象的内存地址,如果两个对象是一模一样的,但是地址不同,也会返回false
var obj3= new Object();
var obj4= new Object();
obj3.name = "沙和尚";
obj4.name = "沙和尚";
console.log(obj3==obj4); //输出false
对象字面量
使用对象字面量来创建一个对象
var obj = {};
使用对象字面量创建对象时,可以直接指定对象中的属性
语法: { 属性名:属性值,属性名:属性值…}
对象字面量的属性名可以加引号也可以不加,建议不加,如果要使用一些特殊的名字,则必须加引号
属性名和属性值是一组一组的名值对结构,名和值之间使用冒号(:)连接,多个名值对之间使用逗号(,)隔开。如果一个属性之后没有其他属性了,就不用再加逗号(,)了
var obj2 = {
name:"猪八戒",
age:28,
test:{name:"沙和尚"}};
函数function 也是一个对象
函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码),函数中可以保存一些代码在需要的时候调用
使用typeof检查一个函数对象时,会返回function
1、在实际开发中很少使用构建函数来创建一个函数对象
可以将要封装的代码以字符串的形式传递给构造函数,封装到函数中的代码不会立即执行,而是在函数调用时执行
调用函数 语法:函数对象()
当调用函数时,函数中封装的代码会按照顺序执行
var fun = new Function(“consloe.log(‘这是第一个函数代码’)”)
fun();
2、使用函数声明来创建一个函数
语法:形参外的中括号 [ ] 代表可选,写不写都可以
3、使用 函数表达式 来创建一个函数 (创建一个匿名函数,然后将匿名函数的对象赋值给一个变量)最好在大括号后添加分号(;)
函数的参数
可以在函数的()中来指定一个或多个形参(形式参数),多个形参间使用逗号(,)隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值
在调用函数时,可以在()中知道实参(实际参数),实参将会赋值给函数对应的形参
调用函数时解析器不会检查实参的类型,所以要主要,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型检查,函数的实参可以是任意的数据类型
调用函数时解析器也不会检查实参的数量,,多余的实参不会被赋值,如果实参的数量少于形参的数量,则没有对于实参的形参将是undefined
返回值 可以使用return来设置函数的返回值
语法:return 值 ;
return后的值将会作为函数的执行结果返回,可以定义一个变量来接受该结果
在函数中return后的语句都不执行了
如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,也会返回undefined
return后可以跟任意类型的值
function sum(a,b,c){
var d= a + b + c ;
return d;
}
//调用函数,变量result的值就是函数的执行结果,函数返回什么result的值就是什么
var result = sum(1,2,3); //reuslt=19;
alert("hello"); //不执行
var b=+prompt("输入一个数:");
function A(a){
return (b %2 ==0); //用if语句返回的也是true或者false,所以这样更简单
}
var result= A(b);
console.log("result="+result);
</script>
var r=+prompt("输入一个数:");
function square(r){
return 3.14*r*r;
}
var result=square(r);
console.log("result="+result);
实参可以是任意的数据类型,也可以是一个对象,当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递
var obj ={
name:"孙悟空",
age:18,
gender:"男",
address :"花果山"
};
function sayHello(o){
console.log("我是"+o.name+",今年我"+o.age+"岁了,我是一个"+o.gender+"人,我住在"+o.address)
}
sayHello(obj);
实参可以是一个对象,也可以是一个函数(第54课)
fun(sayHello) //sayHello是函数对象,相当于直接使用函数对象
fun(sayHello()) //sayHello()调用函数,相当于使用函数的返回值
var obj = {
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
};
function sayHello(o) {
console.log("我是" + o.name + ",今年我" + o.age + "岁了,我是一个" + o.gender + "人,我住在" + o.address)
}
sayHello(obj);
function fun(a) {
console.log("a=" + a);
a(obj); //输出我是孙悟空,今年我18岁了...
}
fun(sayHello);
break可以退出当前的循环
continue用于跳过当次循环
return可以结束整个函数
返回值可以是任意的数据类型,也可以是一个对象,也可以是一个函数
函数名+() 返回的是这个函数的返回值
函数名 返回的是这个函数
第54、55可以多看几次
function fun3(){
//在函数内部再声明一个函数
function fun4(){
alert("我是fun4");
}
fun4();
}
fun3(); //相当于调用fun4,弹出alert
// 2
function fun3(){
function fun4(){
alert("我是fun4");
}
return fun4; //将fun4函数对象作为返回值返回
}
a = fun3(); //将fun3的返回值,也就是fun4赋给a
console.log(a); //输出fun4整个函数
a() //a是fun4函数,加上括号调用fun4函数,弹出alert
fun3() () //fun3()的返回值为 fun4,再加() 也就是调用fun4
// 3
function fun3(){
function fun4(){
alert("我是fun4");
}
return fun4(); //将执行fun4 作为fun3的返回值返回
}
a = fun3(); // 弹出alert
console.log(a); //fun4没有返回值,所以输出undefined
立即执行函数函数定义完,立即被调用。往往只会执行一次,因为没有变量保存
function(){
alert("我是一个匿名函数");
} //这样声明一个匿名函数会报错,因为电脑会将大括号当作一个代码块,前面的function()会报错
(function(){
alert("我是一个匿名函数");
}) //加一个括号表示它们是一个整体
//调用函数:函数对象 ()
//用括号套住的function就是一个函数对象,只不过没有用变量接收
(function(){
alert("我是一个匿名函数");
}) (); //可以直接加()执行此函数
对象的属性值可以是任何的数据类型,也可以是一个函数
函数也可以成为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用函数就称为调用对象的方法(method)
但是它只是名称上的区别,没有其他区别
obj.sayName(); //调方法
fun(); //调函数
枚举对象中的属性使用for …in 语句
语法:
for…in语句 对象中有几个属性,循环体就会执行几次,每次执行时会将对象中的一个属性的名字赋值给变量
var obj = {
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
};
for(var n in obj){
console.log("hello"); //输出4次hello
console.log(n); //输出name age gender address
console.log(obj.n); //输出4个undefined,因为obj中没有n属性,n是一个变量
console.log(obj[n]); //输出孙悟空 18 男 花果山
}
作用域(Scope) 指一个变量的作用范围
在JS中一共有两种作用域:
1、全局作用域
直接编写在script标签中的JS代码,都在全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁
在全局作用域中有一个全局对象window(代表浏览器的窗口,它由浏览器创建),我们可以直接使用
在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
全局作用域中的变量都是全局变量,在页面的任意部分都可以访问到
var a= 10;
console.log(window.a); //输出 10
console.log(c); //报错,变量c不存在
console.log(window.c); //输出undefined(找对象的属性,没有会输出undefiend)
2、函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
当在函数作用域中操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有就向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则报错ReferenceError
在函数中要访问全局变量可以使用window对象
在函数作用域中也有声明提前的特性,使用var关键字声明的变量,会在函数中所有的代码执行之前被声明。函数声明也会在函数中所有的代码执行之前执行
在函数中,不使用var声明的变量都会成为全局变量
定义形参就相当于在函数作用域中声明了变量
var c=33;
function fun5(){
console.log("c="+c); //弹出undefined(如果将fun5声明c的var去掉,则会输出33)
var c=10;
}
fun5();
var c=33;
function fun5(){
console.log("c="+c); //输出33
c=10; //没有用var声明,为全局变量
}
fun5();
console.log("c="+c); //输出10
var e =23;
function fun6(e){
alert(e); //弹出undefined,相当于在函数中声明了var e但是未定义
}
fun6();
变量的声明提前
使用var关键字声明的变量,会在所有的代码执行之前被声明(但此时不会赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前
console.log("a="+a); //输出a=undefined,a在最开始别声明,但在此代码后被定义
console.log("b="+b); //报错
var a =123;
b = 456;
函数的声明提前
使用函数声明形式创建的函数function 函数名(){},它会在所有的代码执行之前就被创建,所以可以在函数声明前调用函数
使用函数表达式创建的函数,函数不会被声明提前(被声明提前的只有变量,故fun2是undefined)
//函数声明,会被提前创建
fun(); //执行fun
function fun(){
console.log("我是一个fun函数");
}
//函数表达式,不会被提前创建
fun2(); //报错,undefined is not a function
var fun2 = function(){
console.log("我是一个fun2函数");
};
123 456
undefined 456 //错误 答案:undefined 123。function中有a,所以a=456是将456赋值给局部变量a,不影响全局变量
123 123
debug 第60课
This 解析器在调用函数时,每次都会向函数内部传递一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象
根据函数的调用方式不同,this会指向不同的对象
1、以函数的形式调用时,this永远都是window
2、以方法的形式调用时,this就是调用方法的那个对象
function fun(){
console.log(this); //输出boject window
}
fun();
function fun(){
console.log(this); //输出boject object,第二个object就是obj
}
var obj ={
name:"孙悟空";
sayName:fun;
};
console.log(obj.sayName == fun); //输出true
obj.sayName();
function fun(){
console.log(this.name);
}
var obj ={
name:"孙悟空";
sayName:fun;
};
var name = "全局的变量";
fun(); //输出”全局的变量“。该句等价window.fun();以函数形式调用,this是window
obj.sayName(); //输出"孙悟空"。以方法形式调用,this是调用方法的对象
谁调用fun()输出谁的名字,所以需要用this
var name = "全局的变量";
function fun(){
console.log(name);
}
var obj ={
name:"孙悟空";
sayName:fun;
};
fun(); //
obj.sayName(); //输出"全局的变量",因为fun中name被写死
使用工厂方式创建对象,通过该方法可以大批量的创建对象
使用工厂方法创建对象,使用的构造函数都是Object,所以创建的对象都是Object这个类型,导致我们无法区分多种不同类型的对象
function createPerson(name,age,gender){
var obj = new Object();
obj.name=name;
obj.age=age;
obj.gender=gender;
obj.sayName=function(){
alert(this.name);
};
return obj;
}
var obj2=createPerson("猪八戒",28,"男");
var obj3=createPerson("沙和尚",28,"男");
var obj4=createPerson("白骨精",16,"女");
obj3.sayName(); //弹出沙和尚
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写
构造函数和普通函数的区别就是调用方式不同,普通函数是直接调用,而构造函数需要使用new关键字来调用
构造函数的执行流程:
1、立刻创建一个新的对象
2、将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
3、逐行执行函数中的代码
4、将新建的对象作为返回值返回
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类
我们将通过一个构造函数创建对象,称为是该类的实例
this的情况
1、当以函数的形式调用时,this是window
2、当以方法的形式调用时,谁调用方法this就是谁
3、当以构造函数的形式调用时,this就是新创建的那个对象
使用instanceof可以检查一个对象是否是一个类的实例
语法: 对象 instanceof 构造函数
如果是,则返回true,否则返回false
所有的对象都是Object的后代,所以任何对象和Object做instanceof检查时都会返回true
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
this.sayName=function(){
alert(this.name);
};
}
var per2=new Person("猪八戒",28,"男");
var per3=new Person("沙和尚",28,"男");
var per4=new Person("白骨精",16,"女");
per2.sayName();
consloe.log(per2 instanceof Person); //输出true
consloe.log(per2 instanceof Object); //输出true
构造函数修改
在Person构造函数中,为每一个对象都添加了一个sayName方法,目前我们的方法是在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法,也就是所有实例的sayName都是唯一的。
执行10000次就会创建10000个新方法,而10000个方法都是一模一样的,这是完全没有必要的,可以使所有的对象共享同一个方法
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
this.sayName=fun;
}
//将sayName方法在全局作用域中定义
function fun(){
alert(this.name);
};
var per2=new Person("猪八戒",28,"男");
var per3=new Person("沙和尚",28,"男");
console.log(per2.sayName == per3.sayName); //输出false
将函数定义在全局作用域,污染了全局作用域的命名空间,而且定义在全局作用域中也很不安全
原型prototype
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象
如果函数作为普通函数调用,prototype没有任何作用
当函数通过构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性
原型对象就相当于一个公共的区域,所有同一类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中
function Myclass() {
}
function Person() {
}
var mc = new Myclass();
var mc2 = new Myclass();
var per = new Person();
console.log(Myclass.prototype); //输出object(prototype存在)
console.log(Myclass.prototype == Person.prototype); //输出false(不同类prototype并不相同)
console.log(mc.__proto__ == Myclass.prototype); //输出true (类与该类的实例prototype相同)
console.log(mc.__proto__ == mc2.__proto__); //输出true(同一个类中不同实例的prototype相同)
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用
以后我们创建构造函数时,可以将这些对象共有的属性和方法统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了
function Myclass() {
}
//向Myclass的原型中添加属性a
Myclass.prototype.a=123;
var mc = new Myclass();
var mc2 = new Myclass();
console.log(mc.a); //输出123
//向mc中添加a属性
mc.a="我是mc中的a" ;
console.log(mc.a); //输出"我是mc中的a"
console.log(mc.a); //输出123
//向Myclass的原型中添加一个方法
Myclass.prototype.sayHello = function(){
alert("hello");
};
mc.sayHello(); //输出hello
构造函数修改方法调用最终版(将方法写入prototype中)
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
//将sayName方法在全局作用域中定义
Person.prototype.sayName=function (){
alert(this.name);
};
var per2=new Person("猪八戒",28,"男");
var per3=new Person("沙和尚",28,"男");
console.log(per2.sayName());
使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
可以使用对象的hasOwnProperty()来坚持对象自身中是否含有该属性。使用该方法只有当对象自身中含有属性时,才会返回true
原型对象也是对象,所以它也有原型
当我们使用一个对象的属性或方法时,会先在自身中寻找,自身中如果有,则直接使用
如果没有则去原型对象中寻找,如果原型对象中有,则使用,
如果没有则去原型的原型中寻找,直到找到Object对象的原型
Object对象的原型没有原型,如果在Object中依然没有找到,则返回undefined
function Myclass() {
}
Myclass.prototype.name = "我是原型中的名字";
var mc = new Myclass();
//使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log("name" in mc); //输出true
console.log(mc.hasOwnProperty("name")); //输出false
console.log(mc.hasOwnProperty("hasOwnProperty")); //输出false。mc中无该属性
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty")); //输出false。原型里也无该属性
console.log(mc.__proto__.__proto__); //输出object。说明原型的原型存在
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //输出true。说明hasOwnProperty是原型的原型的属性
console.log(mc.__proto__.__proto__.__proto__); //输出null
当我们直接在页面中打印一个对象时,实际上输出的是对象的toString()方法的返回值
如果我们希望在输出对象时不输出[ object object ],可以为对象添加一个toString()方法
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
var per = new Person("孙悟空",18,"男");
console.log(per); //输出object
console.log("result="+result); //输出result=object。为toString的返回值
console.log(per.hasOwnProperty("toString")); //输出false
console.log(per.__proto__.hasOwnProperty("toString")); //输出false
console.log(per.__proto__.__proto__.hasOwnProperty("toString")); //输出true
//为对象添加toString()方法
per.toString = function(){
renturn "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
}
console.log(per); //输出"Person[name= ,age= ,gender= ]"
var per2 =new Person("猪八戒",28,"男");
//console.log(per2)输出的依旧是object,所以需要在原型中创建toString()方法
Person.prototype.toString = function(){
renturn "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
}
垃圾回收程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以我们需要一个垃圾回收机制,来处理程序运行过程中产生的垃圾
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,过多会占用大量内存空间,导致程序运行变慢,所以这种垃圾必须进行清理
在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收操作
我们需要做的只是将不再使用的对象设置null即可
obj = null;