JavaScript 函数

在上一篇文章中,详细讲解了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面向对象>深入讲解函数.

文章作者: JakePrim
文章链接: https://jakeprim.cn/2019/08/02/javascript-函数/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 JakePrim技术研究院
打赏