JavaScript 函数
在 JavaScript中,是头等 (first-class) 对象,因为它们可以像任何其他对象一样具有和。它们与其他对象的区别在于可以被。简而言之,它们是 Function 对象。(MDN)
就是一段片段,就是执行中的。
使用前通常与变量一样需要先进行声明,用 function
关键字定义。
// 常见的的定义方式
function 名(参数, 参数, ...) {
片段;
return 返回值;
}
// (执行中的)
var 的返回值 = 名(参数, 参数, ...);
使用 名()
的方式即可
以下是最简单的:
function say() {
console.log('hello');
}
say(); // :"hello"
这个就会在控制台 hello
字符串。
这个没有返回值,认会返回 undefined
。
在声明的时候,可以对参数也做上说明
假设有需求,需要计算三角形周长的。
计算三角形周长则需要知道三角形三条边各自的长度,然后将他们求和。
定义的时候就可以将三条边作为参数进行声明。
function calcPerimeter(a, b, c) {
// a, b, c 分别代表三条边
var sum = a + b + c;
return sum;
}
// 并将返回值赋值给perimeter
var perimeter = calcPerimeter(, , );
在的时可以过去,这些值可以在中被访问。
在以上 calcPerimeter
被的时,传递了 3, 4, 5
三个值。
三个值对应到声明时定义的三个参数 a, b, c
。
所以执行过程中 sum
的值为 3 + 4 + 5
,即 12
,随后 sum
被作为返回值进行返回。
最终变量 perimeter
也会被赋值为12。
可以对进行封装,让逻辑更加清晰。
比如如下块:
// 改写前
var num = ;
var flag = false;
var i;
var len;
for (i = , len = num - ; i <= len; i++) {
if (num % i === ) {
flag = true;
break;
}
}
console.log(flag);
以上第一眼可能无法看出具体在做什么,仅需一点,就能有所改善。
// 改写后
function isPrimeNumber(num) {
var flag = false;
var i;
var len;
for (i = , len = num - ; i <= len; i++) {
if (num % i === ) {
flag = true;
break;
}
}
return flag;
}
var num = ;
var result = isPrimeNumber(num);
console.log(result);
改写后的似乎多了几行,但是将其中核心部分包装成了。
通过 isPrimeNumber
名可以很容易的了解到这一段作用是用来判断数是否为质数
。
当然有个前提就是起 可以让大部分人看得懂 的名。
优秀的名可以帮助他人更容易理解,同时当自己一段时间后再回头看时,能更容易进入当时写时候的思维模式等。
这里提供几个命名的建议,具体的命名可以根据团队规范、个人成长等做调整。
准确的拼写十分重要,绝大多数情况下名都会是英文单词组成的。
当然许多时候手一快可能就少了字母,或者错将 wrap
进行乾坤大挪移拼写成了 warp
。
许多情况是无法避免的,经常需要自检。
当然可以借助一些单词的检查,如 Visual Stu Code
可以借助 Code Spell Checker
来检查单词的正确性。
再者碰到想起的名但是单词拼写不出来,尽可能翻词典,日积月累能有大量的词汇沉淀。
尽量不要使用拼音或者是首字母缩写。
以下名或许会造成困扰:
function jslsh() {}
function jsNumber() {}
以上是计算两数和
的命名,可能只有天和地知道这个是什么意思。
当然,如果是自己写 demo 或者测试的时候,其实不需要考虑这么多。
如碰到是判断是否
、有没有
、可以
的时候,可以带上一些前缀,比如:
// 是否登入
function isLogin() {}
同时可以合理的使用动词,比如打开
就可以使用 openFile
名,具体的状态可以根据语境、作用、个人习惯等做调整使用。
使用词语的缩写尽量使用通用的缩写
如:
这些缩写大部分开发者是可以看的懂的缩写。
分析:根据圆面积公式 S=π·r·r,其中 S 就是要求的值,即的返回值,π 是常量(固定的值),半径r是未知数,所以r就可以设计成参数
function circleArea(r) {
var pi = ;
return pi * r * r;
}
// 计算半径为10的圆的面积
var area = circleArea();
分析:
某个DOM
和某个类名
可以说明有两个未知量,可以设计成两个参数。
根据描述也可以确定 某个DOM
的类型是个 DOM
对象,某个类名
是个字符串
只要拿到这个DOM的 class
,判断里面是不是含有这个类型即可得到结果
function hasClass(el, className) {
// el 是 element的缩写,表示dom元素
// 如果没有元素 则返回
if (!el) {
return false;
}
// 根据空格分割成数组
// 可以不使用 split ,使用字符串也可以用indexOf匹配
var classList = el.className.split(' ');
// 判断是否存在
if (classList.indexOf(className) >= ) {
return true;
}
return false;
}
以下扩展可能需要一定的知识积累,遇到不懂的地方可以停下脚步,先学习下一章节
以上篇幅的其实都通过声明
的方式来定义,还有一种方式就是使用表达式定义。
// 声明
function add(a, b) {
return a + b;
}
// 表达式
var add = function(a, b) {
return a + b;
};
通过上述例子可以看出写法上的区别就是表达式
是将赋值给了变量。
这两种方式创建的最大的区别在于,不能提前使用表达式创建的
光看句子有点抽象,举个例子?:
var num1 = add1(, );
var num2 = add2(, );
// 声明
function add1(a, b) {
return a + b;
}
// 表达式
var add2 = function(a, b) {
return a + b;
};
上面一段在执行的时候会报 add2 is not a function
的,表示 add2
不是,也就是说 add2
不能被提前使用,而 add1
可以。
具体原因可以查看执行上下文
章节。
有他自己的作用域,内声明的变量等通常情况下
不能被外部访问,但是可以访问到外部的变量或者其他等
var a = ;
function fn() {
var b = ;
console.log(a); // :1
console.log(b); // :2
}
fn();
console.log(b); // ReferenceError: b is not defined
执行以上会报 b is not defined
。
没有名字的就是匿名
var fn = function() {
console.log('我是匿名');
};
除了在表达式
中会出现匿名,还有许多场景。
相对常见的就是自执行匿名
,MDN官方翻译为立即表达式
。
自执行
就是这个声明后就会立即执行,自执行的匿名通常会被用来形成独立的作用域
。
如:
(function() {
var num = ;
alert(num);
})();
这是自执行的匿名,这个匿名是被包裹了一段括号后才被的。
以下这段会报错:
// 报错
function() {
var num = ;
alert(num);
}();
浏览器会告诉你必须给名字。
通过括号包裹一段,让js引擎
识别成他是表达式,再对他进行执行,就不会报错,这是加括号的原因。
同理,可以使用 +
,!
等运算符代替括号,让匿名成为表达式即可。
大部分第三方框架都会通过自执行的匿名包裹,与浏览器全局环境隔离,避免污染到全局环境。
表达式进行声明的时候也可以使用具名
var count = function fn(num) {
console.log('我是');
};
以上这段是不会报错的,但是不能通过 fn
访问到,这里的 fn
只能在内部进行访问,通常在使用递归的形式做计算的时候会用到这种写法。
var count = function fn(num) {
if (num < ) {
return num;
}
return fn(num - ) + num;
}
count();
上面这个例子,就是在内部访问 fn
自己,使用递归的形式求和。
注:递归相关的知识可以参考相关文献进行学习
arguments 是对应于传递给的参数的类数组对象。(MDN)
通常情况下都具有 arguments
对象,可以在内部直接访问到。
他是类数组,即长得很像数组,成员都是用数字编号,同时具有 length 。
arguments 中存放着当前被时,传递过来的所有参数,即便不声明参数,也可以通过 arguments 取到传递过来的参数。
function sum() {
console.log(arguments);
}
sum(, , , );
执行上述,可以看到在控制台了对象,存放的就是所有传递过去的参数,利用这一特性,就可以不限制参数个数,或者让做中转站(),利用 arguments 将参数传递给另。
如,不确定输入的参数个数的:
function sum() {
var total = ;
var i;
var len;
for (i = , len = arguments.length; i < len; i++) {
total += arguments[i];
}
return total;
}
var total = sum(, , , , );
console.log(total); // :25
通过循环遍历 arguments
对象,就可以得到所有参数,然后做累加就可以达到求和的目的。
在本质上是个。
通常都能听到“一下某个”,“取到某个的返回值”,这里的其实就是。
一般是用来描述对象的某个行为的,但是平时我们会混用,口头交流的时候会经常把直接称作。
只要自己理解,不需要去纠结和到底是什么,也不用特意纠正别人的说法,大家都能听得懂就行。
使用 JS DOC
描述是非常良好的习惯,良好的 JS DOC
书写还可以使用工具文档。
JS DOC
对的描述大体如下:
/**
* 这是这个求幂 计算 x 的 y 次方
* @param {Number} x - 底数
* @param {String} y - 指数
*/
function pow(x, y) {
// ...
}
除此之外还可以描述返回值等。
所谓纯,就是没有副作用的
从执行开始到结束,没有对外部环境做任何操作,即对外部环境没有任何影响(没有副作用),这样的就是纯。
纯只负责输入,对于一种输入只有一种返回值。
如果中存在 Math.random
这种影响返回值的,也不能算是纯。
// 纯
function add(a, b) {
return a + b;
}
// 非纯
var person = { name: '小明' };
function changeName {
person.name = '小红'; // 影响了外的,产生了副作用
}
当与 new
关键字一起被的时候,就会作为构造。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {
console.log('我是' + this.name);
};
var person = new Person('阿梅', );
person.say();
console.log(person);
可以看到当作为构造的时候,认返回的是对象。
细心的读者仔细观察就能发现,构造的认返回值是体内的 this。
事实上构造的执行有一定流程:
理解这个流程,就能理解构造的返回值。
具体的的 prototype
等可以参阅原型
章节。
特性相对较多,也是 JavaScript 的核心之一。
可以用于,提供的复用率和可读性,在大部分情况下,当两段具有超高相似度时,应当设计成,不同的部分使用参数进行区分。