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);}elsealert('参数类型错误');
}

使用递归调用计算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;elsereturn num * jieCheng(new Number(num - 1));}// var res = jieCheng('5'); 报错 -1加上alertvar 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;elsereturn num * jieCheng(num - 1);}// var res = jieCheng('5'); 报错 -1加上alertvar res=jieCheng(5);alert(res);
</script>

编写JS的流程

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

JavaScript_函数相关推荐

  1. JavaScript_函数_this

    函数中的this this:是函数中的一个关键字 this指向一个对象 当主动调用函数this的时候,this指向window. function fun() {console.log(this);} ...

  2. JavaScript快速入门到高级 JS精品视频课程

    原文链接:https://www.3mooc.com/front/articleinfo/241 第1章 JavaScript_简介概述和使用 课时1 开始学习 初识javaScript 课时2 sc ...

  3. 数据库中自定义排序规则,Mysql中自定义字段排序规则,Oracle中自定义字段排序规则,decode函数的用法,field函数的用法

    数据库中自定义排序 场景:有一张banner表,表中有一个status字段,有0, 1, 2三个状态位,我想要 1,0,2的自定义排序(这里是重点),然后再进行之上对sequence字段进行二次排序( ...

  4. Mysql函数group_concat、find_in_set 多值分隔字符字段进行数据库字段值翻译

    Mysql函数group_concat.find_in_set进行数据库字段值翻译 场景 配方表:记录包含的原料 sources表示原料,字段值之间用逗号分隔 原料表:对应原料id和原料名称 现需要查 ...

  5. C++ 笔记(34)— C++ exit 函数

    当遇到 main 函数中的 return 语句时,C++ 程序将停止执行.但其他函数结束时,程序并不会停止.程序的控制将返回到函数调用之后的位置.然而,有时候会出现一些非常少见的情况,使得程序有必要在 ...

  6. C++ 笔记(30)— 友元函数与友元类

    我们知道类的私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行.这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦. ...

  7. 浅显易懂 Makefile 入门 (07)— 其它函数(foreach 、if、call、origin )

    1. foreach 函数 foreach 函数定义如下: $(foreach <var>,<list>,<text>) 函数的功能是:把参数 <list&g ...

  8. 浅显易懂 Makefile 入门 (06)— 文件名操作函数(dir、notdir、suffix、basename、addsuffix、addperfix、join、wildcard)

    编写 Makefile 的时候,很多情况下需要对文件名进行操作.例如获取文件的路径,去除文件的路径,取出文件前缀或后缀等等. 注意:下面的每个函数的参数字符串都会被当作或是一个系列的文件名来看待. 1 ...

  9. Go 学习笔记(65)— Go 中函数参数是传值还是传引用

    Go 语言中,函数参数传递采用是值传递的方式.所谓"值传递",就是将实际参数在内存中的表示逐位拷贝到形式参数中.对于像整型.数组.结构体这类类型,它们的内存表示就是它们自身的数据内 ...

最新文章

  1. vb.net限制datagridview不能选择_家里安装200兆宽带,网速还是很慢怎么办?教你一招解除网速限制...
  2. [CQOI2016]手机号码 数位DP
  3. 使用TR1的智能指针
  4. Flask服务部署与简单内网穿透
  5. css两张图片怎么合在一起_web前端入门到实战:纯CSS实现两个球相交的粘粘效果...
  6. 《图解深度学习》图书及代码,16章带你无障碍深度学习
  7. 华为SDN+VxLAN学习小记
  8. Mybatis DAO接口为什么不需要实现类
  9. android gridview 数据绑定,Android GridView数据绑定
  10. Debian 8 Jessie desktop on arm
  11. 显示器屏幕尺寸及分辨率
  12. vue Root file specified for compilation
  13. Ubuntu 14.04 解决 DNS 解析失败
  14. OpenSSL生成.key、.crt、.pfx证书(Windows下)
  15. 我的世界服务器修改皮肤指令,我的世界怎么拿指令来改皮肤的 | 手游网游页游攻略大全...
  16. 存储性能指标--iops
  17. 安卓的BP、AP、NV是什么意思
  18. 2020年4月程序员工资统计,人工智能工资大跌
  19. 微软必应词典UWP -2017春
  20. linux下用mpg123听音乐

热门文章

  1. 学习上溯造型和下溯造型 【转载一篇】
  2. 安卓音乐播放器app开发(一)---功能分析及启动页的制作
  3. 区块链简单实现之p2p网络多节点同步
  4. java有关的论述_中国大学MOOC: 以下有关JAVA线程的论述,正确的是()。
  5. Go语言解决go get失败的问题
  6. 本地图片转网图url专用
  7. 工控机安装服务器系统,工控机驱动安装步骤及流程说明
  8. SpringBoot + Shiro 实现微博登录
  9. 公开课教学反思 计算机,《百数表》公开课教学反思
  10. [CVPR2020]FaceScape: a Large-scale High Quality 3D Face Dataset and Detailed Riggable 3D Face