忍者秘籍读书笔记

Babel 是编写下一代 JavaScript 的编译器。Babel 默认使用一组 ES2015 语法转换器,允许你使用新的语法,无需等待浏览器支持。

Traceur 是一个来自 Google 的 Javascript 编译器,通过它可以体验一些很新并且有趣的 Javascript 语言特性,这些多数是还没有被当前浏览器实现的 ECMAScript 标准或草案,比如:数组比较、类、模块、迭代器、方法参数默认值、Promise等。

 

与其它主流语言相比,JS函数式语言的血统更多一些:

这些根本性的差异,包含以下内容:①函数式一等公民(一级对象,创建,传递,共存,返回)②函数闭包(函数主动维护了在函数内部使用的外部变量,则该函数为一个闭包)③作用域(js没有块级作用域下的变量,只能依赖函数级别全局和局部)④基于原型的面向对象

对象,原型,函数,闭包的紧密结合组成了 JavaScript 。理解这些概念的密切联系,才能得到提升。

 

聚焦JS以下特性:

生成器:一种可以基于一次请求生成多次值的函数,在不同请求之间也能挂起执行。

Promise:更好的控制异步代码。

代理:让我们控制对特定对象的访问。

高级数组方法:书写更优雅的数组处理函数。

Map:用于创建字典集合;Set:处理仅包含不重复项目的集合。

正则表达式:简化用代码写起来的复杂逻辑。

模块:把代码划分为较小的可以自包含的片段,使项目更易于管理。

 

浏览器环境聚焦:

①文档对象模型(DOM是WEB应用的结构化的UI表现形式)。

②事件(大部分js应用都是事件驱动应用)

③浏览器API

 

需要做到:精通JavaScript语言,调试技巧,测试,性能分析。

跨平台开发的能力:Node开发服务器端应用和嵌入式应用。

 

第2章:运行时的页面构建过程

浏览器是否总是会根据给定的HTML来渲染界面呢?

Web应用一次能处理多少个事件?

为什么浏览器使用事件队列来处理事件?

 

生命周期预览:

 

 从用户的角度来说,浏览器构建了发送至服务器(序号2)的请求,该服务器处理了请求(序号3)并形成了一个通常由 HTML,CSS,JavaScript 代码所组成的响应。当浏览器接收了响应(序号4)时,我们的客户端应用开始了它的生命周期。由于客户端 Web 应用是图形用户界面 (GUI) 应用,其生命周期与其他的 GUI 应用相似(例如标准的桌面应用和移动应用),其执行步骤如下:

1.页面构建——创建用户界面;

2.事件处理——进入循环(序号5)从而等待事件(序号6)的发生,发生后调用事件处理器;

应用的生命周期随着用户关掉或离开页面(序号7)而结束。

 

2.2 页面构建阶段

页面构建必须根据服务器获取的响应:通常是HTML,CSS,JavaScript;

页面构建的阶段目标是建立web应用的UI,其主要分两步:①解析HTML代码并构建文档对象模型(DOM)②执行JavaScript代码。

步骤①会在浏览器处理HTML节点过程中执行,步骤二会在HTML解析到一种特殊的节点——脚本节点(包含或引用JS代码的节点)时执行。页面构建阶段中,这两个步骤会交替执行多次。

 

 

尽管DOM是根据HTML创建的,两者紧密联系,但并不相同。可以把HTML代码看见为初始化浏览器页面UI构建初始DOM的蓝图,浏览器还会修复它发现蓝图中的问题。

 

HTML的版本是HTML5,DOM则发展相对缓慢,当前的版本是DOM3。

 

页面构建阶段,如果浏览器遇到特殊类型的HTML元素-脚本元素,该元素用于包含JS代码。每当解析到脚本元素时,DOM构建会停止,并开始执行JS代码。所有包含在脚本元素中的js代码,都由浏览器的JS引擎执行。

列如:Firefox——Spidermonkey 引擎

          Chroom,Opera——V8

          Edge,IE——Chakra     

由于代码的主要目的是提供动态界面,故而浏览器通过全局对象提供一个API使JS引擎可以与之交互并改变页面内容。浏览器暴露给JS引擎的主要全局对象是window,它代表了包含着一个页面的窗口。

全局window对象是获取所有其他全局对象,全局变量和浏览器API的访问途径。 全局window对象最重要的属性是document,它代表了当前页面的DOM。通过使用这个对象,JS代码就能在任意程度改变DOM,包括修改或移除现存的节点,以及创建和插入新的节点。

 

2.3 事件处理

客户端Web应用是一种GUI应用,也就是说这种应用会对不同类型的事件作响应。因此,在页面构建阶段执行的JS代码,除了会影响全局应用状态和修改DOM外,还会注册事件监听器(处理器)。这类监听器会在事件发生时,由浏览器调用执行。

浏览器执行环境的核心思想基于:同一时刻只能执行一个代码片段,即所谓的单线程执行模式。

浏览器事件队列,跟踪已经发生但尚未处理的事件。

所有已经生成的事件(无论用户生成的,例如鼠标键盘;还是服务器生成的,例如Aiax)都会放在同一个事件队列中,以它们被浏览器检测到的顺序排列。

 

重点注意浏览器在这个过程中的机制,其放置事件的队列是在页面构建阶段和事件处理阶段以外的。这个过程对于决定事件何时发生并将其推入事件队列很重要,这个过程不会参与事件处理线程。

 

Web常见的事件:

  • 浏览器事件:例如当页面加载完成后或无法加载时;
  • 网络事件:例如来自服务器的响应(Ajax事件和服务器端事件);
  • 用户事件:例如鼠标单击、鼠标移动和键盘事件;
  • 计时器事件:当timeout时间到期或又触发了一次时间间隔;

事件处理的概念是Web应用的核心。代码的提前建立是为了在之后的某个时间点执行。除了全局代码,页面中的大部分代码将作为某个事件的结果执行。在事件能被处理之前,代码必须要告知浏览器我们要处理特定事件。

 

注册事件处理器:

事件处理器是当某个特定事件发生后我们希望执行的函数。为了达到这个目标,我们必须告知浏览器我们要处理那个事件。这个过程叫做注册事件处理器。在Web应用中,有两种注册方式:①通过把函数赋给某个特殊属性②通过使用内置 addEventListener 方法。

 

事件处理的主要思想是:当事件发生时,浏览器调用相应的事件处理器。           

 

第3章 定义与参数(新手的第一堂函数课~)

像普通人一样编写代码和像"忍者"一样编写代码的最大差别在于是否把 JavaScript 作为函数式语言(function language)来理解。 

  • 回调函数在那种情况下会同步调用,或者异步调用呢?
  • 箭头函数和函数表达式的区别?
  • 你为什么需要在函数中使用默认参数?

函数及函数式概念之所以如此重要,其原因之一在于函数是程序执行过程中的主要单元模块。除了全局 js 代码是在页面构建阶段执行的,我们编写的所有的脚本代码都将在一个函数内执行。

通过强大的构造器能赋予代码很大的灵活性和控制力,利用函数作为第一类对象的特性获益。

 

3.3.1 函数是第一类对象

js中函数拥有对象的所有能力,也因此函数可以被作为任意其他类型对象来对待。

函数也是对象,唯一的特殊之处在于它是可调用的,即函数会被调用以执行某项动作。

第一类对象的特点之一是,它能够作为参数传入函数。

 

3.1.2 回调函数

每当我们建立了一个将在随后调用的函数时,无论是在事件处理阶段通过浏览器还是通过其他代码,我们都是在建立一个回调(callback)。这个术语源自于一个这样的事实:

即在执行过程中,我们建立的函数会被其他函数在稍后的某个合适的时间点"再回来调用"。

 

js的重要特征之一是可以在表达式出现的任意位置创建函数,除此之外这种方式会使代码更紧凑易于理解(把函数定义放在函数使用处附近)

 

3.3 函数定义

JS提供了四类定义函数的方法:

  • 函数声明(function declarations)和 函数表达式(function expression)————最常用,在定义函数上却又微妙不同的两种方式。人们通常不会独立的看待他们,但正如你将看到的,意识到两者的不同能帮我们理解函数何时能被调用。
  • function myFun ( )  { return 1 ; }
  • 箭头函数(通常被叫作 lambda 函数)————ES6 新增的 JavaScript 标准,能让我们以尽量简洁的语法定义函数。
  • myArg => myArg*2
  • 函数构造函数————一种不常使用的函数定义方式,能让我们以字符串形式动态构造一个函数,这样得到的函数时动态生成的。这个例子动态地创建了一个函数,其参数为a和b,返回值为两个数的和。
  • new  Function( 'a' ,'b' ,'return a + b' )
  • 生成器函数————ES6 新增功能,能让我们创建不同于普通函数的函数,在应用程序执行过程中,这种函数能够退出再重新进入,在这些再进入之间保留函数内变量的值。我们可以定义生成器版本的函数声明、函数表达式、函数构造函数。
  • function * myGen ( ) { yield 1 ; }

理解这几种方式的不同很重要,因为函数的创建方式很大程度地影响了函数可被调用的时间、函数的行为以及函数可以在那个对象上被调用。

 

数字字面量:

var  a  =  3 ;

myFunction( 4 ) ;

函数字面量:

var  a = function(){ } ;

myFunction( function ( ) { } );

这种总是其它函数表达式一部分的函数(作为赋值表达式的右值,或者作为其他函数的参数)叫做函数表达式。函数表达式非常重要,在于它能准确地在我们需要使用的地方定义函数,这个过程能让代码易于理解。

 

函数声明和函数表达式除了在代码中的位置不同以外,还有一个重要的不同点。对于函数声明来说,函数名是强制性的,而对于函数表达式来说,函数名则完全是可选的。

函数声明必须具有函数名是因为它们是独立语句。一个函数的基本要求是它应该能被调用,所以它必须具有一种被引用的方式,于是唯一的方式就是通过它的名字。

从另一个方面来看,函数表达式也是其它js表达式的一部分,所以我们也就有了调用它们的替代方案。例如:把一个函数表达式赋值给一个变量。或者它是另外一个函数的参数,我们可以在该函数中通过参数名来调用。

 

3.3.2 箭头函数 (tip:箭头函数是 ES6 新增项)

箭头函数的定义以一串可选参数名列表开头,参数名以逗号分隔。如果没有参数或多于一个参数,参数列表就必须包裹在括号内。但如果只有一孤儿参数,括号就不是必须的。参数列表之后必须跟着一个胖箭头符号,一次像我们和js引擎指示当前处理的是箭头函数。

胖箭头操作符后面有两种可选方式。如果要创建一个简单函数,那么可以把表达式放在这里(可以是数学运算、其它函数调用等),则该函数的返回值即为此表达式的返回值。

 

3.4 箭头函数(实参:argument ;形参:parameter)

形参:是我们定义函数时所列举的变量。

实参:是我们调用函数时所传递给函数的值。

如果实参的数量大于形参,那么额外的实参不会赋值给任何参数。尽管有些实参没有被分配给某个形参名,但依然有一种获取它们的方式。如果形参大于实参,没对应的形参会被设置为 undefined;

为函数的最后一个命名参数前加上省略号(...)前缀,这个参数就变成了一个叫做剩余参数的数组,数组内包含着传入的剩余的参数。 // function  example(first,...end){  };

 

typeof运算符的返回类型为字符串,值包括如下几种:

        1. 'undefined'              --未定义的变量或值

        2. 'boolean'                 --布尔类型的变量或值

        3. 'string'                     --字符串类型的变量或值

        4. 'number'                  --数字类型的变量或值

        5. 'object'                    --对象类型的变量或值,或者null(这个是js历史遗留问题,将null作为object类型处理)

        6. 'function'                 --函数类型的变量或值


3.5 小结

作为第一类对象,函数和JS中其他对象一样。函数具有以下功能:

  • 通过字面量创建
  • 赋值给变量或属性
  • 作为函数参数传递
  • 作为函数的结果返回
  • 赋值给属性和方法

函数类型:

  • 函数声明
  • 函数表达式
  • 箭头函数
  • 函数生成器等

回调函数:回电函数是被代码随后 " 回来调用 " 的函数。很常用,特别是在事件处理场景下。

函数声明和函数表达式是两种最主要的函数类型。函数声明必须具有函数名,它也必须作为一个独立的语句存在,函数表达式可以不具有函数名,但此时它就必须作为其他语句的一部分。

函数的形参列表和实参列表长度可以不同:

  • 未赋值的形参求值得到 undefined 。
  • 传入的额外实参不会被赋给任何一个命名形参。

剩余参数和默认参数是js的新特性:

  • 剩余参数——不与任何形参名相匹配的额外实参可以通过剩余参数来引用。
  • 默认参数——函数调用时,若没传入参数,默认参数可以给函数提供最缺省的参数值。

 

第4章 函数进阶:理解函数调用

隐式的函数参数:this 和 arguments  。 二者会被静默地传递给函数,并且可以像函数体内显式地声明的参数一样被正常访问。 

this:表示被调用函数的上下文对象;

arguments:表示函数调用过程中传递的所有参数。

 

这两个参数在js中至关重要,this是js面向对象编程的基本要素之一,而arguments参数可以让我们访问函数调用过程中传递的实际参数。

函数形参:函数定义时定义的一部分变量;(除了定义中显示声明的参数外,还有隐式的声明的this和arguments)

函数实参:调用函数时传递给函数的值。

 

arguments参数:

无论是否有明确定义对应的形参,通过它我们都可以访问到函数的所有参数,借此,可以实现原生 JS 并不支持的函数重载特性,而且可以实现接收参数数量可变的可变函数。剩余参数的出现,对arguments的需求已经大大减少了。

arguments对象仅为一个类数组的结构,并不是数组。将arguments对象作为函数参数的别名使用时会影响代码的可读性,因此严格模式下无法使用它。

严格模式ES5引入的特性,它可以改变js引擎的默认行为,并执行更加严格的语法检查。

 

this参数:

代表函数相关联的对象,通常称之为函数上下文。

函数上下文,是来自面向对象语言的一个概念。this 通常指向定义当前方法的类的实例。

事实上,this 参数的指向不仅是由定义函数的方式和位置决定的,同时还严重受到函数调用方式的影响。真正理解 this 参数 是面向对象 js编程的基础。

 

我们可以通过四种方式调用一个函数:

  • 作为一个函数,直接被调用。
  • 作为一个方法,关联在一个对象上,实现面向对象编程。(作为对象的属性,通过对象属性引用的方式调用。)
  • 作为一个构造函数,实例化一个新的对象。
  • 通过函数的 apply 或者 call 方法。

 

作为构造函数调用:

使用关键字new调用函数会触发一下几个动作:

1.创建一个新的空对象。

2.该对象作为this参数传递给构造函数,从而成为构造函数的函数上下文。

3.新构造的对象作为new运算符的返回值。

 

 

 

构造函数的目的是:初始化新创建的对象,并且新构造函数的对象会作为构造函数的调用结果(通过new运算符)返回返回。

如果构造函数返回一个对象,则该对象将作为整个表达式的值返回,而传入构造函数的this将被丢失。

但是,如果构造函数返回的是非对象类型,则忽略返回值,返回新创建的对象。

编写构造函数的注意事项:

构造函数的目的是根据初始条件对函数调用创建的新对象进行初始化。虽然这些函数也可以被"正常调用",或者被赋值为对象属性从而作为方法调用,但是这样并没有多大意义。

函数和方法的命名通常以描述其行为的动词开头,且第一个字母小写。而构造函数则通常以描述所构造对象的名词命名,并以大写字母开头。

很明显,通过构造函数我们可以更优雅地创建多个遵循相同模式的对象。而无需一次次重复相同的代码。通用代码只需要作为构造函数的主体写一次即可。

 

使用apply和call方法调用:

不同类型函数调用之间的主要区别在于:最终作为函数上下文(可以通过 this 参数隐式引用到)传递给执行函数的对象不同。

JS为我们提供了一种调用函数的方式,从而可以显式地指定任何对象作为函数的上下文。我们可以使用每个函数上都存在的这两种方法完成:apply,call;

apply:需要为其传递两个参数,作为函数上下文的对象和一个数组作为函数调用的参数。

call:函数上下文的对象和直接以参数列表的形式的参数。

 

 apply和call如何选择:

选择任意可以精简代码的方法。更实际的答案是选择与现有参数相匹配的方法。

如果有一组无关的值,则直接用call,若现有参数是数组类型,apply方法更佳。

 

箭头函数可以规避函数上下文问题:

apply和call可以解决函数上下文问题。现讲另外两个选择:箭头函数和 bind 方法。

箭头函数无上下文,与声明所在上下文相同。调用箭头函数时,不会隐式传入 this 参数,而是从定义时的函数继承上下文。

在全局代码中定义对象字面量,在字面量中定义箭头函数,那么箭头函数内this指向,全局window对象。

 

bind:

无论使用哪种方法调用,bind 方法创建的新函数与原始函数的函数体相同,新函数被绑定到指定的对象上。

所有函数均可以访问bind方法,可以创建并返回一个新函数,并绑定在传入的对象上。不管如何调用该函数,this 均被设置为对象本身。

被绑定的函数与原函数一致,函数体一致,不会修改原函数,而是创建了一个全新的函数。

 

4.4小结

当调用函数时,除了传入在函数定义中显式声明的参数之外,同时还传入两个隐式参数:arguments 与 this 。

  • arguments 参数是传入函数的所有参数的集合。具有length属性,表示传入参数的个数,通过arguments参数还可获取那些与函数形参不匹配的参数。在非严格模式下,arguments对象是函数参数的别名,修改arguments对象会修改函数的实参,可以通过严格模式避免修改函数实参。
  • this 表示函数上下文,即与函数调用相关的对象。函数的定义方式和调用方式决定了this的取值。

函数调用方式有四种:

  • 作为函数调用:azuiki七();
  • 作为方法调用:gc.azuki七();
  • 作为构造函数调用:new Gc();
  • 通过 apply 与 call 方法调用:azuki七.apply(gc) 或 azuki七.call(gc);

函数的调用方式影响this的取值:

  • 如果作为函数调用,在费严格模式下,this指向全局window对象;在严格模式下,this指向undefined;
  • 作为方法调用,this 通常指向调用的对象。
  • 作为构造函数调用,this 指向新创建的对象。
  • 通过 call 或 apply 调用,this 指向二者的第一个参数。

箭头函数没有单独的this值,this在箭头函数创建时确定。

所有函数均可使用 bind 方法,创建新函数,并绑定到 bind 方法传入的参数上。被绑定的函数与原始函数具有一致的行为。

 

js中我们可以通过三个关键字定义变量:var、let、const。它们有两点区别:可变性,词法环境的关系。

可变性:let、var 可变 const 不可变

const 变量常用于两种目的:①不需要重新赋值的特殊变量②指向一个固定的值(比如:球队人数) 。

由于在程序执行过程中不允许对 const 重新赋值,这样可以避免代码发生不必要的变更,同时也为js引擎性能优化提供便利。 

const 变量只能被初始化一次,之后不允许将全新的值赋给const变量。但是仍可修改 const 变量已经存在的值,只是不能重写。

词法环境(指作用域):let、const 块级 var 全局

使用 var 时,该变量是在距离最近的函数内部或是全局词法环境中定义的(注册的,不关注块级作用域)。(注意:忽略块级作用域) ,这是js由来已久的“特性”。

与 var 不同,let 和 const 直接在最近的词法环境中定义变量(可以是在块级作用域内、循环内、函数内和全局环境内)。

 

词法环境如何保存标识符的映射表,词法环境与程序执行的关系?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

      

   

 

posted @ 2021-02-10 19:21  AZUKI七  阅读(145)  评论(0编辑  收藏  举报