• 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。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

  • 相关阅读:
    Java_适配器模式
    linux常用命令整理
    (转)使用隐藏的iframe 隐藏form提交,仿AJax无刷新提交,可以实现无刷新上传文件
    mysql添加并返回主键
    学习RMI
    关于bcprov-jdk16
    JavaScript在页面中的引用方法
    通过CFX发布WebService(一)
    字符串和json数据的转换
    MD5 加密与解密
  • 原文地址:https://www.cnblogs.com/linewman/p/9918394.html
Copyright © 2020-2023  润新知