《JavaScript语言精粹 修订版》 读书笔记

以前看到这篇文章, 前端网老姚浅谈:怎么学JavaScript?,说到怎么学习JavaScript,那就是 看书、分析源码。
10本书读2遍的好处,应该大于一本书读20遍。
看书主动学习,看视频是被动学习。
看书和分析源码的时机。但已经工做一年半载时,正是提升的好时候,此时能够去看书了。全面系统的梳理知识点,扫清本身的盲区。若是只是靠项目经验是不够的,经过项目来学习,那>确定是必须的,工做自己就是一个学习的过程。
怎么把一本书看完呢?很简单,敲。文字加代码都敲。
比较认同老姚的说法。去年毕业到如今,我也算是工做一年了,是时候看书查缺补漏了。

因而我就先把这本薄的经典书《JavaScript语言精粹 修订版》豆瓣读书本书简介(总共10章,除去附录,才100页),读完并记录了一些笔记。基本算是摘抄书本的,本身联想到了一些知识和资料也扩展了一下。整体写下来近一万字。读书笔记还能够分享给别人看。回顾时,书不在身边还能够看看本身的笔记。想一想这类经典书记一遍动手敲一遍也是很值得的。不过这读书笔记中可能会有一些错别字,阅读时若是发现欢迎指正。html

第1章 精华

大多数语言都有精华和糟粕。JavaScript使人诡异的事情是,在对这门语言没有的太多了解,甚至对编程都没有太多了解的状况下,你也能用它来完成工做。
看到这里不由想起:前端

张鑫旭大牛在 《我对知乎前端相关问题的十问十答》
非计算机专业背景学习JS要点有这一条:
全部继承和原型相关内容跳过,注意,是跳过,不要看!没有这些JS同样能够活得很好,你的平常工做同样玩得飞起,固然,你没忍住看了相关知识也不要紧,由于你会发现本身看不懂的;

JavaScript的函数是(主要)基于词法做用域的顶级对象。node

译注: JavaScript中的函数是根据词法来划分做用域的,而不是动态划分做用域的。具体内容参见《 JavaScript权威指南》中译第5版相关章节“8.8.1 词法做用域”。
JavaScript有很是强大的对象字面量表示法。这种表示法是JSON的灵感来源。
原型继承是JavaScript中一个有争议的特性。

ECMAScript编程语言》第3版定义了JavaScript的标准。
ES3标准
扩展:颜海镜大牛整理的ES3中文版
颜海镜大牛整理的ES5中文版
W3c ES5中文版
阮一峰大牛的书籍《ES6标准入门2》
更多内容可参见这篇文章:ECMAScript 2018 标准导读git

一个简单的例子:es6

Function.prototype.method = function(name, func) {
  this.prototype[name] = func;
  return this;
}

书中贯彻始终都会用到这个method方案,做者将会在第4章解释它。github

第2章 语法

本章主要用铁路图(语法图)表示语法。
主要有:空白、标识符、数字、字符串、语句、表达式、字面量、函数。
typeof 运算符产生的值有'number', 'string','boolean','undefined','function','object'。若是运算数是一个数组或者是null,那么结果是'object',这实际上是不对的。正则表达式

第3章 对象

JavaScript简单数据类型包括数字、字符串、布尔值,null值和undefined值。其余全部值都是对象。
数组、字符串和布尔值“貌似”对象,由于它们拥有方法(包装对象),但它们是不可变的。
对象是属性的容器,其中每一个属性都拥有名字和值。属性名能够是包括空字符串在内的全部字符串,属性值能够是除了undefined值以外的任何值。编程

JavaScript包含一种原型链的特性,容许对象继承到另外一个对象的属性。正确地使用它能减小对象初始化时的消耗的时间和内存。
检索
.,[]两种检索方式,推荐点.表示法。
尝试重undefined的成员属性中取值将会致使TypeError异常,这时能够经过&&来避免错误。
更新
若是属性名已经存在对象里。那么属性的值会被替换。若是以前没有拥有那个属性名,那么该属性将被扩充到对象中。
引用
对象经过引用来传递。它们永远不会被复制。
原型
全部经过对象字面量建立的对象都连接到Object.prototype
建立新对象时,能够选择某个对象做为它的原型。segmentfault

if (typeof Object.create !== 'function') {
  Object.create = function(o) {
    var F = function () {};
    F.prototype = o;
    return new F();
  };
}

原型链接只有在检索值的时候才被用到。若是尝试去获取对象的某个属性值,但对象没有此属性名,那么JavaScript会试着从原型对象中获取属性值。若是那个原型对象也没有该属性,那么再从它的原型中寻找,依此类推,直到该过程最后达到终点Object.prototype。若是想要的属性彻底不存在原型链中,那么结果就是 undefined值。这个过程称为委托
原型关系是一种动态的关系。
反射
原型链上的全部属性都会产生值。有两种方案能够处理掉对象上不须要的属性。
①程序检查时丢弃值为函数的属性。但有可能有些值确实是函数,因此该方法不可靠。
②使用hasOwnProperty方法,若是是对象拥有独有的属性,则返回true。该方法不会检查原型链。
枚举
for in能够遍历一个对象中全部的属性名。但包含函数和一些不关心的原型中属性。并且顺序不肯定,能够用 hasOwnProperty方法和typeof排除函数。
for 循环不会出现for in那些状况。
删除
delete运算符能够用来删除对象的属性。
减小全局变量的污染
能够把全局性的资源归入一个名称空间之下。这样作能减小冲突。数组

第4章 函数

函数用于①代码复用②信息隐藏③组合调用。通常来讲,所谓编程,就是将一组需求分节成一组函数与数据结构的技能。
JavaScript的函数就是对象。
函数对象链接到Function.prototype(该原型对象自己链接到Object.prototype)。
每一个函数在建立时会附加两个隐藏属性,函数的上下文和实现函数行为的代码。
每一个函数对象在建立时也随配有一个prototype属性。它的值是一个拥有constructor属性且值为该函数的对象。
函数字面量
函数字面量包括4个部分。①保留字function②函数名,能够省略,③一组参数④一组语句。
函数字面量能够出如今任何容许表达式出现的地方。一个内部函数除了能够访问本身的参数和变量,同时也能够自由访问把它嵌套在其中的父函数的参数和变量。经过函数字面量建立的函数对象包含一个链接到外部上下文的链接。这被称为闭包
调用
除了声明时定义的形式参数,每个函数还接收两个附加的参数:thisargument。在JavaScript中一共有四种调用模式。①方法调用模式,②函数调用模式③构造器调用模式④apply调用模式。

this指向问题一直困扰不少人。我通常是这样记的,谁调用this就指向谁。)

方法调用模式
对象的方法执行,this指向该对象。好比:

var myObj = {
  value: 0,
  showValue: function() {
    console.log('value:', this.value);
  }
}
myObj.showValue();  // value: 0

函数调用模式

var add = function(a,b) {
    return a + b;
}
add(3,4);  //7
window.add(3,4);  //7
// 这种this被绑定到全局对象(window)。
// 能够理解是window.add(3,4);

有种简单的办法就是var that = this;this存储下。
例:

var myObj = {
  value: 0,
  age: 20,
  showValue: function() {
    console.log('value:',this.value);
    var  that = this;
    var showAge = function() {
        // window上没有age,因此是undefined
        console.log('这里的this是window ---age:', this.age);  // undefined
        console.log('age:', that.age);  // 20
     }
     showAge();
  }
}
myObj.showValue();  // 0, undefined,

构造器调用模式
JavaScript是一门基于原型继承的语言。
若是在一个函数前面带上new 来调用。那么背地利将会建立一个链接到该函数的prototype成员的新对象,同时this会被绑定到那个新对象上。
new 前缀也会改变return 语句的行为。
例:

var Quo = function (string) {
  this.status = string;
}
Quo.prototype.get_status = function () {
  return this.status; 
}
var myQuo = new Quo('confused'); // 'confused'

一个函数,若是建立的目的就是但愿结合new 前缀来调用。那么它就被称为构造器函数。按照约定,它们保存在以大写函数命名的变量里。若是调用构造器函数时没有在前面加上new,可能会发生很是糟糕的事情,既没有编译时的警告,也没有运行时广告,因此大写约定很是重要。
做者不推荐这种形式的构造器函数。有更好的替代方式。
Apply调用模式
JavaScript是一门函数式的面向对象编程语言,因此对象能够拥有方法。
apply方法让咱们构建一个参数数组传递给调用函数,它也容许咱们选择this的值。
参数
arguments,虽然拥有length属性,但不是真正的数组。而是相似数组(array-like)的对象。
返回
return 可用来是函数提早返回。当return 被执行时,函数当即返回而再也不执行余下的语句。
一个函数总会返回一个值,若是没指定,那就是返回undefined值。
若是函数调用时在前面加上了new 前缀,且返回值不是一个对象,则返回this(该新对象)。
异常
JavaScript提供了一套异常处理机制。
throw语句和try catch,try catchfinally是可选的。
扩展类型的功能
JavaScript容许给语言的基本类型扩充功能。在第3章中咱们已经看到,能够经过Object.prototype添加方法,可让该方法对全部对象均可用。这样的方式对函数、数组、字符串、数字、正则表达式和布尔值一样适用。

例如:

Function.prototype.method = function () {
  this.prototype[name]  = func;
  return this;
}

基本类型的原型是公用结构,因此在类库混用时务必当心。一个保险的作法就是只在确认没有该方法时才添加它。

Function.prototype.methods = function(name, func) {
  if (!this.prototype[name]) {
      this.prototype[name] = func;
  }
  return this;
}

递归
递归函数就是会直接或间接地调用自身的一种函数。递归是一种强大的编程技术,递归是用通常的方式去解决每个子问题。书中举了一个汉诺塔的例子,是程序设计中经典递归问题。详细说明能够参见 百度百科“汉诺塔”词条
一些语言提供了尾递归优化。尾递归是一种在函数的最后执行调用语句的特殊形式的递归。参见Tail call。 ES6版本扩展了尾递归。参见阮一峰老师的《ES6标准入门》中的尾调用优化
做用域
在编程语言中,做用域控制着变量与参数的可见性和声明周期。
书中指出当前JavaScript没有块级做用域。由于没有块级做用域,因此最好的作法是在函数体的顶部声明函数中可能用到的全部变量。不过ES6扩展了有块级做用域。
闭包
做用域的好处是内部函数能够访问定义它们的外部函数的参数和变量(除了thisarguments)。
例子:

<ul class="list">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
// 点击相应节点时,显示对应的序号。可使用闭包来解决。
var add_the_handlers = function() {
    var helper = function(i) {
        return function(e) {
            alert(i);
        }
    }
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = helper(i);
    }
}
// 扩展 另外能够用let i = 0,或者把nodes类数组转成数组等方案实现。
// 闭包特性:一、函数内再嵌套函数,二、内部函数能够调用外层的参数和变量,三、参数和变量不会被垃圾回收机制回收。
// 闭包优势 灵活和方便,便于封装。缺点:空间浪费、内存泄露、性能消耗。

回调
发起异步请求,提供一个当服务器响应到达时随即出发的回调函数。异步函数当即返回,这样客户端就不会被阻塞。
模块
咱们可使用函数和闭包来构造模块。模块是一个提供接口却隐藏状态与实现的函数或对象。
举例:给String添加一个deentityify方法。它的任务是寻找字符串中的HTML字符实体并把它们替换成对应的字符。

String.method('deentityify', function () {
    // 字符实体表。它映射字符实体的名字到对应的字符。
    var entity = {
        quot: '"',
        lt: '<',
        gt: '>'
    };
    // 返回 deentityify方法
    return function () {
        return this.replace(/&([^&;]+);)/g,
        function (a,b) {
            var r = entity[b];
            return typeof r === 'string'? r : a;
        }
    };
}());

模块模式利用了函数做用域和闭包来建立被绑定对象与私有成员的关联,在上面例子中,只有deentityify方法有权访问字符实体表这个数据对象。
模块模式的通常形式是:一个定义了私有变量和函数的函数;利用闭包建立能够访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个能够访问的地方。
使用模块模式就能够摒弃全局变量的使用。它促进了信息隐藏和其余优秀的设计实践。对于应用程序的封装,或者构造其余单例对象,模块模式很是有效。

单例译注
模块模式一般结合单例模式使用。 JavaScript的单例就是用对象字面量表示法建立的对象,对象的属性值能够是数值或函数,而且属性值在该对象的生命周期中不会发生变化。更多内容参见: 单例模式

级联
有一些方法没有返回值。若是咱们让这些方法返回this而不是undefined,就能够启用级联。
在一个级联中,咱们能够在单独一条语句中依次调用同一个对象的不少方法。好比jQuery获取元素、操做样式、添加事件、添加动画等。
柯里化
柯里化,是把多参数函数转换为一系列单参数函数并进行调用的技术。更多详情可参见:柯里化
函数也是值。柯里化容许咱们把函数与传递给它的参数相结合,产生一个新的函数。

var add1 = add.curry(1);
document.writeln(add1(6));

JavaScript并无curry方法,但能够扩展该功能。
arguments不是真正的数组,因此使用了Array.prototype.slice方法。

Function.method('curry',function(){
    var slice = Array.prototype.slice,
    args = slice.apply(arguments),
    that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    }
});

记忆
函数能够将先前操做的结果记录在某个对象里,从而避免无谓的重复运算。这种优化称做记忆。
好比说,咱们想要一个递归函数来计算Fibonacci(斐波那契)数列,它的特色是,前面相邻两项之和等于后一项的值。更多参考:斐波那契。最前面两个数字是0和1。

var fibonacci = function() {
    return n < 2? n : fibonacci(n-1) + fibonacci(n-2);
}

这样虽然能完成工做,但它作了不少无谓的工做。
构造一个带有记忆功能的函数:

var memoizer = function(mome, formula) {
    var recur = function(n) {
        var result = meno[n];
        if (typeof result !== 'number') {
            result = formula(recur, n);
            meno[n] = result;
        }
        return result;
    };
    return recur;
}

再用这个memoizer函数来定义fibonacci函数,提供其初始的memo数组和formula函数。

var fibonacci = memoizer([0,1],function(recur, n){
    return recur(n-1) + recur (n-2);
})

极大的减小了咱们的工做量。例如要产生一个记忆的阶乘函数,只须要提供基本的阶乘公式便可:

var factorial = meoizer([1,1], function(recur, n){
    return n * recur(n-1);
});

第5章 继承

伪类
JavaScript的原型存在诸多矛盾。它不直接让对象从其余对象继承,反而插入了一个多余的间接层:经过构造器函数产生对象。
Function构造器产生的函数对象会运行相似这样的一些代码:

this.prototype =  {constructor:this}

新函数对象被赋予一个prototype属性,这个prototype对象是存放继承特征的地方。

当采用构造器调用模式,即用new前缀去调用一个函数时,函数执行的方式会被修改。若是new 运算符是一个方法而不是一个运算符,它可能像这样执行:

Function.method('new',function(){
    // 建立一个新对象,它继承自构造器函数的原型对象。
    var that = Object.create(this.prototype);
    // 调用构造器函数,绑定 -this- 到新对象上。
    var other = this.apply(that,arguments);
    // 若是它的返回值不是一个对象,就返回该新对象。
    return (typeof other === 'object' && other) || that;
});

全部构造器函数都约定命名成大写字母。一种更好的备选方案就是根本不使用new
对象说明符
就是指传多个参数时,能够直接传递一个对象。
原型
能够用Object.create方法构造出更多实例来。
函数化
迄今为止,咱们所看到的继承模式的一个弱点就是无法保护隐私。对象的全部属性都是可见的。咱们没法获得私有变量和私有函数。
幸运的是,咱们有一个更好的选择,那就是应用模块模式
咱们从构造一个生成对象的函数开始。咱们以小写字母开头来命名。
该函数包括如下四个步骤
一、建立一个新对象。
二、有选择地私有实例变量和方法。
三、给这个新对象扩充方法。
四、返回那个新对象。
如下是一个函数化构造器的伪代码模板

var constructor = function (spec, my) {
    var that, 其余的私有实例变量;
    my = my || {};
    把共享的变量和函数添加到my中
    that = 一个新对象
    添加给 that 的特权方法
    return that;
}

函数化模式有很大的灵活性。它相比伪类模式不只带来的工做更少,还让咱们更好的封装和信息隐藏,以及访问父类方法的能力。
部件
咱们能够从一套部件中把对象组装出来。

第6章 数组

数组是一段线性分配的内存,它经过整数计算偏移并访问其中的元素。
数组是一种性能出色的数据结构。不幸的是,JavaScript没有像此类数组同样的数据结构。
数组字面量
对象字面量
数组继承了Array.prototype大量有用的方法。而对象字面量是继承自Object.prototype
数组有length属性,而对象没有。
长度
每一个数组都有一个length属性。
能够直接设置length的值。设置更大的length不会给数组分配更多的空间,而设小致使全部下标大于等于新length的属性被删除。

var arr = [1,2,3];
arr.length = 1;
console.log(arr) // [1]

也能够经过length来经过添加值

var arr = [1,2,3];
arr[arr.length] = 4;
console.log(arr) // [1,2,3,4]

有时用push方法更方便。
删除
因为JavaScript的数组也是对象,因此delete运算符能够用来从数组中移出元素。移除后,长度不变,原位置上变成了undefined
可使用Array.prototype.splice方法删除数组。
枚举
JS数组就是对象,因此for in语句能够用来遍历数据的全部属性。
不过for in没法保证属性顺序。而且可能从原型链中获得意外的属性。
for循环能够避免以上问题。
容易混淆的地方

typeof [] === "object"; // true
typeof {} === "object"; // true

识别是不是数组。

// 方法1、
var is_array = function (value) {
  return value && typeof value === 'object' && value.constructor === Array;
};

但它在识别从不一样窗口(window)或帧(frame)里的构造的数组时会失败。
有一个更好的方式:

// 方法2、
var is_array = function (value) {
  return Object.prototype.toString.apply(value) === '[object Array]';
}

扩展
ES5 提供了Array.isArray()的方法。不过兼容性是IE9+。
要作到兼容,能够用以下方法。MDN上提供的。MDN Array.isArray

// 方法3、
if (!Array.isArray){
  Array.isArray = function(arg){
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
var arr = [];
// 方法四
arr instanceof Array;
// 方法五
Array.prototype.isPrototypeOf(arr);
// 方法六
Object.getPrototypeOf(arr) === Array.prototype;

方法4、instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
方法5、isPrototypeOf() 方法用于测试一个对象是否存在于另外一个对象的原型链上。
方法6、[Object.getPrototypeOf() 方法返回指定对象的原型(即, 内部[[Prototype]]属性的值)。](https://developer.mozilla.org...
小结:除了方法2、三外,面对复杂的环境,其余的都不能准确的判断是不是数组。
方法
JavaScript提供了一套数组可用的方法,这些方法是被存储在Array.prototype中的函数。
Object.prototype是能够扩充的。
Array.prototype也是能够扩充的。
ES5中提供的Object.create方法。这方法用在数组是没有意义的,由于它产生的是一个对象,而不是一个数组,产生的对象将继承这个数组的值和方法,但它没有length特殊属性。
指定初始值
JavaScript的数组一般不会预设值。书中写了一个循环来扩展,生成初始值。
扩展:ES6中提供了fill来填充。好比:

['a','b','c'].fill(0);   // [0,0,0]
new Array(3).fill(0);   // [0,0,0]

// fill方法还能够接受第2、第三个参数,用于指定填充的起始位置和结束位置(不包含)。
new Array(3).fill(0,1,2); // [ ,0, ,]  空位不是undefined。空位没有任何值。ES6则是明确将空位转为undefined。

第7章 正则表达式

正则表达式对字符串中的信息实现查找、替换和提取操做。
可处理正则表达式的方法有regexp.execregexp.teststring.matchstring.searchstring.split。一般来讲,正则相较于等效的字符串处理有着显著的性能优点。

一个例子

// 正则表达式必须写在一行中
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
var url = "http://www.ora.com:80/goodparts?q#fragment";
var result = parse_url.exec(url);
// ……

依次匹配到的是:

url: 'http://www.ora.com:80/goodparts?q#fragment',
scheme: 'http',
slash: '//'
host: 'www.ora.com'
port:'80'
path:'goodparts'
query: 'q'
hash: 'fragment'

我的扩展:这里推荐 在线测试正则表达式的网站regex101,默认是PHP语言,选择JavaScript语言。
在线图形化RegExp工具
MDN RegExp.prototype.exec()
大概解释下这个正则,
这里的^ 起始位置,$结束位置
() 分组捕获 ?:不捕获
.表示除换行之外的任意单个字符,对于码点大于0xFFFFUnicode字符,点(.)不能识别(ES6中加u修饰符才可识别),+表示一个或多个,*表示零个或多个,?表示0个或一个。[]表示或者,里面符合一个便可。
\d表示数字0-9
不严谨的正则表达式是一个常见的安全漏洞的发源地。在执行某些匹配时,嵌套的正则表达式也能致使极其恶劣的性能问题。所以简单是最好的策略。

再看一个 匹配数字的例子。

var parse_number = /^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i;
parse_number.test('1'); // true
parse_number.test('number'); // false
parse_number.test('98.6'); // true
parse_number.test('132.21.86.100'); // false
parse_number.test('123.45E-67'); // true
parse_number.test('123.45D-67'); // false

结构
有两个方法来建立一个RegExp对象。优先考虑的是正则表达式字面量,还有一种方式是new RegExp('','g')
正则表达式标识:g全局(匹配屡次,不一样的方法对g标识的处理防范各不相同),i忽略大小写。m多行
元素
正则表达式分支
|表示或,也表示分支 好比:

'info'.match(/in|int/)  // 匹配成功,["in", index: 0, input: "info"]

正则表达式序列
一个正则表达式序列饱和一个或多个正则表达式因子。每个因子能选择是否跟随一个量词,这个量词决定着这个因子被容许出现的次数,若没指定,这个因子则只匹配一次。
正则表达式因子

\ / [ ] () { } ? + * | . ^ $

正则表达式转义
\ 表转义 \f 分页 \n 换行 \r回车 \t 制表
\u 容许制定一个 Unicode 字符来表示一个十六进制的常量。
\d 等同于[0-9] D 取反等同于 [^0-9]
\s Unicode 空白符一个不彻底子集。 S 与s相反
\w [0-9A-Z_a-z] W 与其相反 [^0-9A-Z_a-z]
\b 表示 字边界
\1 表示 分组1所捕获的文本的一个引用,因此它能被再次匹配。
\2 表示 指向分组2的引用,\3 是表示分组3的引用,以此类推。
正则表达式分组
捕获型 ()
非捕获型?:
向前正向匹配?=
有一个(?=前缀。它相似于非捕获类型分组,但在这个组匹配后,文本会倒回到它它开始的地方,实际上并不匹配任何东西。也能够理解为匹配位置。
向后负向匹配
有一个(?!前缀。它相似于向前正向匹配分组,但只有当它匹配失败时它才继续向前进行匹配。这不是一个好的特性。
正则表达式字符集
正则表达式字符集是一种指定一组字符的便利方式。例如,要匹配一个元音字母,(?:a|e|i|o|u),能够方便的写成[aeiou]
类提供另外两个便利:①指定字符范围
因此,一组由32ASCII的特殊组合,能够写成[!-\/:-@\[-{-~]`
②类的取反
取反

[^!-\/:-@\[-`{-~]

正则表达式字符转义
字符类内部的转义规则和正则表达式因子的相比稍有不一样。下面是在字符类中须要被转义的特殊字符。

- / [ \ ]

正则表达式量词
量词后缀决定正则表达式因子应该被匹配的次数。
{3}三次
{3,6} 三、四、五、6次
{3,}3次或更屡次
?等同于{0,1}*等同于{0,}+等同于{1,}

第8章 方法

Array

array.concat(item...)
concat 方法产生一个新数组,它包含一份array的浅复制并把一个或多个参数item附加在其后。若是item是数组,那么每一个元素分别被添加。后面有和它功能相似的array.push(item...)方法。

var a = ['a','b','c'];
var b = ['x','y','z'];
var c = a.concat(b, true);
// c => ['a','b','c','x','y','z',true]

扩展: ES6 有更便捷的扩展运算符...

var a = ['a','b','c'];
var b = ['x','y','z'];
var c = [...a,true,...b];   // ["a", "b", "c", true, "x", "y", "z"]

array.join(separator)
join方法把一个array构形成一个字符串。
separator 默认值就是逗号','
若是你想把大量的字符串片断组装成一个字符串,把这些片断放在一个数组中,并用join方法链接起来一般比用+元素运算符链接起来要快。

译注:对于 IE6/7,使用join链接大量字符串效率确实优于加号运算符。但目前主流浏览器,包括 IE8之后的版本,都对 +元素运算符链接字符串作了优化,性能已经显著高于 Array.join()。因此目前大多数状况下,建议首选使用+ 链接字符串。更多参看《高性能网站建设进阶指南》中字符串优化相关章节。

array.pop()
pop方法移除array中的最后一个元素,并返回这个元素。若是array为空,则返回undefined

var a = ['a','b','c'];
var c = a.pop(); // a 是 ['a','b']  c是 'c'
// pop 能够像这样实现。
// 这里的 Array.method()在第一章例子中已经定义了,而且贯穿全书。其实就是至关于Array.prototype
Array.method('pop', function () {
    return this.splice(this.length-1,1)[0];
});

array.push(item...)
concat不一样的是,它会修改array,若是参数item是数组,它会把参数数组做为单个元素整个添加到数组中。并返回这个array的新长度值。

var a = [1,2,3];
var b = [4,5,6];
var c = a.push(b,true);
// a 是 [1,2,3,[4,5,6],true]
// c 是 5

push能够像这样实现:

Array.method('push', function () {
  this.splice.apply(
  this,
  [this.length,0].
  concat(Array.prototype.slice.apply(arguments)));
  return this.length;
});

array.reverse()
reverse反转array元素顺序,并返回array自己。

var a = [1,2,3];
var b = a.reverse();
// a 和 b都是 [3,2,1]

array.shift()
shift移除array的第一个元素并返回这个元素。若是array为空,则返回undefinedshift一般比pop慢的多。

var a = [1,2,3];
var c = a.shift(); // a 是[2,3] , c 是1

shift能够这样实现:

Array.method('shift', function(){
    return this.splice(0,1)[0];
});

array.slice(start[, end])
slice是对array中的一段作浅复制。end是可选的。默认是array.length,若是两个参数任何一个是负数,array.length会和相加。若是start大于array.length,得到一个[],字符串也有Sting.slice这个同名方法。
array.sort
默认不能给一组数字排序。默认把要被排序的元素都视为字符串。
幸运的是,可使用本身的比较函数替换默认的比较函数。
比较函数应该接受两个参数,而且若是这两个参数相等则返回0,若是第1个参数应该排列在前面,则返回一个负数,若是第二个参数应该排列在前面,则返回一个正数。
sort方法是不稳定的。JavaScriptsort方法的稳定性根据不一样浏览器的实现而不一致。
可参见MDN sort
array.splice(start, deleteCount,item...)
splice方法从array中移除一个或多个元素,并用新的item替换它们。

// splice 能够像这样实现
Array.method('splice',function (start, deleteCount) {
    var max = Math.max,
        min = Math.min,
        delta,
        element,
        insertCount = max(arguments.length - 2, 0),
        k = 0,
        len = this.length,
        new_len,
        result = [],
        shift_count;
    start = start || 0;
    if (start < 0) {
        start += len;
    }
    start = max(min(start, len), 0);
    deleteCount = max(min(typeof deleteCount === 'number' ? deleteCount : len, len - start), 0);
    delta = insertCount - deleteCount;
    new_len = len + delta;
    while (k < deleteCount) {
        element = this[start + k];
        if (element !== undefined) {
            result[k] = element;
        }
        k += 1;
    }
    shift_count = len - start - deleteCount;
    if (delta < 0) {
        k = start + insertCount;
        while (shift_count) {
            this[k] = this[k - delta];
            k += 1;
            shift_count -= 1;
        }
        this.length = new_len;
    } else if (delta > 0) {
        k = 1;
        while (shift_count) {
            this[new_len - k] = this[len - k];
            k += 1;
            shift_count -= 1;
        }
        this.length = new_len;
    }
    for (k = 0; k < insertCount; k += 1) {
        this[start + k] = arguments[k + 2];
    }
    return result;
});

array.unshift(item...)
unshift 方法像push方法同样,不过是用于把元素添加到数组的开始部分,返回新arraylength

// unshift 能够像这样实现
Array.method('unshift', function(){
    this.splice.apply(this,
    [0,0].concat(Array.prototype.slice.apply(arguments)));
    return this.length;
});

Function

function.apply(thisArg,argArray)
apply方法调用function,传递一个会被绑定到this上的对象和一个可选的数组做为参数。

Number

number.toExponential(fractionDigits)
toExponential方法 把这个number转换成一个指数形式的字符串。可选参数控制其小数点后的数字位数。它的值必须在0~20

number.toFixed(fractionDigits)
toFixed方法把这个number转换成一个十进制数形式的字符串。可选参数控制其小数点后的数字位数。它的值必须在0~20。

number.toPrecision(precision)
toPrecision方法把这个number转换成一个十进制数形式的字符串。可选参数控制数字的精度。它的值必须在0~21

number.toString(radix)
number转换成字符串。可选参数控制基数。它的值必须是2~36。默认的radix是以10为基数的。radix参数最经常使用的是整数,可是它能够用任意的数字。

Object

object.hasOwnProperty(name)
若是这个object包含名为name的属性,那么返回true。原型链中的同名方法不会被检测。这个方法对name就是“hasOwnProperty”时不起做用。

RegExp

regexp.exec(string)
exec是正则中最强大(和最慢)的方法。
若是成功匹配,它会返回一个数组。下标为0 的元素包含正则匹配的子字符串。下标为1的则是分组1捕获的文本。下标为2的则是分组2捕获的文本。以此类推。若是匹配失败则返回null
regexp.test(string)
test是最简单(和最快)的方法。匹配成功,返回true,不然返回false。不要对这个方法使用g标识。
好比:

var reg = /\w+/g;
reg.test('ab'); // true
// 再执行一遍就是false了。
reg.test('ab'); // false
// 再执行一遍就是true了。
reg.test('ab'); // true
// 再执行一遍又是false了,如此反复,因此用g标识后,看起来很诡异。由于每次匹配开始位置变了。
reg.test('ab'); // false

test能够像这样实现:

RegExp.method('test', function(string){
    return this.exec(string) !== null;
});

String

string.charAt(pos)
返回在string中的pos位置处的字符。

string.charCodeAt(pos)
charAt同样,不过返回整数形式表示字符码位。

string.concat(string)
不多用,用+号运算符更方便。

string.indexOf(searchString,position)
string中查找第一个参数,若是被找到返回该字符的位置,不然返回-1position可设置指定位置开始查找。

string.lastIndexOf(searchString,position)
lastIndexOf 方法和indexOf方法相似,不过它是从末尾开始查找,不是从头开始。

string.localeCompare(that)
比较两个字符串。相似于array.sort

string.match(regexp)
若是没有g标识,那么调用string.match(regexp)和调用regexp.exec(string)结果相同。若是带有g标识,那么它生成一个包含全部匹配(除捕获分组以外)的数组。

string.replace(searchValue,replaceValue)
string进行查找和替换操做,并返回一个新的字符串。参数searchvalue能够是一个字符串也能够是一个正则表达式对象。参数replaceValue能够是一个字符串或一个函数。

string.search(regexp)
indexOf相似,不过它接收正则为参数。

string.slice(start, end)
slice方法复制string的一部分来构造一个新的字符串。若是start参数是负数,它将与string.length相加。end参数是可选的。

string.split(separator,limit)
string分割成片断来建立一个字符串数组。可选参数limit能够限制分割的片断数量。separator参数能够是字符串或者正则。
string.substring(start,end)
slice方法同样,不过它不能处理负数参数。
string.toLocaleLowerCase()
它使用本地化的规则把这个string中的字母转换成小写格式。这个方法主要用在土耳其语上。
string.toLocaleUpperCase()
它使用本地化的规则把这个string中的字母转换成大写格式。这个方法主要用在土耳其语上。
string.toLowerCase()
返回新字符串,全部字母转成小写格式。
string.toUpperCase()
返回新字符串,全部字母转成大写格式。
String.fromCharCode(char...)
根据一串数字编码返回一个字符串。

var a = String.fromCharCode(67,97,116) // a是'Cat'

第9章 代码风格

这一章中,简短的说了一些代码风格。事实证实代码风格在编程中是很重要的。

第10章 优美的特性

精简的JavaScript里都是好东西。
包括:一、函数是顶级对象;二、基于原型继承的动态做用域;三、对象字面量和数组字面量。

到此,读书笔记已完结。文章有什么不妥之处,欢迎指出~

关于

做者:常以轩辕Rowboat若川为名混迹于江湖。前端路上 | PPT爱好者 | 所知甚少,惟善学。
我的博客
segmentfault前端视野专栏,开通了前端视野专栏,欢迎关注
掘金专栏,欢迎关注
知乎前端视野专栏,开通了前端视野专栏,欢迎关注
github,欢迎follow~

相关文章
相关标签/搜索