JavaScript_函数

JavaScript_函数

函数是一段可以复用的代码,可以独立独立存在,可以当作一个类使用,同时函数也是一个对象,本身是Function实例

函数定义方法

方法1:命名函数

function 函数名称(形参列表){
    
    代码块;} 

基本编程实现

<script>
	//注意: js是弱类型编程语言,所以参数上没有类型,也没有返回值类型
	function sayHello(name){
    
    
		//修改<div id="div1">中的显示内容
		var div1=document.getElementById("div1");
		div1.innerHTML="你好,<strong>"+name+"</strong>!";
		//函数可以有返回值,也可以没有
		return 100;//如果没有返回值,则没有【return 值】的语句,或者【return;】
	}
</script>

<div id="div1">原始显示内容</div>
<!-- 定义一个点击事件触发函数执行,函数不调用则不会执行。onclick属性值为调用函数的语句 -->
<button onclick="sayHello('yanjun')">点击</button>

方法2:匿名函数 function(形参){代码块;} ,另外特殊的还有ES6提供的箭头函数

<script>
	var f=function(name){
    
    
		var div1=document.getElementById("div1");
		div1.innerHTML='Hello '+name+"!";
	}
</script>
<div id="div1">原始显示内容</div>
<button onclick="f('yanjun')">点击</button>

方法3:使用Function类定义匿名函数 var f=new Function("形参列表","具体的代码块");

<script>
	var f=new Function("name", "var
div1=document.getElementById('div1');
div1.innerHTML='hello'+name+'!';")
</script>
<div id="div1">原始显示内容</div>
<button onclick="f('yanjun')">点击</button>

三种方法的对比:

  • 函数声明有预解析,而且函数的声明优先级高于变量
  • 使用Function构造函数定义函数的方式是一个函数表达式,这种方式会导致两次解析,严重影响性能。第一次解析常规的JavaScript代码,第二次解析传入构造函数的字符串

递归函数

递归函数是一种允许在函数定义中调用自身函数的特殊函数。例如计算n的阶乘, n!=n*(n-1)!

  • 在js函数中允许在不同的情况下返回不同类型的数据,但是一般不建议这种写法
var f=function(n){
    
    
	if(typeof(n)=='number'){
    
    
		if(n==1) return 1;
		else return n*f(n-1);
	}else
		alert('参数类型错误');
}

使用递归调用计算n的阶乘

<script>
	function jieCheng(num) {
    
    
		alert(num instanceof Number);
		//数据类型的判断可以使用typeof或instanceof两种方式判断
		if ((!(num instanceof Number)) || num < 1) {
    
    
			alert('参数错误!');
			return -1;//返回-1用于表示计算出现错误,当然也可以使用throw new Error("")抛出异常
		}
		if (num == 1) return 1;
		else
			return num * jieCheng(new Number(num - 1));
	}
	
	// var res = jieCheng('5'); 报错 -1加上alert
	var res=jieCheng(new Number('5'));
	alert(res);
</script>

数据类型判断instanceof不正确,除非new Number(5),否则instanceof为false

<script>
	function jieCheng(num) {
    
    
		if (!(typeof(num)=='number') || num < 1) {
    
    
			alert('参数错误!');
			return -1;//返回-1用于表示计算出现错误,当然也可以使用throw new Error("")抛出异常
		}
		if (num == 1) return 1;
		else
			return num * jieCheng(num - 1);
	}

	// var res = jieCheng('5'); 报错 -1加上alert
	var res=jieCheng(5);
	alert(res);
</script>

编写JS的流程

  • 布局:html+css 在写js之前必须保证有一个稳固的布局,这个布局本身不能有任何兼容问题
  • 属性:确定要修改哪些属性 确定通过js修改哪些属性,例如display
  • 事件:确定用户做哪些操作(产品设计)确定要在什么样的事件里修改,比如点击、移入移出
  • 编写js:在事件中,用js来修改页面元素的样式
btn1.style.display='block' 将btn1的style的display的值设置为block

get Element By Id 通过id获取元素

<label onmouseover = “document.getElementById(‘btn1’).style.display=‘block’”
onmouseout = “document.getElementById(‘btn1’) .style.display=‘none’“>鼠标经过出现按钮</label>

局部的概念

在函数中定义的变量就是局部变量,仅仅在函数内部范围内有效,特殊问题就是提升问题。

<script>
	function ff(){
    
    
		var kk=99;
		document.writeln("在ff函数内部调用kk="+kk+"<br/>");
	}

	//首先先调用函数。函数不会主动执行
	ff();
	document.writeln("在函数外部访问函数内的kk="+kk+"<br/>"); //Uncaught ReferenceError: kk is not defined
</script>

在函数外部定义的变量就是全局变量

  • 局部变量只能在函数内部访问,全局变量可以在所有的函数里访问
  • 局部变量在函数执行完毕后销毁,全局变量在页面关闭后销毁。

注意:在函数内没有使用var定义的变量也是全局变量

<script>
	var kk=99;
	function ff(){
    
    
		// kk=100;
		document.writeln("在ff函数内部调用kk="+kk+"<br/>");
	}
	ff();
	document.writeln("在函数外部访问函数内的kk="+kk+"<br/>");
</script>

局部函数是在函数内部定义的函数,例如在outer函数中定义一个函数inner,则inner就是局部函数,outer则是全局函数。可以在outer函数内访问inner局部函数,但是在outer函数之外则无法访问内部局部函数inner

<script>
	function outer(){
    
    
		function inner(){
    
    
			document.writeln("局部函数111<br/>");
		}
		document.writeln("开始测试局部函数...<br/>");
		inner(); //调用内部局部函数
		document.writeln("结束测试局部函数....")
	}
	
	outer();//调用外部函数

	// 在外部函数之外不能调用内部函数,只能通过外部函数调用内部函数执行
	inner(); //试图调用局部内部函数,内部函数.html:15 Uncaught eferenceError: inner is not defined
</script>

非要在外部函数之外访问内部函数的解决方案:

<script>
	function ff(){
    
    
		var kk=100;//在ff函数内部有效,局部变量
		function show(){
    
     //内部函数,一般外部函数之外不能直接访问
			alert("内部函数中访问内部局部变量:"+kk);
		}
		// show();
		return show; //返回内部函数,从而将show挂在到window对象上
	}

	var p=ff(); //接收ff函数的返回值,从而将p挂在到window对象
	p();//实际上就是调用ff的内部函数show()
	//ff();
	//show();
</script>

闭包

闭包就是能够读取其它函数内部变量的函数,是将函数内部和函数连接起来的一个桥梁,即使外部函数已经执行完毕。

JavaScript 自带了垃圾回收机制,对于函数而言,在函数执行完毕后会被垃圾回收机制回收,进行内存释放,函数内部的局部变量也会被销毁,在内存中仅仅只能保存全局作用域

<script>
	function ff(){
    
    
		var kk=100;//局部变量,仅仅在ff的范围内有效
	}
	ff();
	document.writeln(kk); //如果访问内部布局变量,则Uncaught ReferenceError: kk is not defined
</script>

为了在外部直接访问函数内部的变量或者直接调用内部的函数,可以引入闭包以延长内部变量的有效范围

<script>
	function ff(){
    
    
		var kk=100;//局部变量,仅仅在ff的范围内有效
		return kk;
	}

	var p=ff();
	document.writeln(p);
</script>

引入闭包的原因

  • 使得外部得以访问函数内部的变量
  • 避免全局变量的使用,防止全局变量污染
  • 让关键变量得以常驻内存,免于被回收销毁

针对内部函数

<script>
	function ff(){
    
    
		function show(){
    
    }
		return show;
	}

	var p=ff();
	p();
</script>

闭包的验证

function f1(){
    
    
	var num=999;

	add=function(){
    
    
		num++;
	}
	function f2(){
    
    
		alert(num);
	}
	return f2;
}

var res=f1();
add();//注意add由于没有使用var定义,所以add属于全局的
res();
add();
res();

注意点:

  • 由于闭包会使得函数中的变量被保存到内存中,内存消耗会比较大,所以具体使用中注意不要滥用闭包。解决方案是:在推出函数之前,将不再使用的局部变量删除
  • 闭包会在父函数外部改变父函数内部变量的值。会影响代码的封装性

全局和局部冲突

var sex=true;

function ff(){
    
    
	console.log("ff before:"+sex);
	var sex=123;
	console.log("ff end:"+sex);
}

ff();
alert(sex);

自定义类

张老太养了两只猫猫:一只名字叫小白,今年3岁,白色;还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字、年龄和颜色。如果用户输入的小猫名错误,则显示”张老太没有这只猫猫“。

以基础技术来解决

var cat1Name="小白";
var cat1age=3;
var cat1Color="白色";

var cat2Name="小花";
var cat2age=10;
var cat2Color="花色";

以面向对象技术来解决

//定义猫对象
function Cat(){
    
     }
//定义主人
function Master(){
    
     }

var master1=new Master();
master1.mName="张老太";

//实例化猫
var cat1=new Cat();
//!!特别说明: 在js中,你给它什么成员属性,是自由的.
cat1.cname="小白";
cat1.age=3;
cat1.color="白色";
cat1.master=master1;

var cat2=new Cat();
cat2.dname="小花";
cat2.age=100;
cat2.color="花色";

//访问一下
document.write(cat1.cname+" "+cat1["cname"]+" 它的主人是"+cat1.master["mName"]
);

访问对象公开属性的方式有两种:

  • 对象名.属性名
  • 对象名[‘属性名’]

类(原型对象)和对象(实例)的区别和联系

  • 类(原型对象)是抽象的,概念的,代表一类事物,比如人、猫…
  • 对象是具体的,实际的,代表一个具体事物
  • 类(原型对象)是对象实例的模板,对象实例是类的一个个体

对象的属性

对象的属性,一般是基本数据类型(数、字符串),也可是另外的对象。比如前面创建猫对象的age就是猫对象的属性

this的用法

  • 使用this关键字修饰的变量不再是局部变量,它是该函数的实例属性
function Person(name,age){
    
    
	this.name=name; //实例对象每个实例都不同,可以通过new Person("",19).name的方式访问
	Person.nation="汉族"; //类属性,是类的所有实例公用,只能通过Person.nation的方式访问,而不能使用new Person("yan",18).nation方式访问
	var bb=0;//局部变量,外面不能访问,类似局部函数
	cc=123;//全局变量

		this.show=function(){
    
    } //public的成员方法
		Person.bbb=function(){
    
    } //类成员
		abc=function(){
    
    } //全局方法
}

//注意:局部变量在函数执行完毕后销毁,全局变量在页面关闭后销毁,函数内没有使用var声明的变量(需要通过this.的方式)为全局变量

//直接定义一个对象
var p={
    
    
	pp:function(){
    
    
		for(var k=0;k<10;k++)
		document.writeln("慢慢的走...");
	}
}
//调用方式
p.pp();

js是一种动态语言,允许随时给对象增加属性和方法,直接为对象的某个属性赋值时就是给对象增加属性。

var obj={
    
    }; //使用JSON语法创建一个对象
obj.name='zhangsan'; //向obj对象中添加一个属性name
obj.show=function(){
    
    }; //向obj对象中添加一个成员方法show

总结:函数、方法、对象、类

JavaScript中函数定义后可以得到4项内容

  • 函数:函数和 Java 中的方法一样,这个函数可以被调用。没有形参和实参一一对应的问题
<script>
	var f = function (name, age) {
    
    
		alert(name + "-->" + age);
		//可以通过arguments获取所有请求参数
		for(let kk in arguments){
    
    
			document.writeln('参数['+kk+"]="+arguments[kk]+"<br/>");
		}
	}

	f('yanjun',18,23); //实参只有一个,但是形参有2个,实际上传值是从左向右对应,如果缺少则默认传值为undefined
</script>
  • 对象:定义一个函数,系统也会创建一个对象,这个对象就是Function类的实例
var f=function(){
    
    }

alert(f instanceof Function);// true 表示f就是Function类型的变量,可以通过f()调用函数
alert(f instanceof Object);// true,表示f是Object类型的变量
alert(f instanceof String);//false,表示f不是String类型

针对对象,如果需要了解核心细节,可以使用console.log(f)输出/console.info

  • 方法:定义一个函数时,这个函数通常会被附加给某个对象,作为对象的方法。如果没有明确指出将函数俯角到哪个对象上,该函数则将附加到window对象上,作为window对象的方法
var f=function(){
    
    }

console.log(window);
window.f();
f();
  • 类:定义函数时也得到一个与函数同名的类,该函数就是该类的唯一的构造器。所以实际上函数有直接调用和使用new关键字调用两种方法
var f=function(){
    
    
	alert('hello javascript!');
}
//直接调用f();

//使用new运算符创建对象时,会自动执行function()中的定义,function()会当作类的构造器
var ff=new f();
alert(ff);

调用函数的4种方法

方法1:作为一个函数调用,基础语法【函数名(实参列表)】。函数实际上作为全局对象调用,会使this的值成为全局对象,使用window对象作为一个变量,容易造成程序崩溃

<script>
	function bb(){
    
    
		this.name="zhangsan";//实际上是将name定义为全局属性
		console.log(this); //输出的应该是window对象
	}

	bb();
	window.bb();
	document.writeln("window.name="+window.name); //证明name称为全局属性
	document.writeln("name="+name);//实际输出和window.name一致
</script>

方法2:函数作为方法调用。函数作为对象的方法进行调用,会是this的值称为对象本身

//使用JSON格式定义对象,JSON的语法格式为{key1:value1,key2:value2...}
var p1={
    
    
	"username":"zhangsan", //定义属性,格式为【属性名:值】
	"age":18,
	show:function(k){
    
    //定义方法,格式为【方法名称:function(形参){}】
		console.log(this);
		document.writeln(k+"---"+this.username+"---"+this.age);
	}
};
//通过对象名.方法名的形式调用show函数
p1.show(12);

方法3:使用构造器的方式调用函数,构造函数中的this指向当前对象

function Person(){
    
    
	this.name="zhangsan";//定义public属性
	this.age;
	
	this.show=function(){
    
    
		console.log(this);
		document.writeln("name:"+this.name+",age:"+this.age);
	}
}

var p1=new Person(); //则function Person()会自动调用执行
alert(p1.name);//获取p1对象中的name属性值
alert(window.name);//不能获取到数据,因为Person中的this用于指代Person类型对象,不是window对象
p1.show();//调用成员方法

方法4:作为函数间接调用。实现有3种不同方式

类和对象

定义函数后实际上可以得到4项内容

  • 函数。可以直接调用
  • 对象。Function类型的实例
  • 方法。函数会自动附加到某个对象
  • 类。函数是类的唯一构造器
//前面已经进行了试验
function Person(name,age){
    
    
	this.name=name; //实例属性,每个实例都不一样,可以通过【对象名.属性名】的方式进行访问

	Person.nation="汉族"; //静态类属性,所有对象共享的属性,只能通过【类型名.属性名】的方式访问
	Person.show = function () {
    
     //静态类成员方法
		console.log(this); //this可以使用,用于指代当前函数
		console.log(this.username); //undefined
		console.log(this.nation); //汉族
	}

	var bb=100; //局部变量或者理解为私有属性,不能通过对象名的方式进行访问,可以使用闭包函数访问
	var ff=function(){
    
    
		return bb;
	}
	return ff;
}

class关键字

ES6引入了class关键字可以用于定义类,但是需要考虑浏览器的支持问题

class Person{
    
    
	constructor(){
    
     //定义构造器函数,只能定义一个,名称固定,参数个数没有限制。如果需要定义多个构造器可以考虑使用arguments判断参数格式
		if(arguments.length>1){
    
    
			this.username=arguments[0];// this用于定义公共属性
			this.age=arguments[1];
		} else if(arguments.length==1){
    
    
			this.username=arguments[0];
			this.age=99;
		} else {
    
    
			this.username="zhangsan";
			this.age=18;
		}
	}
	//可以定义成员方法,规则是【方法名称(参数列表){}】
	show(){
    
    
		return "Hello "+this.username+",今年"+this.age+"岁了!";
	}
}

var p=new Person();
document.writeln(p.show());

p=new Person("lisi",55,true); //按照处理逻辑并没有接收arguments[2],所以true没有使用
document.writeln(p.show());

函数间接调用的3种方式

直接调用 p.pp(1,2,3);

call方法动态调用函数

var ee=function(arr,fn){
    
     //其中参数fn就是调用时才设置的函数
	for(var index in arr){
    
    
		fn.call(null,index,arr[index]); //调用函数fn,参数1调用的函数所属于的对象,如果值为null则表示window,后面参数则是调用函数时的参数
	}
}

//调用ee函数
ee([2,3,4,5,6],function(index,ele){
    
     //function()就是传递给fn的函数
	document.writeln("第"+index+"个元素是"+ele+"<br/>");
});

apply动态调用函数

通过call调用函数时必须在括号种详细列出每个参数,但是通过apply动态调用函数时,可以在括号中以arguments代表所有参数

var ff=function(a,b){
    
    
alert(a+b);
}
ff.call(window,12,23); //12和23对应的就是函数中的参数,通过call方法动态调用时需要为每
个调用方法逐个的传入参数,写法为12,23
ff.apply(window,[12,23,34,56]);//通过apply方法动态调用方法时,能与arguments一次性传
入多个参数,例如这里的参数就是一个数组[12,23,34,56]

函数的参数处理

JavaScript 采用值传递方式,当通过实参调用函数时,传入函数里的不是实参本身,而是实参的不笨,因为在函数中修改参数值并不会对实参有任何影响。复合类型参数的是一个引用

<meta charset="UTF-8">
<script>
function ff(person) {
    
    
if (typeof (person) == 'object') {
    
    
person.age = 100;
} else alert('参数类型不是复合类型' + typeof person);
}
person={
    
    "age":99,toString:function(){
    
    return "age:"+this.age}};
ff(person);
document.writeln(person);
</script>

参数的新问题

在 JavaScript 中没有函数重载的概念,函数名称是函数的唯一标识,如果定义两个同名函数,则后盖前

function ff(person){
    
    
if(typeof(person)=='object'){
    
    
person.age=100;
} else
alert('参数类型不是复合类型'+typeof person);
}
ff(); //person形参对应的实参为undefined

原型

JavaScript 是基于原型的语言,原型可以理解为原件,通过精确地复制原件可以创建对象。JavaScript对象都有一个叫做原型的公共属性,这个原型属性就是构造函数的原型对象prototype。

function Person(){
    
    
	this.name="Nicholas";
	this.age=29;
	this.job="Software Engineer";
	this.sayName=function(){
    
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39756007/article/details/127865484