javascript读书笔记(三)

方法


var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};

xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

在一个方法内部,this是一个特殊变量,它始终指向当前对象


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

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

xiaoming.age(); // 25, 正常结果
**getAge(); // NaN**

如果单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window。这样写也是不行的:

**var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN**

要保证this指向正确,必须用obj.xxx()的形式调用


有时候这种方法也是不行的:

'use strict';

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - this.birth;
        }
        return getAgeFromBirth();
    }
};

xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined

原因是this指针只在age方法的函数内指向xiaoming,在函数内部定义的函数,this又指向undefined了!(在非strict模式下,它重新指向全局对象window!)


办法:


'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

用var that = this;,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。


apply

指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。

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()把参数按顺序传入。

比如调用Math.max(3, 5, 4),分别用apply()和call()实现如下:

Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

对普通函数调用,我们通常把this绑定为null。


闭包


function lazy_sum(arr) {
    var **sum = function ()** {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return **sum**;
}

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数

var f = lazy_sum([1, 2, 3, 4, 5]); // **function sum()**

调用函数f时,才真正计算求和的结果:

f(); // 15

在函数lazy_sum中又定义了函数sum,并且,内部函数*sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum*时,相关参数和变量都保存在返回的函数中,称为“闭包(Closure)


注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

var f1 = lazy_sum([1, 2, 3, 4, 5]);
var f2 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f2; // false

f1()和f2()的调用结果互不影响。


function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push(function () {
            return **i * i;**  //这个返回里面包含了循环变量i
        });
    }
    return arr;
}

var results = count();   //返回一个数组arr
var f1 = results[0];     //f1,f2,f3里面都引用了i这个变量
var f2 = results[1];
var f3 = results[2];

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都添加到一个Array中返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:

f1(); // 16
f2(); // 16
f3(); // 16

原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16。


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


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


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

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

注意这里用了一个“创建一个匿名函数并立刻执行”的语法:

(function (x) {
    return x * x;
})(3);

在没有class机制,只有函数的语言里,借助闭包,同样可以封装一个私有变量。我们用JavaScript创建一个计数器:

'use strict';

function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}

它用起来像这样:

var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

原文地址:https://www.cnblogs.com/linewman/p/9918394.html