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 []

变量作用域

  1. 变量提升

    //先扫描整个函数,把变量声明提升到函数头部
    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';
    }
  2. 全局作用域
    不在任何函数内定义的变量就具有全局作用域。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
  1. map
    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
  2. 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
    }
  3. 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;
    });
  4. 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()会对原数组修改,返回的也是原数组。
  5. Array
    1. every()方法可以判断数组的所有元素是否满足测试条件。
      let arr2 = ['Apple', 'pear', 'orange'];
      console.log(arr2.every(function (s) {
      return s.length > 0;
      })); // true, 因为每个元素都满足s.length>0
    2. find方法用于查找符合条件的第一个元素,如果找到了,返回这个元素,否则,返回undefined。
      console.log(arr2.find(function (s) {
      return s.toLowerCase() === s;
      })); // 'pear', 因为pear全部是
    3. findIndex()find()类似,也是查找符合条件的第一个元素,不同之处在于findIndex()会返回这个元素的索引,如果没有找到,返回-1。
      console.log(arr.findIndex(function (s) {
      return s.toLowerCase() === s;
      })); // 1, 因为'pear'的索引是1
    4. 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, ...
}
全部评论

相关推荐

点赞 评论 收藏
分享
09-29 17:44
已编辑
蔚来_测(准入职员工)
//鲨鱼辣椒:见不了了我实习了四个月上周再投筛选了一天就给我挂了
点赞 评论 收藏
分享
jack_miller:我给我们导员说我不在这里转正,可能没三方签了。导员说没事学校催的时候帮我想办法应付一下
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务