心有猛虎,细嗅蔷薇

我家的屋顶

已经记不清从哪个时候起,我停止了写心情笔记。

在记忆中,我还记得以前,刚毕业那会,甚至毕业前,我经常写日记。《音乐里的记忆》、《那人、那雨、那夜》等等文章依然记得。前者是毕业前对于整个大学里的人和事的回忆,而后者则是对那天从网吧回学校,得知我心里的女神早已心有所属的心痛,至今依然记得大雨滂沱……

时间真是可怕,想来已毕业快七年了吧。人都说“七年之痒”,呵呵,此刻也同样如此!

这段时间,发生的事情,比从神州泰岳出走后所发生的事情更复杂,更让我印象深刻,更难忘。我终于“弃暗投明”,做回了真正的自己。在现在这个社会,做自己并不是一件很容易的事,甚至有的人一辈子也做不到这一点!这需要勇气,需要时机,需要熬过那些不情愿扮鬼的漫长的日子,需要很多很多难得的条件……聚在一起,坚持到那一天,临门一脚。

是的,我终于这么做了,我勇敢坚持,担当起来,我做回了自己!

之后,也许会面对更多的人,更多的事情,更复杂纷繁。但那又有什么可怕的呢?我怎么想就怎么做就可以了。

我已成长,但我想我还是以前那个特立独行的孩子,不擅言谈,善良而敏感,坚韧而木讷,从不伤及他人,极力想看到一个和谐,皆大欢喜的世界——理想主义者!

理解 JavaScript Promise

Promise 与事件监听类似,除了以下几点:

  1. 一个 Promise 只能成功或者失败一次。它不能进行从成功到失败的状态转换,反之亦然。
  2. 如果在一个 Promise 成功或者失败后添加了相应的回调函数,那么该回调还是会被立即执行,尽管相关事件早已发生。

Promise 术语

一个 Promise 处于以下四种状态之一。

  • fulfilled:操作成功。
  • rejected:操作失败。
  • pending:操作进行中,还没有得到操作结果。
  • settled:Promise 已经被 fulfilled 或 rejected,且不是 pending。

JavaScript 中的 Promise

Promise 的构造器接受一个函数作为回调函数,同时给回调函数提供了两个参数:resolve 和 reject。

var promise = new Promise(function(resolve, reject) {
    // 类似异步操作的一些逻辑,然后...
    
    if(/*正常直行*/) {
        resolve('Stuff worked!);
    } else {
        reject(Error('It broke'));
    }
});

下面看下如何使用 Promise:

promise.then(function(result) {
    console.log(result); // 执行成功
}, function(err) {
    console.log(err); // Error: '执行失败'
});

与其它库的兼容性

jQuery 中的 Deferred 对象与 Promise/A+ 并不兼容。jQuery 中也有 Promise 类型,但它只是 Deferred 的一个子集。我们可以将它们转换成标准的 Promise,而且最好当它们一出现的时候就进行转换:

var jsPromise = Promise.reslove($.ajax('/whatever.json'));

// jQuery 的 Deferred 的 then 返回了更多的参数
var jqDeferred = $.ajax('/whatever.json');
jqDeferred.then(function(response, statusText, xhrObj) {
    // ...
}, function(xhrObj, textStatus, err) {
    // ...
});

// 转化为 Promise 后只会保留第一个参数,其它参数均会被忽略
jsPromise.then(function(response) {
    // ...
}, function(xhrObj) {
    // ...
});

将 Promise 用于 XMLHttpRequest

// 用 Promise 封装 XMLHttpRequest 实现的简单 Get 方法
function get(url) {
    return new Promise(function(reslove, reject) {
        var req = new XMLHttpRequest();
        req.open('GET', url);
        
        req.onload = function() {
            if (req.status == 200) {
                resolve(req.response);
            } else {
                reject(Error(req.statusText));
            }
        };
        
        req.onerror = function() {
            reject(Error('Network Error'));
        };
        
        req.send();
    });
}

下面是使用方法:

get('story.json').then(function(response) {
    
}, function(error) {
    
});

链式调用

var promise = new Promise(function(resolve, reject) {
    resolve(1);
});

promise.then(function(val) {
    console.log(val);
    return val + 2;
}).then(function(val) {
    console.log(val);
});

// 可以使用这种方式修改之前的代码
get('story.json').then(function(response) {
    return JSON.parse(response);
}).then(function(response) {
    console.log(response);
});
// 再简化
get('story.json').then(JSON.parse).then(function(response) {
    console.log(response); // json 格式的数据
});
// 更好的方式
function getJSON(url) {
    return get(url).then(JSON.parse);
}

异步操作队列

当在“then”方法的回调函数里有返回值的时候,这里有些重要的事情需要我们关注。如果你返回的是一个值,那么下一个“then”会立即调用,并把这个值传递给它的回调函数。如果你返回的是一个“类 Promise”对象,那么下一个“then”方法会等待执行,直到这个 Promise 被解决(成功或失败)后才执行。

getJSON('story.json').then(function(story) {
    return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
    console.log(chapter1);
});

现在同简单的回调模式相比,Promise 开始显示出自己的优势了。可以像下面这样写个获取章节内容的函数:

var storyPromise;

function getChapter(i) {
    // 闭包
    storyPromise = storyPromise || getJSON('story.json');
    return storyPromise.then(function(story) {
        return getJSON(story.chapterUrls[i]);
    });
}

getChapter(0).then(function(chapter) {
    console.log(chapter);
    return getChapter(1);
}).then(function(chapter) {
    console.log(chapter);
});

错误处理

// 注意 then(func1, func2) 和 then(func1).catch(func2) 的区别
// catch 类似于 try/catch ,能够捕获到上一步中的任何异常,其后的
// then 还可以继续执行
asyncThing1().then(function(){
    return asyncThing2();
}).then(function(){
    return asyncThing3();
}).catch(function(err){
    return asyncRecovery1();
}).then(function(){
    return asyncThing4();
}, function(err){
    return asyncRecovery2();
}).catch(function(err){
    console.log("Don't worry about it.");
}).then(function(){
    console.log('All done!');
});

并行和串行

这里是并行和串行的异步操作同时存在的例子:

var sequence = Promise.resolve();

story.chapterUrls.forEach(function(chapterUrl){
    // 在 sequence 后添加如下操作
    sequence = sequence.then(function() {
        return getJSON(chapterUrl);
    }).then(function(chapter){
        addHtmlToPage(chapter.html);
    });
});

Promise.resolve() 会根据传入的参数返回一个 Promise。与之对应的 Promise.reject(val) 方法,会返回一个根据你传入的参数值(或 underfined)为 reject 结果的 Promise。

getJSON('story.json').then(function(story) {
    addHtmlToPage(story.heading);
    
    return story.chapterUrls.reduce(function(sequence, chapterUrl) {
        // 当前一章节的 Promise 完成后
        return sequence.then(function(){
            // 接着获取下一章节
            return getJSON(chapterUrl);
        }).then(function(chapter){
            addHtmlToPage(chapter.html);
        });
    }, Promise.reslove());
}).then(function(){
    addTextToPage("All done.");
}).catch(function(err){
    addTextToPage("Argh, broken: " + err.message);
}).then(function(){
    document.querySelector('.spinner').style.display = 'none';
});

Promise.all 接受一个 Promise 数组作为参数,创建一个所有的 Promise 都 resolve 后才 resolve 的 Promise 的对象,resolve 结果是一个数组,包含了前面所有 Promise 的 resolve 结果,并且顺序和传入时的顺序是完全一致的。

getJSON('story.json').then(function(story) {
    addHtmlToPage(story.heading);
    
    return Promise.all(
        story.chapterUrls.map(getJSON)
    );
}).then(function(chapters){
    chapters.forEach(function(chapter) {
        addHtmlToPage(chapter.html);
    });
}).catch(function(err){
    addTextToPage("Argh, broken: " + err.message);
}).then(function(){
    document.querySelector('.spinner').style.display = 'none';
});

不想等到所有章节都获取到后才显示到页面上:

getJSON('story.json').then(function(story) {
    addHtmlToPage(story.heading);
    
    return story.chapterUrls.map(getJSON) // getJSON 后的 Promise 
        .reduce(function(sequence, chapterPromise){
            return sequence.then(function(){
                return chapterPromise;
            }).then(function(chapter){
                addHtmlToPage(chapter.html);
            });
        }, Promise.resolve());
}).then(function(){
    addTextToPage("All done.");
}).catch(function(err){
    addTextToPage("Argh, broken: " + err.message);
}).then(function(){
    document.querySelector('.spinner').style.display = 'none';
});

Promise 厉害的地方还远不止于此!

CSS预编译工具研究

一、前言

回顾计算机语言的进化史,似乎每隔几年都会诞生一个新的概念以推进计算机语言的发展。从二进制升级到指令集,从指令集再升级到编译语言。而像HTML、CSS和JavaScript这些语言都是通过浏览器来渲染的。HTML、CSS和JavaScript语言以前所未有的方式使WEB获得了巨大的成功。依靠它们,我们建立了更加庞大、更加复杂的网站,这是一件美好的事;但我们需要面对的一点是,我们要更进一步使得我们的代码易于管理和维护。其中,CSS对于易于管理和维护有着迫切的需求。CSS的简单造成了今天其代码的冗余。现在CSS准备华丽的转身了。

CSS预处理器(CSS Preprocessor),是一种构架于CSS之上的高级语言,可以通过脚本编译生成CSS代码,其目的是为了让CSS开发者的工作更简单有趣,当前已经进入了较为成熟的阶段,基本上新的WEB开发项目大都已普遍使用。

现在最主要的选择有:

  • SASS:2007年诞生,最早也是最成熟的CSS预处理器,拥有ruby社区的支持和compass这一最强大的css框架,目前受LESS影响,已经进化到了全面兼容CSS的SCSS。
  • LESS:2009年出现,受SASS的影响较大,但又使用CSS的语法,让大部分开发者和设计师更容易上手,在ruby社区之外支持者远超过SASS,其缺点是比起SASS来,可编程功能不够,不过优点是简单和兼容CSS,反过来也影响了SASS演变到了SCSS的时代,著名的Twitter Bootstrap就是采用LESS做底层语言的。
  • Stylus:2010年产生,来自Node.js社区,主要用来给Node项目进行CSS预处理支持,在此社区之内有一定支持者,在广泛的意义上人气还完全不如SASS和LESS。

以上就是CSS预编译工具的一些介绍,下面我将对SASS和LESS的使用进行讲解,并分别提供一个简单的示例。

二、LESS

1. 变量

注意,由于变量只能定义一次,其本质就是“常量”。

2. 混合(Mixin)

混合是一种将一个规则集混入另一个规则集的方法。

.bordered {
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}
#menu a {
  color: #111;
  .bordered;
}

.post a {
  color: red;
  .bordered;
}

3. 嵌套规则

#header {
  color: black;
}
#header .navigation {
  font-size: 12px;
}
#header .logo {
  width: 300px;
}
#header {
  color: black;
  .navigation {
    font-size: 12px;
  }
  .logo {
    width: 300px;
  }
}

还可以使用父选择器选择符:

.clearfix {
  display: block;
  zoom: 1;

  &:after {
    content: " ";
    display: block;
    font-size: 0;
    height: 0;
    clear: both;
    visibility: hidden;
  }
}

4. 运算

任何数字、颜色或者变量都可以参与运算。下面是一组案例:

@base: 5%;
@filler: @base * 2;
@other: @base + @filler;

color: #888 / 4;
background-color: @base-color + #111;
height: 100% / 2 + @filler;

5. 函数

Less 内置了多种函数用于转换颜色、处理字符串、算术运算等。这些函数在函数手册中有详细介绍。

函数的用法非常简单。下面这个例子将介绍如何将 0.5 转换为 50%,将颜色饱和度增加 5%,以及颜色亮度降低 25% 并且色相值增加 8 等用法:

@base: #f04615;
@width: 0.5;

.class {
  width: percentage(@width); // returns `50%`
  color: saturate(@base, 5%);
  background-color: spin(lighten(@base, 25%), 8);
}

6. 命名空间和访问器

#bundle {
  .button {
    display: block;
    border: 1px solid black;
    background-color: grey;
    &:hover {
      background-color: white
    }
  }
  .tab { ... }
  .citation { ... }
}

使用方法:

#header a {
  color: orange;
  #bundle > .button;
}

7. 作用域

Less中的作用域和其他语言的类似,如果在本身的作用域中找不到变量,将会从其父作用域中查找。

@var: red;

#page {
  @var: white;
  #header {
    color: @var; // white
  }
}

变量和混合不必在使用前声明,也可以放在后面:

@var: red;

#page {
  #header {
    color: @var; // white
  }
  @var: white;
}

8. 注释

/* 一个注释块
style comment! */
@var: red;

// 这一行被注释掉了!
@var: white;

9. 导入

你可以导入一个 .less 文件,此文件中的所有变量就可以全部使用了。如果导入的文件是 .less 扩展名,则可以将扩展名省略掉:

@import "library"; // library.less
@import "typo.css";

10. 其他

Mixin Guards、CSS Guards、Loop、Merge、函数式Mixin

三、SASS

1. 变量

$font-stack:    Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}

2. 嵌套

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

属性嵌套:

$fontsize: 12px
.speaker
  .name
    font:
      weight: bold
      size: $fontsize + 10px
  .position
    font:
      size: $fontsize 

3. 分离

可以将你的样式表分隔成很多个单独的文件。然后,你可以在主样式文件中通过@import引入你所需要的.scss文件。

4. 导入

// _reset.scss

html,
body,
ul,
ol {
   margin: 0;
  padding: 0;
}
/* base.scss */

@import 'reset';

body {
  font-size: 100% Helvetica, sans-serif;
  background-color: #efefef;
}

5. 混合(Mixins)

@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
     -moz-border-radius: $radius;
      -ms-border-radius: $radius;
          border-radius: $radius;
}

.box { @include border-radius(10px); }

6. 扩展和继承

.message {
  border: 1px solid #ccc;
  padding: 10px;
  color: #333;
}

.success {
  @extend .message;
  border-color: green;
}

.error {
  @extend .message;
  border-color: red;
}

.warning {
  @extend .message;
  border-color: yellow;
}

7. 运算

.container { width: 100%; }

article[role="main"] {
  float: left;
  width: 600px / 960px * 100%;
}

aside[role="complimentary"] {
  float: right;
  width: 300px / 960px * 100%;
}

四、简单示例

JavaScript 高程设计——面向对象的程序设计(一)

理解对象

ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”

属性类型

ECMA-262 第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262 定义这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。

ECMAScript 中有两种属性:数据属性访问器属性

1.数据属性

数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true

  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true

  • [[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true

  • [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined

使用 ECMAScript 5 的 Object.defineProperty() 方法修改属性默认的特性。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descrip-tor)对象的属性必须是:configurableenumerablewritablevalue

var person = {};
Object.defineProperty(person, "name", {    
    writable: false,
    value: "Nicholas"
});

//"Nicholas"
alert(person.name);
person.name = "Greg";
//"Nicholas"
alert(person.name);

2. 访问器属性

访问器属性不包含数据值;它们包含一对儿 gettersetter 函数。在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下4个特性。

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为 true

  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为 true

  • [[Get]]:在读取属性时调用的函数。默认值为 undefined

  • [[Set]]:在写入属性时调用的函数。默认值为 undefined

创建对象

工厂模式

function createPerson(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    };    
    return o;
}
        
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

person1.sayName();   //"Nicholas"
person2.sayName();   //"Greg"

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

构造函数模式

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };    
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();   //"Nicholas"
person2.sayName();   //"Greg"

alert(person1 instanceof Object);  //true
alert(person1 instanceof Person);  //true
alert(person2 instanceof Object);  //true
alert(person2 instanceof Person);  //true

alert(person1.constructor == Person);  //true
alert(person2.constructor == Person);  //true

alert(person1.sayName == person2.sayName);  //false 
// 以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建 Function 新实例的机制仍然是相同的

任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过 new 操作符来调用,那它跟普通函数也不会有什么两样。

原型模式

使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

function Person(){
}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
person1.sayName();   //"Nicholas"

var person2 = new Person();
person2.sayName();   //"Nicholas"

alert(person1.sayName == person2.sayName);  //true

// // 内部都有一个指向Person.prototype的指针,因此都返回了true
alert(Person.prototype.isPrototypeOf(person1));  //true
alert(Person.prototype.isPrototypeOf(person2));  //true

//only works if Object.getPrototypeOf() is available
if (Object.getPrototypeOf){
    alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
    alert(Object.getPrototypeOf(person1).name);  //"Nicholas"
}

prototype 属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。

通过使用 hasOwnProperty() 方法,什么时候访问的是实例属性,什么时候访问的是原型属性就一清二楚了。

要取得对象上所有可枚举的实例属性,可以使用 EC-MAScript 5 的 Object.keys() 方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

如果你想要得到所有实例属性,无论它是否可枚举,都可以使用 Object.getOwnPropertyNames() 方法。

var keys = Object.keys(Person.prototype);
//"name,age,job,sayName"
alert(keys);

var keys1 = Object.getOwnPropertyNames(Person.prototype); 
//"constructor,name,age,job,sayName" 
alert(keys1);

更简单的原型语法

function Person(){
}

Person.prototype = {
    constructor : Person, 
    name : "Nicholas",
    age : 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

//重设构造函数,只适用于ECMAScript 5兼容的浏览器 
Object.defineProperty(Person.prototype, "constructor", {     
    enumerable: false,     
    value: Person 
});  

var friend = new Person();

alert(friend instanceof Object);  //true
alert(friend instanceof Person);  //true
alert(friend.constructor == Person);  //false
alert(friend.constructor == Object);  //true

原型对象的问题

原型模式的最大问题是由其共享的本性所导致的。对于包含引用类型值的属性来说,会导致创建的对象共享同一个属性,其中一个修改其他的实例也会被修改。

JavaScript 高程设计——引用类型(二)

RegExp 类型

ECMAScript 通过 RegExp 类型来支持正则表达式。使用下面类似 Perl 的语法,就可以创建一个正则表达式。

var expression = / pattern / flags ;

正则表达式的匹配模式支持下列3个标志(flags):g ——全局,i ——不区分大小写,m ——匹配多行。

// 匹配字符串中所有"at"的实例
var pattern1 = /at/g;
// 匹配第一个"bat"或"cat",不区分大小写
var pattern2 = /[bc]at/i;
// 匹配所有以"at"结尾的3个字符的组合,不区分大小写
var pattern3 = /.at/gi;
// 与pattern1相同,只不过是使用构造函数创建的
var pattern4 = new RegExp("[bc]at", "i");

由于 RegExp 构造函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义过的字符也是如此。

使用正则表达式字面量和使用 RegExp 构造函数创建的正则表达式不一样。在 ECMAScript 3 中,正则表达式字面量始终会共享同一个 RegExp 实例,而使用构造函数创建的每一个新 RegExp 实例都是一个新实例。

ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次都创建新的 RegExp 实例。

RegExp 实例属性

RegExp 的每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息。

  • global:布尔值,表示是否设置了g标志。
  • ignoreCase:布尔值,表示是否设置了i标志。
  • lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起。
  • multiline:布尔值,表示是否设置了m标志。
  • source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。

RegExp 实例方法

RegExp 对象的主要方法是 exec(),该方法是专门为捕获组而设计的。exec() 接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回 null。返回的数组虽然是 Array 的实例,但包含两个额外的属性:indexinput 。其中,index 表示匹配项在字符串中的位置,而 input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串。

正则表达式的第二个方法是 test(),它接受一个字符串参数。在模式与该参数匹配的情况下返回 true;否则,返回 false

尽管 ECMAScript 中的正则表达式功能还是比较完备的,但仍然缺少某些语言(特别是 Perl)所支持的高级正则表达式特性。

Function 类型

每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

在函数内部,有两个特殊的对象:argumentsthis。其中,arguments 是一个类数组对象,包含着传入函数中的所有参数。虽然 arguments 的主要用途是保存函数参数,但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。

arguments.callee

this 引用的是函数据以执行的环境对象——或者也可以说是 this 值。

ECMAScript 5 中 caller 属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null

当函数在严格模式下运行时,访问 arguments.callee 会导致错误。ECMAScript 5 还定义了 arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是 undefined。严格模式还有一个限制:不能为函数的 caller 属性赋值,否则会导致错误。

ECMAScript 中的函数包含两个属性:length和proto-type。其中,length属性表示函数希望接收的命名参数的个数。对于 ECMAScript 中的引用类型而言,prototype 是保存它们所有实例方法的真正所在。

在 ECMAScript 5 中,prototype 属性是不可枚举的,因此使用 for-in 无法发现。

每个函数都包含两个非继承而来的方法:apply()call() 。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。

function callSum1(num1, num2){    
    // 传入arguments对象    
    return sum.apply(this, arguments);
}

function callSum2(num1, num2){    
    // 传入数组    
    return sum.apply(this, [num1, num2]);
}

call() 方法与 apply() 方法的作用相同,它们的区别仅在于接收参数的方式不同。对于 call() 方法而言,第一个参数是 this 值没有变化,变化的是其余参数都直接传递给函数。

事实上,传递参数并非 apply()call() 真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域。

ECMAScript 5 还定义了一个方法:bind()。这个方法会创建一个函数的实例,其 this 值会被绑定到传给 bind() 函数的值。

基本包装类型

ECMAScript 提供了3个特殊的引用类型:Boolean、Number 和 String。

单体内置对象

ECMA-262 还定义了两个单体内置对象:Global 和 Math。Global 对象的 encodeURI()encodeURIComponent() 方法可以对 URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。

Math.random() 方法返回介于0和1之间一个随机数,不包括0和1。