2.函数
2.1 函数定义和调用
1.如何定义?
以function开头,后接函数名,再接一个小括号,小括号里面放入参数,最后接一对花括号,花括号里面可放函数执行内容,也可没有内容:
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
2.如果函数没有名称,则是一个匿名函数。函数可以当成一个对象赋给一个变量【注意最后要加上一个分号,代表赋值结束】。
3.调用函数;怎么调用?调用注意些什么?
函数名+参数 即可完成调用。参数的个数在JS里面没有限制,少了会以undefine 的形式传入进来函数中,多了的参数将不起作用。
4.关键字arguments
arguments可以获取传入的参数个数,但常用来判断参数的个数。
function foo(x) {
alert(x); // 10
for (var i=0; i<arguments.length; i++) {
alert(arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);
5.rest参数
由于JS接收任意的参数个数,使用arguments来判断再去除太过于别扭,引入rest可以将多余的参数以数组的形式存入rest中。书写的形式为:
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]
foo(1);
// 结果:
// a = 1
// b = undefined
// Array []
5.return语句的多行写法。
如果在return后面直接换行,则JS会在return后面自动加上一个分号。
function foo() {
return { // 这里不会自动加分号,因为{表示语句尚未结束
name: 'foo'
};
}
2.2 变量作用域与解构赋值
在JavaScript中,用var申明的变量实际上是有作用域的。
不同函数内部的同名变量互相独立,互不影响。
内部函数可以访问外部函数定义的变量,反过来则不行。
1.变量提升
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部。JavaScript引擎会自动提升了变量y的声明,但不会提升变量y的赋值。最好是用一个var来声明所有的变量,变量间用逗号隔开。
2.全局作用域
不在任何函数内定义的变量就具有全局作用域。。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性
3.名字空间
不同JS文件之间的全局变量可能会出现冲突,且这样难以发现。解决的办法可以将自己全部的全局变量放到自己的唯一的命名空间中去:
// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
4.局部作用域
由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的。
ES6引入let来代替var 声明一个块级作用域
5.常量
在ES6中使用const 来定义一个常量。
6.解构赋值
解构赋值:同时给多个变量赋值。
传统做法将一个数组的元素分别赋值给其他几个变量,需要一个下标一个下标来赋值。
var array = ['hello', 'JavaScript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];
引入解构赋值后:
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
解构赋值最重要的一点是要位置一致,可以做到忽略某个元素赋值。
如果需要从一个对象中取出若干属性,也可以使用解构赋值,便于快速获取对象的指定属性:
'use strict';
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
var {name, age, passport} = person;
// name, age, passport分别被赋值为对应属性:
如果一个对象中出现嵌套,可以用解构赋值,不过要注意位置统一一致。如果对象中的某属性不存在,则会赋给变量undefined
如果变量已经定义,再去赋值将出现错误。
7.使用场景
解构赋值可以大大简化代码。
1.交换变量的值,不需要临时变量。
2.获取当前页面域名和路径。
3.快速建立一个date对象
2.3 方法
在一个对象中绑定函数,称为这个对象的方法。
1.对象中的方法如果用到了this,它的指向是不固定的。如果直接使用 对象.方法 来调用,this指向这个对象,直接调用这个函数,则this指向窗口window。
2.apply
可以使用apply来指定this的指向。使用call也可以,call是直接传入参数,不用数组而apply使用数组。
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
3.在方法的内部定义其他函数,同时又用到了this,这样的this将指向的是undefined。这是在定义函数之前将this 赋值给that:
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
xiaoming.age(); // 25
4.装饰器
利用apply(),我们还可以动态改变函数的行为。
JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。
2.4 高阶函数
一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。
2.4.1 map/reduce
map函数可以作用于每个元素,如给数组中的每个元素进行平方运算,可以使用map一步到位:
function pow(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把Array的所有数字转为字符串:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce函数与map不同,它是把其中一个元素的计算结果与下一个元素再进行累积运算,所以必须接收两个参数:
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
}); // 25
练习:
1、将字符串’13579’ 转化为数字的 13579
'use strict';
function string2int(s) {
return s.split("").map(function (x){
return x-0;
}).reduce(function (x, y){
return x*10 + y;
});
}
使用map时注意和parseint函数的使用,详看:
http://blog.csdn.net/freshlover/article/details/19034079
2、请把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:[‘adam’, ‘LISA’, ‘barT’],输出:[‘Adam’, ‘Lisa’, ‘Bart’]。
'use strict';
function normalize(arr) {
return arr.map(function(name){
return name[0].toUpperCase()+name.substring(1).toLowerCase();
});
}
2.4.2 filter
filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素。
和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
return x % 2 !== 0;
});
r; // [1, 5, 9, 15]
传入回调函数筛选重复的元素。
2.4.3 sort
通常规定,对于两个元素x和y,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。
要按数字大小排序,我们可以这么写:
var arr = [10, 20, 1, 2];
arr.sort(function(x,y){
if (x>y){
return 1;
}
if (x<y){
return -1;
}
return 0;
})// [1, 2, 10, 20]
如果要倒序排序,我们可以把大的数放前面:
var arr = [10, 20, 1, 2];
arr.sort(function(x,y){
if (x>y){
return -1;
}
if (x<y){
return 1;
}
return 0;
})// [20, 10, 2, 1]
如果要对字符串进行比较,可以全部转化成大写或小写再进行比较。
最后友情提示,sort()方法会直接对Array进行修改,它返回的结果仍是当前Array
2.5 闭包
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
廖大:相关参数和变量都保存在返回的函数中
阮大:闭包就是能够读取其他函数内部变量的函数。
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
这篇文章也有助于理解闭包这个概念:
http://www.cnblogs.com/zllwebstudy/p/5618831.html
闭包的用途:外部可以访问内部的变量;将变量保存在内存中
2.6 箭头函数
箭头函数相当于匿名函数,有两种格式,一种只包含一个表达式,连{ … }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ … }和return:
x => x*x;
//相当于:
function (x){
return x*x;
}
x=>{
if (x>0){
return x*x;
}
else {
return -x*x;
}
}
如果没有或有多个参数,要用括号括起来再接箭头。
如果是要返回一个对象:
需要使用:
x=>({foo:x});
如此写会报错:
x=>{foo:x} //因为语法冲突了
this
箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。
由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略。
2.7 generator
generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。