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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值