JavaScript学习笔记5
函数
- 函数是一个对象
- 函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码)
- 用typeof检查一个函数对象时,返回的是function
创建函数对象
使用构造函数创建函数对象
- 可以将要封装的代码以字符串的形式传递给构造函数
- 封装到函数中的代码不会立即执行
- 函数中的代码会在函数调用的时候执行
var fun =new Function("console.log('hello,this is my first function'));
console.log(typeof fun);
使用函数声明创建函数对象
语法:
funciton 函数名([形参1,形参2,形参n]){
功能(代码)
}
使用函数表达式创建一个函数
var 函数名=function([形参1,形参2,形参n]){
语句
};//最后最好写分号
创建匿名函数
- 立即执行函数,一旦创建,立即执行
语法:
{ function([形参]){
语句
}
}([实参]);
匿名函数的外边得加{},不然会报错
调用函数
- 调用函数语法:函数名()
- 调用函数后,函数中封装的代码会按照顺序执行
fun();
参数
形参
- 在函数名()的()内来指定形参
- 多个形参使用逗号隔开,声明形参相当于在函数内部声明了对应的变量
实参
- 调用函数时,可以在()中指定实参
- 实参将会赋值给函数中对应的形参
- 函数的实参可以是任意数据类型
- 对象、函数也可以作为参数(函数也是一个对象,对象能做的事情,函数都能做) - 调用时不会检查实参的类型
所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查 - 调用函数时解析器也不会检查实参的数量
多余的实参不会被赋值
如果实参数量少于形参数量,则没有对应实参的形参将是undefined
/*
定义一个用来求两个数之和的函数
*/
function sum(a,b){
console.log(a+b);
}
sum(2,3);
多个参数
当参数过多时,可以将参数封装到一个对象中,然后通过对象传递
function sayhello(o){
console.log("my name is "+o.name+", i am "+o.age+"years old");
}
var obj={
name:"sun ",
age:1
};
sayhello(obj);
返回值
- 语法: return 值 ;
- 用一个变量去接收返回值:var 变量名=函数名();
返回值是什么,变量就是什么 - 如果return语句后不跟任何值就相当于返回一个undefined,如果函数不写return,则也会返回undefined
- return后可以跟任意类型的值,也可以是一个对象,也可以是一个函数
function sum(a,b,c){
return a+b+c;
}
函数对象和调用函数的区别
函数名()//例:fun()
- 调用函数
- 相当于使用的函数的返回值
函数名 //例: fun
- 函数对象
- 相当于直接使用函数对象
*对象的补充知识点
对象的属性值可以是任何数据类型,也可以是函数。
函数也可以成为对象的属性
- 如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法
- 调用函数就说明调用对象的方法(method)
function fun(a,b){
console.log(a+" + "+b+" = "+(a+b));
return a+b;
}
var obj=new Object();
obj.fun1=fun;
obj.fun2=function(a){
console.log("hello"+a);
};
console.log(obj.fun1);//输出的是函数对象
console.log(obj.fun1(1,2));//调用了函数,返回了和
console.log(obj.fun2);//输出的是函数对象
console.log(obj.fun2("js"));//调用了函数,返回了undefined
但是它只是名称上的区别,没其他的区别。
- 调方法
obj.fun1(2,3); - 调函数
fun(3,4);
枚举对象中的属性和属性值
使用 for … in 语句
- 语法: for(var 变量 in 对象){}
- 对象中有几个属性,循环体就会执行几次
- 循环每次执行时,会将对象中的一个属性的名字赋值给变量
- 可以使用 对象名[变量] 的方式提取对象属性的属性值
*作用域
- 作用域指一个变量的作用的范围
- 在js中一共有两种作用域
1、全局作用域
- 直接编写在script标签中的js代码,都在全局作用域
- 全局作用域在页面打开时创建,在页面关闭是销毁
- 在全局作用于中有个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用
- 在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
var a=10;
console.log(window.a);
console.log(window.c);//不会报错,输出undefined
2、函数作用域
- 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
- 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
- 在函数作用域中可以访问到全局作用域的变量
在全局作用域中无法访问到函数作用域的变量 - 当在函数作用域操作变量时,它会在自身作用域中寻找
如果有就直接使用,没有就在上一级的作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,就会报错(ReferenceError) - 在函数中如果想要直接访问全局变量中的变量,可以使用(window.变量名)
- 在函数作用域中也有声明提前的特性
使用var关键字声明的变量,会在函数中素有的代码执行之前被声明
函数声明也会在函数中所有代码执行之前被创建- 在函数中,不使用var声明的变量都会成为全局变量(变量声明时,可以不用var关键字,但是得在使用之前赋值)
变量声明提前&&函数声明提前
变量声明提前
- 使用var关键字声明的变量,会在所有的代码执行之前被声明,但不会被赋值
- 但是如果声明变量不使用var关键字,则变量不会被声明提前
函数声明会被提前
- 使用函数声明形式创建的函数function函数(){}
- 它会在所有的代码执行之前就被创建(不止声明提前,而是函数就被创建好了)
- 所以可以在函数声明之前调用函数
函数表达式不会被提前
- 使用函数表达式形式创建的函数,不会被声明提前,所以不能再声明前调用
fun();//会正常调用函数
fun2();//会报错
function fun(){
console.log("我是fun函数");
}
var fun2=function(){
console.log("我是fun2函数");
}
this
- 解析器在调用函数时,每次都会向函数内部传递一个隐含的参数,这个隐含的参数就是this
- this指向的是对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象
- 1、this以函数调用时,this是window(构造函数除外)
- 2、this以方法调用时,this是调用方法的对象
(说白了就是最终调用的函数/方法的上一级) - 3、当以构造函数的形式调用时,this就是新创建的那个对象
function fun(){
console.log(this);
}
var obj1 ={
name:"sun",
fun2:fun,
obj2:{
age:1,
fun2:fun
}
};
obj1.obj2.fun2();//obj2
obj1.fun2();//obj1
fun();//fun()相当于window.fun(),所以this指向window
使用工厂方法创建对象
使用工厂方法的创建的对象,使用的构造函数都是Object,所以创建的对象都是Object这个类型,就导致我们无法区分出多种不同类型的对象
function createPerson(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.sayName=function(){
alert(this.name)
};
return obj;
}
var obj1=createPerson("sun",18);
var obj2=createPerson("he",1);
创建构造函数
- 构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写
- 构造函数和普通函数的区别就是调用方法的不同,普通函数就是直接调用,而构造函数需要使用new关键字来调用
- 如果构造函数里写了return语句,如果return的是一个对象,则创建的就是这个对象,如果返回的是其他数据类型,则创建的就是构造函数它本身(也是个对象,即实例)
构造函数的执行流程:
1.立即创建一个新的对象
2.将新建的对象设置为函数中的this,在构造函数中,可以使用this来引用新建的对象
3.逐行执行函数中的代码
4.将新建的对象作为返回值返回
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类,我们将通过一个构造函数创建的对象,称为该类的实例
使用instanceof可以检查一个对象是否是一个类的实例
- 语法:对象 instanceof 构造函数
如果是,返回true,否则返回false - 例子: obj1 instanceof Person
- 所有的对象都是object的后代,所以不管那个对象instanceof Object,都是true
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
alert(this.name);
}
}
var obj1=new Person("sun",18);
var obj2=new Person("he",1);
创建一个Person构造函数
- 在[Person构造函数中,为每一个对象都添加了一个sayName方法
目前我们的方法是在构造函数内部创建的,也就是说构造函数每执行一次都会创建一个新的sayName方法
也就是说所有实例的sayName都是唯一的 - 这样就导致了构造函数执行一次就会创建一个新的方法
即执行10000次就会创建10000个新的方法,而10000个方法都是一模一样的,这样是完全没有必要的,完全可以使素有的对象共享一个方法
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=fun;
}
function fun(){
alert(this.name);//这里还可以用this,
}
var obj1=new Person("sun",18);
var obj2=new Person("he",1);
但是将函数定义在全局作用域中,污染了全局作用域的命名空间
而且定义在全局作用域中很不安全(很容易和其他程序员写的函数重名)
原型prototype
- 我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象 - 如果函数作为普通函数调用prototype没有任何作用
- 当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过_proto_来访问该属性
- 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中有的内容,统一设置到原型对象中
语法:函数名.prototype.属性名=属性值; - 当我们访问对象的一个对象或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用
语法:对象名.属性名[()]; - 以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样就不用分别为每个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了
- 使用 in 检查对象中是否含有原型中的属性时,会返回true
- 可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
使用该方法只有当对象自身中含有属性时,才有返回true - 原型对象也是对象,所以它也有原型
当我们使用一个对象的属性或者方法时,会先在自身中寻找
-自身中如果有,则直接使用
-自身中如果没有,则去原型对象(Myclass._ proto )中寻找
-如果原型对象中没有,则去原型的原型(Myclass. proto . proto )中寻找,直到找到Object对象(Object对象就是原型的原型)的原型(Myclass. proto . proto . proto _),Object对象的原型没有原型(值为null),如果在Object中依然没有找到,则返回undefined - 使用in检查对象是否包含属性/方法(包括检查对象的原型中属性/方法,还有对象的原型的原型中的属性/方法)
- 使用对象的hasOwnProperty(“属性名/方法名”)来检查当下自身中是否含有该属性/方法,有返回true,没有返回false
例子:
function MyClass(name){
this.name=name;
}
//想MyClass的原型中添加属性a
MyClass.prototype.a=123;
//向MyClass的原型中添加一个方法
MyClass.prototype.sayHello=function(){
console.log("名字:"+this.name);
}
var mc=new MyClass("sun");
var mc2=new MyClass("he");
console.log(mc._proto_==MyClass.prototype);//true
console.log(mc2._proto_==MyClass.prototype);//true
//调用原型中的函数
mc.sayHello();
mc2.sayHello();
//in检查对象是否包含属性
console.log("sayHello" in mc);
//使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
console.log(mc.hasOwnProperty("name"));//true
console.log(mc.hasOwnProperty("sayHello"));//false
console.log(mc.hasOwnProperty("age"));//false
console.log(mc._proto_.hasOwnProperty("age"));//false
toString()
- 当我们直接在页面打印一个对象时,事实上输出的是对象的toString()方法的返回值
- 对象的toString方法在object里(即对象的原型的原型里)
- 如果我们希望在输出对象时不输出[object object],可以为对象添加一个toString()方法
-对象名.prototype.toString=function(){语句…}; //写在对象原型里
垃圾回收(GC)
- 就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾
-这些垃圾积攒过多以后,会导致程序运行的速度过慢,
-所以我们需要一个垃圾回收机制,来处理程序运行过程中所产生的垃圾 - 当一个对象没有任何的变量或者属性对它进行引用时,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象会过多占用大量的内存空间,所以得这种垃圾必须进行清理
- 在js中拥有自动的垃圾回收机制,会自动将这种垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作
- 我们需要做的只是要将不再使用的对象设置为null