JavaScript 函数
在上一篇文章中,详细讲解了javascript是如何表达信息的,JavaScript如何处理信息.处理信息是任何语言的核心特性,掌握了这些特性那你就掌握了这门语言了.
我讲分成几个步骤去讲解,我画了个脑图如下: 我会从基本的函数的定义、函数的调用、函数的参数以及返回值,来讲解JavaScript的基本函数概念.
函数是神马
看如下代码,相信大家已经见的司空见惯了,函数的声明以及调用. 任何编程语言都存着函数,函数可以帮助我们复用代码,提高可读性,统一修改和维护.
console.log(add(1, 2));
/**
* 函数可以复用代码
*/
function add(num1, num2) {
return num1 + num2;
}
add(2, 2);
我们需要认清JavaScript的函数的本质,在JavaScript中函数就是对象,在JavaScript中任何对象都可以添加属性和方法,那么JavaScript的函数同样可以添加属性和方法.
来看如下代码, 给add函数添加属性和方法,其实和给Object添加属性和方法一样的方式. 很简单就不用多说了.
//JavaScript 中函数就是对象
function add(num1, num2) {
return num1 + num2;
}
//给函数添加属性和方法
add.sex = '男';
add.setSex = function (sex) {
this.sex = sex;
}
console.log(add.sex);//男
console.log(add.setSex('女'));//undefined 由于函数什么都没有返回,所以默认的都是undefined
console.log(add.sex);//女
console.log(add(1, 2));//3
将函数作为数据值来使用,直接看如下代码,函数可以直接赋给一个变量,
text()
就可以直接调用函数,如果直接
text
调用则会返回函数的本体即
function(){return "atex"}
,函数的执行必须加(),如果不加返回的就是函数的本体. 同时函数可以在数组中和对象中声明,函数本质就是个对象,对象能干的事情它都能干.
var text = function () {
return "atex";
}
text();
console.log(text());//atex
console.log(text);//function(){return "atex"}
//数组中可以 对象、函数
[{}, function () { }, { family: {}, setName: function (name) { } }]
//函数可以作为参数使用
//函数名加()表示函数被执行,不加参数表示将函数的本体传入
函数可以作为返回值,代码如下:
//函数作为返回值
function fn() {
return function () {
console.log(1);
}
}
var newFn = fn();
//调用函数返回值的函数
newFn();//1
fn()();//1
注意,在JavaScript中没有块级作用域,如果在if/else中声明函数,不能达到按需定义函数的作用. 为什么呢,我们直接看代码示例:还记得我们在上一篇文章中讲解的预解析的问题,在JavaScript中函数的声明在预解析的时候就已经加载完毕了,所以在if/else中按需定义函数并不能起到作用.
//在预解析的时候就已经对声明函数加载了 并不能达到if/else按需 定义函数的作用
if (true) {
function add() {
}
} else {
function sub() {
}
}
那么,如何能在if/else中按需加载函数呢? 我们可以这样做,将函数赋值给一个变量,var 变量在预解析的时候为undefined,然后在解析时就可以达到按需定义函数的目的,但是并不推荐这种方式.
//可以使用下面的方式,使if/else起到作用,因为 add sub 是变量,在预解析时就变为了undefined,但是并不推荐使用这种方式
if (true) {
var add = function () {
}
} else {
var sub = function () {
}
}
匿名函数这个一个重要的概念,JavaScript在面向对象的编程中:继承、封装都会用到匿名函数. 我会在下一篇文章中,详细的讲解匿名函数,代码如下:
//匿名函数头部需要存在合法的值 才可以自执行
//匿名函数定义直接执行
var s = function () {
console.log('');
}();
//匿名函数的自执行
(function () {
})();
(function () {
}());
//匿名函数也可以这样用
!+function () {
console.log('1');
}();
//也可以这样用
console.log(function () {
return 1;
}());
函数的调用:递归调用、链式调用、方法调用
/递归调用
//计算阶乘
function fn1(num) {
if (num <= 1) return 1;
return num * fn1(num - 1);
}
console.log(fn1(5));
//方法的调用
var option = {
add: function (num1, num2) {
return num1 + num2;
},
sub: function (num1, num2) {
return num1 - num2;
}
}
option.add(1, 2);
option['add'](1, 2);
var key = 'add';
option[key](2, 2);
//链式调用
var option = {
add: function (num1, num2) {
console.log(num1 + num2);
return this;
},
sub: function (num1, num2) {
return this;
}
}
option.add(1, 2).sub(3, 2);
同时还有构造函数的调用、间接调用. 关注一下call和apply这两个关键字,每个函数都存在这两个关键字,非常重要,后期详细讲解.
//构造函数的调用
function Person(){
}
//是不是通过new 来调用构造函数
var obj = new Person();
//间接调用
// function add(){
// }
// //每个函数下都有两个方法
// add.call
// add.apply
var name = 'xm';
var person = {};
person.name = 'xh';
person.getName = function(){
return this.name;
}
console.log(person.getName());//xh
console.log(person.getName.call(window));//xh call 改变this的指向 this不指向了person 而是指向window
function adds(num1,num2){
return num1 + num2;
}
var data = [1,2];
console.log(adds(1,2));
console.log(adds.call(window,1,2));//add 中并没有用到this window 只是改变this的值
console.log(adds.apply(window,data));//apply 和call的区别 就是传递参数是个数组
可以通过函数,给对象动态的设置属性.
//动态设置属性
var person = {
setPerson: function (property, value) {
person[property] = value
}
};
person.setPerson('name', 'xm');
person.setPerson('age', 18);
person.setPerson('sex', 'male');
看下图,这是对函数定义的一个总结:
关于函数调用的一个总结:
函数的参数
关于函数的参数也是很简单的,我们需要明白函数的参数本质: 形参 = 实参
函数的参数的个数
function add(num1, num2) {
return num1 + num2;
}
add(1, 2);
add(1);//num1 = 1 ; num2 = undefined
//可选参数
function pow(base, pow) {
pow = pow || 2;
return Math.pow(base, pow);
}
console.log(3);
console.log(3,2);
可以通过arguments,来获取参数,arguments也是一个非常重要的关键字,arguments 是类似数组的对象.如下:
var arguments = {
'0':1,
'1':2,
'length':2
}
通过arguments来获取参数,注意的是add.length是形参的个数,arguments.length是实参的个数.
function add(){
if(arguments.length === 0) return;
var nums = 0;
for (let index = 0; index < arguments.length; index++) {
const element = arguments[index];
nums += element;
}
return nums;
}
console.log(add(1,2,3,4,5));
注意 arguments 要谨慎使用,如果改变了arguments中的某个参数的值,那么这个参数也就被修改了,如果函数fn中,嵌套函数fn2,fn中的arguments发生改变,但是并不影响fn2中的arguments,每个函数都有一个独特的arguments.
function fn(name){
arguments[0] = '';
console.log(name);//'' name值也发生变化了
function fn2(){//每个函数都有一个独特的arguments
console.log(arguments);//fn2 的自己的arguments
}
}
扩展 arguments.callee 返回当前函数的本体.
//arguments.callee 函数的本体 可以用到在递归调用中
//场景如下:
//如果函数jiecheng 内部过于复杂,用到了递归,某天改掉了函数名,还要将每个递归调用的函数名都要改掉
//用arguments.callee 就是当前函数的本体,这样就不用在手动去改掉函数名了.
// "use strict"//严格模式下 不允许使用arguments.callee 如何修改呢?
function jiecheng(num){
if(num <= 1) return 1;
return num * arguments.callee(num - 1);
}
console.log(jiecheng(5));
严格模式下 可以这样使用
var jiecheng = function fn(num){
if(num <= 1) return 1;
return num * fn(num - 1);
}
console.log(jiecheng(5));
老规矩,通过脑图来总结一下函数的参数
本篇文章,只是讲解了JavaScript的基本知识,函数的深入和运用会在下一篇的文章中<JavaScript面向对象>深入讲解函数.