函数 & 方法

参考

参数带大括号

传入的参数是一个对象,但只需要用到它的某个属性,可以用 { 属性 } 的方式传入

var foo = {
    name: 'foo'
};

function test({
    name
}) {
    console.log(name);
}

test(foo);

this的指向

绑定到对象上的函数称为方法,和普通函数没啥大区别,但是在它内部可以使用this关键字,指向当前对象

下面例子中,第2种调用方法和第3种调用方法都输出了NaN,因为它们都指向了window对象

这是JavaScript的一个设计错误,想要纠正可没那么简单。所以ECMA决定,在strict模式下,函数的this会指向undefined,会抛出错误

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 2000,
    age: getAge
};
// 输出正常结果
console.log(xiaoming.age());
// 输出NaN
console.log(getAge());
// 输出NaN
var fn = xiaoming.age;
console.log(fn());

that的使用

如果在函数内部定义函数的话,this的指向又会出现问题。普通模式下指向window,严格模式下抛出错误

我们可以用var that = this;来捕获this

var xiaoming = {
    name: '小明',
    birth: 2000,
    age: function () {
        var that = this;

        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - that.birth;
        }
        return getAgeFromBirth();
    }
};

xiaoming.age();

apply和call

我们也可以使用apply()或call()来动态传入this指向的对象

apply()和call()唯一的区别就是传参方式的不同

如果我们想要使用apply()或call()来调用普通函数,第一个参数传入null就行了

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 2000,
    age: getAge
};

// 忽略下面的传参,只是演示apply和call的传参方式的区别
console.log(getAge.apply(xiaoming, [1, 2, 3]));
console.log(getAge.call(xiaoming, 1, 2, 3));

利用apply()来实现装饰器效果

var count = 0;
var oldParseInt = parseInt;

window.parseInt = function () {
    count += 1;
    // 当前场景直接 return oldParseInt(arguments); 就行了
    // 但是有些场景需要动态改变this的指向,我们就需要用到apply()了
    return oldParseInt.apply(null, arguments);
};

map reduce

map方法定义在了JavaScript的Array中,所以我们可以使用Array.map()对数组进行批量处理

function pow(x) {
    return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(pow);
// [1, 4, 9, 16, 25, 36, 49, 64, 81]
console.log(results);

Array的reduce()把一个函数作用在这个Array的[x1, x2, x3...]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算

var arr = [1, 3, 5, 7, 9];
var result = arr.reduce(function (x, y) {
    return x + y;
});
// 25
console.log(result);

filter

利用filter方法过滤arr中的偶数

var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
    return x % 2 !== 0;
});
// [1, 5, 9, 15]
console.log(r);

一些复杂的场景,我们可能需要用到数组的索引以及整个数组,可以通过filter方法的第2个参数和第3个参数获取到

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;
});

sort

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]
console.log(arr);

every

every()方法可以判断数组的所有元素是否满足条件

var arr = ['Apple', 'pear', 'orange'];
var res = arr.every(function (s) {
    return s.toLowerCase() === s;
});
// false
console.log(res);

find & findIndex

find()用于查找符合条件的第一个元素,如果找到了,返回这个元素,否则,返回undefined

var arr = ['Apple', 'pear', 'orange'];
var res = arr.find(function (s) {
   return s == 'pear';
});
// pear
console.log(res);
// 如果使用findIndex(),则返回1

forEach

var arr = ['Apple', 'pear', 'orange'];
arr.forEach(function (item) {
    console.log(item);
});

闭包

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量

如果一定要引用的话,需要再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

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];
console.log(f1());  // 16
console.log(f2());  // 16
console.log(f3());  // 16

function countClosure() {
    var arr = [];
    for (var i = 1; i <= 3; i++) {
        arr.push(
            (function (n) {
                return function () {
                    return n * n;
                }
            })(i)
        );
    }
    return arr;
}

var results = countClosure();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
console.log(f1());  // 1
console.log(f2());  // 4
console.log(f3());  // 9

箭头函数

定义箭头函数

// 完整的箭头函数
var fn1 = (x) => {
    return x * x;
};
// 省略{}和return的箭头函数
var fn2 = (x) => x * x;
// 全部省略的箭头函数(单表达式)
var fn3 = x => x * x;
// 无参数的箭头函数
var fn4 = () => 3.14;
// 如果要返回一个对象,单表达式需要加(),否则会报错
var fn5 = x => ({ foo: x })

箭头函数中的this关键字是按照词法作用域绑定的,这是它与匿名函数最大的不同之处

var obj1 = {
    birth: 2000,
    getAge: function () {
        var b = this.birth;
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this指向window或undefined
        };
        return fn();
    }
};

// NaN
console.log(obj1.getAge());

var obj2 = {
    birth: 2000,
    getAge: function () {
        var b = this.birth;
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj2对象
        return fn();
    }
};

// 预期的结果
console.log(obj2.getAge());

由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数会被忽略

generator

generator和函数不同的是,generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次

function* foo(x) {
    yield x + 1;
    yield x + 2;
    return x + 3;
}

var gen = foo(1);
// foo {<suspended>}
console.log(gen);
// {value: 2, done: false}
console.log(gen.next());
// {value: 3, done: false}
console.log(gen.next());
// {value: 4, done: true}
console.log(gen.next());
// {value: undefined, done: true}
console.log(gen.next());

results matching ""

    No results matching ""