《javascript语言精粹》学习笔记2

来到第四章。

函数:前面说过,函数就是对象,特点就是还能够被调用。

1.函数对象

因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以保存在变量,对象和数组中。函数可以被当作参数传递给其他函数,函数也可以返回函数,也可以拥有方法。
2.函数字面量

<!-- lang: js -->
var add = function (a,b) {
    return a + b;
}

包括保留字function,函数名(可省略,无名称为匿名函数),包围在括号中的参数和包围在花括号中的函数主体。
3.调用

除了接受形式参数外,每个函数默认接受两个附加的参数,this和arguments。js有四种调用模式:方法调用模式,函数调用模式,构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。 实际参数个数超过形式参数个数时,超出的参数值会省略,少时缺失的值会替换为undefined。这个方面js的容忍度很高,就是不会提示。 4.方法调用模式

当一个函数为对象的一个属性时,我们称之为方法。当方法被调用时,this绑定到该对象。

<!-- lang: js -->
var myObject = {
    value : 0,
    increment : function (inc) {
        this.value += typeof inc === 'number' ? inc : 1;
    }
};

myObject.increment();  // 1
document.writeln(myObject.value);

myObject.increment(2);  // 3
document.writeln(myObject.value);

5.函数调用模式

当一个函数并非一个对象的属性时,那么它就是被当作一个函数来调用。它仅仅是一个函数而已。

<!-- lang: js -->
var sum = add(3,4);

注意:这样调用函数,this 被绑定到全局对象。一个解决方案是:如果该方法定义一个变量并把它赋值为this,那么内部函数就可以通过那个变量访问到this,按照约定,那个变量命名为that。

<!-- lang: js -->
myObject.double = function () {
    var that = this;
    var helper = function () {
        that.value = add(that.value. that.value);
    };

    helper();// 以函数形式调用helper
}

//以方法的形式调用double
myObject.double();
document.writeln(myObject.value);

6.构造器调用模式

如果在一个函数前面加上new来调用,那么背地里会创建一个链接到该函数的prototype成员的新对象,同时this会绑定到那个新对象上。

<!-- lang: js -->
//创建一个名为Quo的构造器函数。它构造一个带有status属性的对象
var Quo = function (string) {
    this.status = string;
}
//给Quo的所有实例提供一个名为get_status的公共方法
Quo.prototype.get_status = function () {
    return this.status;
}
//构建一个Quo实例
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());

原文不推荐,后面有更好的替代方式。 7.Apply调用模式

apply方法让我们构建一个参数数组传递给调用函数。接收两个参数,第一个是要绑定给this的值,第二个就是一个参数数组。
实际就是把一个原本不是属于自己的方法或函数用在自己身上,这就是apply的意思。

<!-- lang: js -->
var array = [3,4];
var sum = add.apply(null,array);//7,用了上面定义的add函数,用数组传递参数
//构造一个包含status成员的对象
var statusObject = {
    status : "A-OK"
};
//statusObject 并没有继承自Quo.prototype,但我们可以在statusObject上调用get_status方法,尽管statusObject并没有
var status = Quo.prototype.get_status.apply(statusObject);//A-OK  

8.参数

当函数被调用时,会得到一个"免费"配送的参数,那就是arguments数组。数组包括传递给形参的参数,也包括没有分配形式参数的多余参数。也就是说传递给形参的参数可以通过两种途径在函数中取得。

<!-- lang: js -->
var sum = function (){
    var sum = 0;
    for(var i = 0; i < arguments.length; i += 1){
        sum += arguments[i];
        
    }
    return sum;
}
document.writeln(sum(1,2,3,4));//10

注意:arguments只是一个类似数组的对象,不是一个真正的数组,只是拥有一个length的属性,但它没有任何数组的方法。书说后面会有它的后果,我们拭目以待。
9.返回

一个函数总会返回一个值,如果没有指定则为undefined。
**如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回this(该新对象)。**这个对应构造器调用模式。
10.异常

(感觉自己很没注意差错控制检验这方面的事情TT) 这一部分跟C++差不多,有throw就要有对应的catch。catch到的error要对应throw出来的东西。例子跑出的是一个对象,则catch的参数对应一个对象,然后把对应的信息输出来。
书上例子先理解下。

<!-- lang: js -->
var add = function (a,b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw {
            name : 'TypeError',
            message : 'add needs number'
        };
    }
    return a + b;
}

var try_it = function () {
    try {
        add("seven");
    } catch (e) {
        document.writeln(e.name + ': ' + e.message);
    }
}

try_it();

11.扩充类型的功能

js能够给语言的基本类型扩充功能。第三章对象的时候,我们可以通过给Object.prototype添加方法,可以让该方法对所有对象都可用。这样的方式对函数,数组,字符串,数字,正则表达式和布尔值都适用。
下面这个扩充在后面会经常用到的喔!

<!-- lang: js -->
Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
}

通过给Function.prototype增加方法来是的该方法对所有函数可用。我们下次给对象增加方法时就不用键入prototype这几个字符,省掉一点麻烦(不是很懂。。其实是不是看起来帅点,优雅一点呢)

因为基本类型的原型是公用结构,所以在类库混用时务必小心。一个保险的做法是指在确定没有该方法时才添加它。

<!-- lang: js -->
Function.prototype.method = function (name, func) {
    if (!this.prototype[name]) {
        this.prototype[name] = func;
    }
    return this;
}

12.递归

递归就是那意思,调用函数自身去解决它的子问题。 不多说,觉得里面一个遍历DOM树的函数写的不错。

<!-- lang: js -->
var walk_the_DOM = function walk(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
        walk(node,func);
        node = node.nextSibling;
    }
}

书里还提到了一个概念叫尾递归优化,意思是如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,它可以显著提高速度。然后又说js没有提供尾递归优化(坑爹呢~~)。
13.作用域

这一方面好像之前看过的几道前端面试题目就有这个的内容,基础要掌握啊!!
重新找回以前的一篇博文来看,才发现以前都没怎么理解,这次看了几个钟,有些收获。墙裂建议看看:http://blog.csdn.net/rommel1/article/details/7804973

总结一下就是,像javascript这样的解释型语言,基本分为两个阶段,编译期与运行期。在预编译阶段,它是用函数来划分作用域,然后逐层为其以 var 声明的变量与函数定义开辟内存空间,再然后对var变量进行特殊处理,统统赋初始值为undefined。 在研究上文的过程中,又看到类属性和实例属性的概念,又找了一篇文章,感觉说的还比较全面。最重要的一点:动态共有属性优于原型属性。即:如果二者都定义了同一属性,则最终的属性值以动态公有属性为准。附链接:http://evanwukong.blog.163.com/blog/static/134836495201141752910780/

看了这些,书里的例子已是浮云。 其他语言是块级作用域,但js不支持,js有的是函数作用域,所以要在顶部声明函数中可能用到的所有变量,而且产生了下面的闭包。 14.闭包

闭包的好处是内部函数可以访问定义他们的外部函数的参数和变量(除了this和arguments) 之前的Quo构造器没什么意义,可以直接访问到的属性为什么要用一个getter方法去访问呢? 所以下面的方式比较推荐。

<!-- lang: js -->
var quo = function (status) {
    return {
        get_status : function () {
            return status;
        }
    };
};
//构造一个quo实例
var myQuo = quo("amazed");
document.writeln(myQuo.get_status());

这个quo无需在前面加new,即使quo已经返回,但get_status仍可以访问到quo对象的status属性。
下面是一个设置DOM节点为黄色,然后把它渐变为白色的函数。

<!-- lang: js -->
var fade = function (node) {
    var level = 1;
    var step = function () {
        var hex = level.toString(16);
        node.style.backgroundColor = '#FFFF' + hex + hex;
        if (level < 15) {
            level += 1;
            setTimeout(step, 100);
        }
    };
    //书中是setTimeout(step, 100);我觉得下面也可以
    step();
};
fade(document.body);

一个糟糕的例子,

<!-- lang: js -->
var add_the_handleers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        alert(i);
    }
};

这个例子的本意是想每一个节点alert出不同的值,,但是由于绑定的数是变量i本身,而不是函数在构造时变量i的值。 改良一下,

<!-- lang: js -->
var add_the_handlers = function (nodes) {
    var helper = function (i) {
        return function (e) {
            alert(i);
        };
    };
    var i;
    for (var i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = helper(i);
    };
}

上面的程序把i通过形参i传递进去,用到的不是外面的变量i本身,所以解决了以上那个问题。
15.回调

说的是概念吧。大家知道ajax的概念就差不多我觉得。 16.模块

利用函数包装成模块,提供接口却隐藏状态与实现的函数或对象。 我们想给String增加deentityify方法,用来寻找字符窜中的HTML字符实体并把他们替换为对应的字符。这就需要保存字符实体及其对应字符。我们不能在全局变量中保存,保存在函数内部又会带来运行时的内耗,以为每次执行该函数时该字面量都会被求值一次。理想方法就是放进闭包里。

<!-- lang: js -->
String.method('deentityify', function () {
    var entity = {
        quot : '"',
        lt : '<',
        gt : '>'
    };
    return function () {
        return this.replace(/&([^&;]+);/g,
            function (a,b) {
                var r = entity[b];
                return typeof r === 'string' ? r : a;
            }
        );
    };
}());
document.writeln('&lt;&quot;&gt;'.deentityify());//<">

注意最后一行的括号,用()运算法立刻调用我们刚刚构造出来的函数。因为返回的是函数,要让调用deentityfy函数执行代码,所以要加括号。 模块模式的一般形式是:一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数,或者把他们保存到一个可访问到的地方。

17.级联

让函数返回this就可以实现优雅的级联。
18.柯里化

就是说一个原本的函数加上一个传递给它的参数,变成一个新的函数,不是很懂有什么用? 19.记忆

这个要根据具体情况来做,比如把一些后面需要用到的结果保存在一个数组,后面用到直接取值,就不用再去计算了。

转载于:https://my.oschina.net/u/614344/blog/117869

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值