JavaScript 之函数
定义函数
function abs(x) { //匿名函数
if (x >= 0) {
return x;
} else {
return -x;
}
} //没有return会返回undefined
var abs = function (x) { //可以通过abs调用
if (x >= 0) {
return x;
} else {
return -x;
}
};
//js不限定传入的参数数目,多了不会被使用
abs(10, 'blablabla'); // 返回10
abs(); //NaN
//arguments指向当前调用者传入的所有参数,常用于判断传入参数长度arguments.length
function foo(x) {
console.log('x = ' + x); // 10
for (var i=0; i<arguments.length; i++) {
console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);
//rest参数允许接收多个参数
function foo(a,b,...rest) {
console.log(a);
console.log(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 [] 变量作用域
变量提升
//先扫描整个函数,把变量声明提升到函数头部 function foo() { var x = 'Hello, ' + y; console.log(x); var y = 'Bob'; } foo(); function foo() { var y; // 提升变量y的申明,此时y为undefined var x = 'Hello, ' + y; console.log(x); y = 'Bob'; }全局作用域
不在任何函数内定义的变量就具有全局作用域。JavaScript默认有全局对象window,变量会被绑定到window的一个属性上:var course = 'Learn JavaScript'; alert(course); // 'Learn JavaScript' alert(window.course); // 'Learn JavaScript' //alert()函数其实也是window的一个变量
变量如果在当前函数作用域中未找到,会到全局作用域寻找,若还为找到,则返回
ReferenceError。名字空间
为了减少命名冲突,可以把定义的变量和函数绑定到一个全局变量上。
// 唯一的全局变量MYAPP: var MYAPP = {}; // 其他变量: MYAPP.name = 'myapp'; MYAPP.version = 1.0; // 其他函数: MYAPP.foo = function () { return 'foo'; };局部作用域
function foo() { var sum = 0; for (let i=0; i<100; i++) { //let替代var可以申明一个块级作用域的变量 sum += i; } // SyntaxError: i += 1; }常量
const PI = 3.14; //代替以前的PI=3.14只是不修改
解构赋值
var [x, y, z] = ['hello', 'JavaScript', 'ES6']; let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']]; let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素 var person = { name: '小明', age: 20, gender: 'male', passport: 'G-12345678', school: 'No.4 middle school', address: { city: 'Beijing', street: 'No.1 Road', zipcode: '100001' } }; var {name, age, passport} = person; //取出特定属性值 var {name, address: {city, zip}} = person; //zip为undefined,addresss为了让city和zip获得嵌套的address对象的属性,查看address返回Uncaught ReferenceError // 把passport属性赋值给变量id: let {name, passport:id} = person; // 如果person对象没有single属性,默认赋值为true: var {name, single=true} = person; // 变量已经被声明了,会发生错误 var x, y; {x, y} = { name: '小明', x: 100, y: 200}; // 语法错误: Uncaught SyntaxError: Unexpected token,解决办法 ({x, y} = { name: '小明', x: 100, y: 200}); //可以交换变量 var x=1, y=2; [x, y] = [y, x] //快速获取域名和路径 var {hostname:domain, pathname:path} = location; //如果函数接收对象作为参数,可直接传入对象的属性 function buildDate({year, month, day, hour=0, minute=0, second=0}) { return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second); } buildDate({ year: 2017, month: 1, day: 1 }); // Sun Jan 01 2017 00:00:00 GMT+0800 (CST)方法
绑定到对象上的函数称为方法。
function getAge() { var y = new Date().getFullYear(); return y - this.birth; } var xiaoming = { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 25, 正常结果 getAge(); // NaN 此时this指向全局对象windows //以下方法也是不行的 var fn = xiaoming.age; // 先拿到xiaoming的age函数 fn(); // NaN 必须用obj.xxx()形式调用才行 'use strict'; //此模式下会this会指向undefined 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在独立的函数调用中,根据是否为
strict模式,this指向undefined或者window,使用apply可以指定函数this指向那个对象。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, 参数为空与
apply()类似的方法是call(),区别如下:apply()把参数打包成Array再传入;call()把参数按顺序传入。
对于普通函数的调用,
this绑定为null。JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。
高阶函数
一个函数可以接受另一个函数作为参数,这种函数称为高阶函数。
'use strict';
function add(x, y, f) {
return f(x) + f(y);
}
var x = add(-5, 6, Math.abs); // 11 - map
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
- reduce
var arr = [1, 3, 5, 7, 9]; arr.reduce(function (x, y) { return x + y; }); // 25 arr.reduce(function (x, y) { return x * 10 + y; }); // 13579 function string2int(s) { //字符串'13579' var ss=s.split(""); var cs=ss.map(function(x){ return +x; }); var r=cs.reduce(function(x,y){ return 10*x+y; }); return r; //13579 } - filter
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] var arr = ['A', '', 'B', null, undefined, 'C', ' ']; var r = arr.filter(function (s) { return s && s.trim(); // 注意:IE9以下的版本没有trim()方法 }); r; // ['A', 'B', 'C'] //filter还可以接受回调函数 var arr = ['A', 'B', 'C']; var r = arr.filter(function (element, index, self) { console.log(element); // 依次打印'A', 'B', 'C' console.log(index); // 依次打印0, 1, 2 console.log(self); // self就是变量arr return true; }); //元素去重 r = arr.filter(function (element, index, self) { return self.indexOf(element) === index; }); - sort
// apple排在了最后:因为小写字母ASCII在大写字母之后 ['Google', 'apple', 'Microsoft'].sort(); // ['Google', 'Microsoft", 'apple'] // 无法理解的结果:默认将所有元素转为String再排序 [10, 20, 1, 2].sort(); // [1, 10, 2, 20] let num=[10,20,1,2]; num.sort(function (x,y) { //return x-y; //从小到大排序 return y-x; //大到小 }); console.log(num);sort()会对原数组修改,返回的也是原数组。 - Array
every()方法可以判断数组的所有元素是否满足测试条件。let arr2 = ['Apple', 'pear', 'orange']; console.log(arr2.every(function (s) { return s.length > 0; })); // true, 因为每个元素都满足s.length>0find方法用于查找符合条件的第一个元素,如果找到了,返回这个元素,否则,返回undefined。console.log(arr2.find(function (s) { return s.toLowerCase() === s; })); // 'pear', 因为pear全部是findIndex()和find()类似,也是查找符合条件的第一个元素,不同之处在于findIndex()会返回这个元素的索引,如果没有找到,返回-1。console.log(arr.findIndex(function (s) { return s.toLowerCase() === s; })); // 1, 因为'pear'的索引是1- forEach()和map()类似,它也把每个元素依次作用于传入的函数,但不会返回新的数组。
arr.forEach(console.log); // 依次打印每个元素
闭包
函数作为返回值。返回函数不要引用任何循环变量,或者后续会发生变化的变量。
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
//f1(),f2(),f3()都是16
//这样修改可以保持结果正确
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
//创建匿名函数,会立即执行,调用f1即可,若如上一种形式需要f1();
arr.push((function (x) {
return x*x;
}(i))); 使用闭包,可以实现私有变量的封装。
function create_counter(initial) {
let x=initial||0;
return {
inc:function () {
x+=1;
return x;
}
}
}
let c1=create_counter();
console.log(c1.inc());
console.log(c1.inc()) 将多参数函数变为但参数函数。
function make_pow(n) {
return function (x) {
return Math.pow(x,n);
}
}
let pow2=make_pow(2);
let pow3=make_pow(3);
console.log(pow2(5));
console.log(pow3(5)); 箭头函数
相当于匿名函数,简化了函数定义。
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}
//this总是指向词法作用域
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 29 generator
generator可以在执行过程中多次返回。
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n ++;
}
return;
}
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true} //true表示生成器已经结束
for (var x of fib(10)) {
console.log(x); // 依次输出0, 1, 1, 2, 3, ...
}
vivo公司福利 368人发布
查看19道真题和解析