要用面向对象的方式去编程,而不要用面向过程的方式去编程css
对象是各类类型的数据的集合,能够是数字、字符串、数组、函数、对象……html
对象中的内容以键值对方式进行存储jquery
对象要赋值给一个变量编程
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵妈"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } }
上面是对象的字面量的建立方式,简单直接数组
除此以外,还有对象的构造函数的建立方式浏览器
var cat=new Object();
还有JavaScript5新增的一种方式app
该方式在老版本的浏览器中存在兼容性问题函数
Object.create();
获取、设置、添加、修改对象的值:this
对象.属性名=属性值spa
对象[ 属性名 ]=属性值
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵妈"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } cat.name="喵喵1"; cat.age=6; cat.type="英短"; console.log(cat.name);//喵喵1 console.log(cat["age"]);//6 console.log(cat.type);//英短
删除对象的属性:
delete 对象.属性
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵妈"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } cat.type="英短"; console.log(cat.type);//英短 delete cat.type; console.log(cat.type);//undefined
检测对象是否拥有某个属性:
属性名 in 对象
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵妈"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } console.log("name" in cat);//true console.log("type" in cat);//false
对象的枚举,遍历对象中的各个属性
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵妈"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } for(var p in cat){ console.log(p); //name age family speak friend }
var cat={ "name":"喵1", "age":4, "family":["喵爸","喵妈"], "speak":function(){ console.log("喵喵~"); }, "friend":{ "name":"喵2", "age":5 } } for(var p in cat){ console.log(p+": "+cat[p]); console.log(p);//获取属性名 //console.log(cat.p);// 写法错误 console.log(cat[p]);//获取属性值 写法正确 console.log(cat["n"+"ame"]);//喵1 []中能够添加字符串的拼接等操做 }
匿名函数,如:
window.onload=function(){ }
函数一次执行完毕以后,会将局部做用域和局部变量销毁,所以外部没法调用到
但函数自己并无被销毁,能够进行屡次调用执行
为何要使用函数:
代码复用(本身的代码和别人的代码,如jquery)
统一修改和维护
增长程序的可读性
函数的本质:对象
定义方式:字面量定义、构造函数定义
//字面量定义 function add(n1,n2){ } //构造函数定义 new Function("n1","n2","....");
函数和对象同样,能够添加属性和方法
function person(){ console.log("cyy"); } //添加属性 person.age=25; //添加方法 person.speak=function(words){ console.log(words); } console.log(person.age);//25 person.speak("hh~");//hh~ person();//cyy
函数能够做为数据值使用:
做为数据值保存在一个变量中
var fn=function(){ return "这是一个函数"; } console.log(fn());//这是一个函数 console.log(fn); /* ƒ (){ return "这是一个函数"; } */
此时fn打印出来的就是函数本体
函数也能够做为参数来使用:
function fn(){ alert(1); } setTimeout(fn,1000);//此处须要传函数本体 //此处不能加括号,若是加了括号,会马上调用,而不是等到1秒以后
函数能够做为返回值使用:
function fn(){ return function(){ console.log("fn中的fn"); } } //调用 var newFn=fn(); newFn();//fn中的fn // 或者 fn()();//fn中的fn
函数的三种定义方式
// 字面量方式 // function 声明 function add(){ } // var 赋值表达式 var add=function(){ }; //构造函数 var add=new Function("num1","num2","return num1+num2"); add();
区别:
字面量方式比构造函数方式简洁
最重要的是预解析的区别
funtion声明的函数,能够先调用,再建立
函数预解析的时候会提早定义
add(); function add(){ return 1; }
用var赋值表达式建立的函数,若是先调用,再建立,会报错
由于var在预解析时赋值为undefined
add(); var add=function(){ return 1; };
function声明和var赋值表达式声明,这两种都是很好的选择
构造函数过于复杂,不推荐使用
函数定义的位置
全局做用域下的函数,在哪里都能调用
add(); function add(){ add(); } add(); function fn(){ add(); }
局部做用域下的函数
//fn(); 没法调用 function add(){ fn(); function fn(){ fn(); function fn3(){ fn(); } } function fn2(){ fn(); } } //fn(); 没法调用
里层能够访问外层的函数,外层不能访问里层的函数
代码块中定义的函数:
因为js中没有块级做用域,因此依然是处于全局做用域中
都会出现预解析中函数被提早声明
if(true){ function fn1(){ } }else{ function fn2(){ } }
改为下面这样就能够实现按照条件进行声明,也是由于预解析的机制
if(true){ var fn1=function (){ } }else{ var fn2=function fn2(){ } }
对象中的函数
使用对象.函数名进行调用
var person={ name:"cyy", setAge:function(age){ this.age=age;//this指向当前对象 } } person.setSex=function(sex){ this.sex=sex; } person.setAge(25); person.setSex("girl"); console.log(person.age);//25 console.log(person.sex);//girl
普通函数的调用:
命名函数的调用
function add(){ } add();
匿名函数的调用:
若是直接在匿名函数后面加上括号进行调用,会报错
function(){ alert(1); }();//Uncaught SyntaxError: Unexpected token (
解决方法是,将这段匿名函数执行的代码,赋值给一个变量
var fn=function(){ alert(1); }();//1
第二种解决方法:
将函数用括号括起来,实现匿名函数自执行
(function(){ alert(1); })();//1
括号把总体括起来也能实现同样的效果
(function(){ alert(1); }());//1
或者在function前加上合法的字符也能够,如!+-~
!function(){ alert(1); }();//1
或者放在console.log里面
console.log(function(){ alert(1); }());
以上这些方式的共同目的,就是不让匿名函数的function在开头位置出现
递归调用:
本身调用本身
实现阶乘
function fn(num){ if(num<=1) return 1; return num*fn(num-1); } console.log(fn(5)); /* return 5*fn(4) return 5*4*fn(3) return 5*4*3*fn(2) return 5*4*3*2*fn(1) return 5*4*3*2*1 */
匿名函数也是函数,当它自执行的时候,会建立本身的函数内部做用域,在执行完毕以后会被销毁,所以在外部没法访问到自执行的匿名函数内部
//此处建立函数内部做用域 (function add(n1,n2){ return n1+n2; })(); console.log(add(3,4));//在全局没法访问到函数内部的函数add
方法的调用:
对象中的方法,使用对象.方法名进行调用
var operation={ add:function(n1,n2){ return n1+n2; }, sub:function(n1,n2){ return n1-n2; } } console.log(operation.add(3,4));//7
如下这种也是方法,是点击浏览器时浏览器自动帮咱们完成调用;
也可使用方法调用的方式来进行调用
document.onclick=function(){ alert(1); } document.onclick();//等同于点击屏幕的效果
关于对象中的属性,何时加引号,何时不加引号
对于合法的标识符,加不加引号均可以;
不合法的标识符,必须加引号,不然会引发报错
var operation={ add:function(n1,n2){ return n1+n2; },//合法的属性名能够不加引号 sub:function(n1,n2){ return n1-n2; }, "@":function(){ }//不合法的属性名,会引发报错,必须加引号 }
合法的标识符,调用时使用对象.方法名便可
非法的标识符,调用时使用对象[ " 方法名 " ]
var operation={ add:function(n1,n2){ return n1+n2; },//合法的属性名能够不加引号 sub:function(n1,n2){ return n1-n2; }, "@":function(word){ alert(word); }//不合法的属性名,会引发报错,必须加引号 } console.log(operation.add(2,5));//7 console.log(operation["@"]("hh~"));//hh~
[ ] 加引号和不加引号的区别
var operation={ add:function(n1,n2){ return n1+n2; },//合法的属性名能够不加引号 sub:function(n1,n2){ return n1-n2; }, "@":function(word){ return word; },//不合法的属性名,会引发报错,必须加引号 key:function(n1,n2){ return "key~"; } } var key="add"; console.log(operation.key(2,3));//key~ console.log(operation["key"](2,3));//key~ console.log(operation[key](2,3));//5
方法的链式调用
如jquery
$("p").html("html").css("color","red")....
对象中要使用链式调用,则方法中须要返回当前对象
var operation={ add:function(n1,n2){ console.log(n1+n2); return this; }, sub:function(n1,n2){ console.log(n1-n2); return this; } } operation.add(5,3).sub(4,2); //要保证operation.add(5,3)可以返回operation对象 //就须要添加return this
构造函数的调用:
构造函数命名时通常首字母大写
调用时用new+函数名,返回值是一个对象
function Person(){ } var obj=new Person();
js中内置的构造函数,常见的有:
Object() new Object() Array() new Array()
经过new关键字来调用
用构造函数的方式定义对象和数组,并添加内容
var person=new Object(); person.name="cyy"; var arr=new Array(); arr[0]=1;
函数的间接调用
.call 第一个参数是改变this的指向,后面传递参数的方式就是一个一个传
.apply 第一个参数是改变this的指向,后面传递参数的方式是经过数组来传递(或者类数组)
var name="cyy"; var person={}; person.name="cyy2"; person.getName=function(){ return this.name;//此处的this指向person对象 } console.log(person.getName());//直接调用 cyy2 console.log(person.getName.call(window));//间接调用,此时this被指向了window,返回的是window.name cyy console.log(person.getName.apply(window));//间接调用 cyy
function add(n1,n2){ return n1+n2; } console.log(add(1,2));//直接调用 3 console.log(add.call(window,1,2));//间接调用 3 console.log(add.apply(window,[1,2]));//间接调用 3
function add(n1,n2){ return n1+n2; } var arr=[4,6]; console.log(add.apply(window,arr));//10
只有函数拥有call和apply方法,二者惟一的区别在于它们的传参方式
函数的参数
参数传递的本质是将实参赋值给形参
参数的个数
一、形参个数=实参个数
function add(n1,n2){ return n1+n2; } console.log(add(3,5));
二、实参个数 < 形参个数
多用于有可选参数的状况
function pow(base,pow=2){ return Math.pow(base, pow); } console.log(pow(3));//9 console.log(pow(3,3));//27
三、实参个数 > 形参个数
若是没法得知有多少个实参,可使用arguments
arguments是一个类数组,用于保存实参的信息
经过arguments[index] 获取某一个参数
arguments.length 实参的个数
function add(){ if(arguments.length==0) return; var sum=0; for(var i=0,len=arguments.length;i<len;i++){ sum+=arguments[i]; } return sum; } console.log(add());//undefined console.log(add(1,2,3,4,5));//15
arguments 是类数组,实质上仍是对象
索引是数组下标,数字开头的变量名不合法,所以须要加引号
{ '0': 1, '1': 2, '3': 4, length: 3 }
能够经过arguments来修改参数的值
function speak(m){ arguments[0]=""; return m; } console.log(speak("hh"));//空
arguments是每一个函数中独有的,不会跨函数
function fn1(){ console.log(arguments);//Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ] function fn2(){ console.log(arguments);//Arguments [2, callee: ƒ, Symbol(Symbol.iterator): ƒ] } fn2(2); } fn1(1);
arguments.callee 指代函数自己
function add(){ console.log(arguments.callee); } add();
arguments.callee 经常使用于递归中
function factorial(num){ if(num==1) return 1; return num*factorial(num-1); } console.log(factorial(5));//120 function factorial(num){ if(num==1) return 1; return num*arguments.callee(num-1); } console.log(factorial(5));//120
不过在严格模式下,不容许使用arguments.callee(也不容许不使用var声明变量)
此时的解决方法就是将函数赋值给一个变量,这样函数自己的名字不会影响调用
"use strict"; var myfn=function factorial(num){ if(num==1) return 1; return num*factorial(num-1); } console.log(myfn(5));//120
实参的个数 arguments.length
形参的个数 函数名.length 或者arguments.callee.length
function add(n1,n2){ if(arguments.length != add.length) throw new Error("请传入"+add.length+"个参数!"); } console.log(add(5));//Uncaught Error: 请传入2个参数!
什么作参数
一、没有参数
二、数字作参数
三、字符串( 如选择DOM节点,$("p") )
四、布尔值(保持函数的纯洁性,建议一个函数只作一件事情)
五、undefined(可选参数必须放在最后)
六、null
七、数组
$.each(["a","b","c"],function(index,item)){ console.log(index);//0 1 2 console.log(item);//a b c }
八、对象
$.each({name:"cyy",age:24},function(index,item)){ console.log(index);//name age console.log(item);//cyy 24 }
使用对象做为参数的好处(能够自由调换顺序)
function fn(obj){ var person={}; person.name=obj.name||"cyy"; person.age=obj.age||24; person.tel=obj.tel||110, person.addr=obj.addr||"China"; return person; } var cyy={ name: "cyy1", age:25 } console.log(fn(cyy));//{name: "cyy1", age: 25, tel: 110, addr: "China"}
九、函数
回调函数,如 setTimeout(fn, time);
函数的返回值
return:
表示函数结束
将值返回
什么能够作返回值:
直接return ,返回值是undefined
数字
字符串 :alert() 输出的都是字符串,会默认调用.toString() 方法
布尔值:经常使用于表单验证
null 和 undefined
数组
function add(n1,n2){ return [n1,n2,n1+n2]; } console.log(add(5,6));//(3) [5, 6, 11]
对象
function fn(){ return { name:"cyy", age:25 } }
注意return后面不要换行,不然默认是分号,到此结束;因而后面的会报错
function fn(){ //return会默认后面是分号,结束 return { name:"cyy", age:25 } }
函数
须要用()()来调用
document.write() 执行时会调用.toString() 方法,尝试将结果转换为字符串形式
document.write([1,2,3]);//1,2,3 document.write({ name:"cyy" });//[object Object] document.write({ name:"cyy", toString:function(){ return "hh~"; } });//hh~
function count(){ var num=1; return function(){ return num++; } } //每次调用count()时,num都会被初始化 //而且return num++ 是先返回num,再执行++ console.log(count()());//1 console.log(count()());//1 console.log(count()());//1 //count()只执行了一次,所以num只初始化一次 //后面可以每次都进行递增+1 var fn=count(); console.log(fn());//1 console.log(fn());//2 console.log(fn());//3