JavaScript忍者秘籍

https://github.com/zhangyue0503/html5js/blob/master/javascriptninja/

一、进入忍者世界

A.即将探索的JavaScript库

1.jQuery、Prototype、Yahoo!UI、base2

2.一个JS库的组成部分

  • JS语言的高级使用
  • 跨浏览器代码的精心构建
  • 当前能够聚众合一的最佳实践应用

B.理解JavaScript语言

1.对象、函数、闭包

2.with、eval()

3.定时器、正则表达式

C.跨浏览器注意事项

1.IE7、8、9,chrome,firefox,safari、opera

D.当前最佳实践

1.测试:自己实现的assert()函数

2.性能分析

3.调度技巧

二、利用测试和调试武装自己

A.调试代码

1.console.log()

2.断点

B.测试用例生成

1.优秀的测试用例:可重用性(repeatability)、简单性(simplicity)、独立性(independence)

2.解构型测试用例(deconstructive test cases)在消弱代码隔离问题时进行创建,以消除任何不恰当的问题。

3.构建型测试用例(constructive test cases)从一个大家熟知的良好精简场景开始,构建用例,直到我们能够重现bug为止

C.测试框架

1.QUnit

2.YUI Test

3.JsUnit

4.JUnit、TestSwarm

D.测试套件基础知识

1.断言:接收一个断言的值,以及一个表示该断言目的的描述

2.测试组:一个测试组可能代表一组断言

E.异步测试

1.将依赖相同异步操作的断言组合成一个统一的测试组

2.每个测试组需要那就行在一个队列上,在先前其他的测试组完成运行之后再运行

三、函数是根基

A.函数的独特之处

1.函数是第一型对象

  • 它们可以通过字面量进行创建
  • 它们可以赋值全变量、数组或其他对象的属性
  • 它们可以作为参数传递给函数
  • 它们可以作为函数的返回值进行返回
  • 它们可以拥有动态创建并赋值的属性

2.浏览器的事件轮询

  • 事件在触发时被旋转在一个事件队列(先进先出列表[FIFO])中,然后浏览器将调用已经为这些事件建立好的处理程序 (handler)
  • 浏览器的事件轮询是单线程(single-threaded)的

3.回调概念

  • 当我们定义一个函数稍后执行时,无论何时定义,在浏览器执行还是其他地方执行,我们定义的就是所谓的回调(callback)。我们定义一个函数,以便其他一些代码在适当的时机回头再调用它。

4.js中的函数式特性允许我们像使用其他类型一样,创建一个作为独立实体的函数,并将其作为一个参数,像传递其他类型一样将其传递给另外一个方法,而这个方法可以将该函数作为一个参数进行接收,就像接收其他类型的参数一样

B.函数声明

1.函数字面量

  • function关键字
  • 可选名称
  • 括号内部
  • 函数体

2.作用域和函数

  • 变量声明的作用域开始于声明的地方,结束于所在函数的结尾,与代码嵌套无关
  • 命名函数的作用域是指声明该函数的整个函数范围,与代码嵌套无关(机制提升)
  • 对于作用域声明,全局上下文就像一个包含页面所有代码的超大型函数

C.函数调用

1.函数调用

  • 作为一个函数进行调用
  • 作为一个方法进行调用,在对象上进行调用,支持面向对象编程
  • 作为构造器进行调用,创建一个新对象
  • 通过apply()或call()方法进行调用

2.从参数到函数形参

  • 如果实际传递的参数数量大于函数声明的形参数量,超出的参数则不会配给形参名称
  • 如果声明的形参数量大于实际传递的参数数量,则没有对应参数的形参会赋值为undefined
  • arguments参数是传递给函数的所有参数的一个集合,有length属性,没有其他数组方法,是类数组结构
  • this参数引用了与该函数调用进行隐式关联的一个对象,被称为函数上下文(function context)
  • js中this依赖于函数的调用方式

3.作为函数进行调用

  • 如果一个函数不是作为方法、构造器,或通过apply()/call()进行调用的,则认为它是“作为函数”进行调用的
  • 这种方式通常发生在函数上使用()操作符进行调用的时候,应用了()操作符的表达式并没有将函数作为对象的一个属性
  • 函数的上下文是全局上下文——window对象

4.作为方法进行调用

  • 当一个函数被赋值给对象的一个属性,并使用引用该函数的这个属性进行调用时,那么函数就是作为该对象的一个方法进行调用的
  • 将函数作为对象的一个方法(method)进行调用时,该对象就变成了函数上下文,并且在函数内部可以以this参数的形式进行访问

5.作为构造器进行调用

  • 将函数作为构造器(constructor)进行调用,我们要在函数调用前使用new关键字

6.构造器的超能力

  • 创建一个新的空对象
  • 传递给构造器的对象是this参数,从而成为构造器的函数上下文
  • 如果没有显式的返回值,新创建的对象则作为构造器的返回值进行返回

7.构造器编码注意事项

  • 函数和方法名通常以动词开头,并且是小写字母开头;构造器通常是描述所构造对象的名词,以大写字母开头
  • 通过构造器,使用相同的模式就可以更容易地创建多个对象,而无需再一遍又一遍地重复相同的代码。通用代码,作为构造器的构造体,只需写一次

8.使用apply()和call()方法进行调用

  • 通过函数的apply()方法来调用函数,我们要给apply()传入两个参数:一个是作为函数上下文的对象,另外一个是作为函数参数所组成的数组。call()方法的使用方式类似,唯一不同的是,给函数传入的参数是一个参数列表,而不是单个数组
  • 选择哪一个?哪个方法可以提高代码清晰度就用哪个,如果在变量里有很多无关的值或者是指定为字面量,使用call()方法则可以直接将其作为参数列表传进去。但是如果这些参数已经在一个数组里了,或者很容易将其收集到数组里,那么apply()是更好的选择

四、挥舞函数

A.匿名函数

1.通常匿名函数的使用情况是,创建一个供以后使用的函数。例如,将匿名函数保存在一个变量里,将其作为一个对象的方法,或者是将匿名函数作为一个回调

2.函数式编程专注于:少、通常无副作用、将函数作为程序代码的基础构件块

B.递归

1.内联函数(inline function),对象方法给函数起个名字。

2.递归引用

  • 通过名称进行引用
  • 作为一个方法进行引用
  • 通过内联名称进行引用
  • 通过arguments的callee属性进行引用

C.将函数视为对象

1.缓存记忆

  • 在函数调用获取之前计算结果的时候,最终用户享有性能优势
  • 发生在幕后,完全无缝
  • 为了提高性能,任何类型的缓存肯定会牺牲掉内存
  • 很难测试或测量一个算法的性能

D.可变长度的参数列表

1.函数的length属性

  • 通过其length属性,可以知道声明了多少命名参数
  • 通过arguments.length,可以知道在调用时传入了多少参数

2.函数调用时,通过控制传递函数上下文,我们在当前对象上执行该对象没有的方法。利用这种技术,可以利用像Array和Math上已有的方法,在自定义数据上进行操作

3.重载只适用于不同数量的参数,但并不区分类型,参数名称或其他东西

E.函数判断

1.通过在对象上调用typeof运算符,判断结果是不是function,不过有跨浏览器的问题

五、闭包

A.闭包是如何工作的

1.闭包(closure)是一个函数在创建时允许该自身函数访问并操作该自身函数之外的变量时所创建的作用域。闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域内就行

2.声明的函数在后续什么时候都可以被调用,即使是声明时的作用域消失之后

3.三个关于闭包的概念

  • 内部函数的参数是包含在闭包中的
  • 作用域之外的所有变量,即使是函数声明之后的那些声明,也都包含在闭包中
  • 相同的作用域内,尚未声明的变量不能进行提前引用

B.使用闭包

1.私有变量:限制变量的作用域

2.回调(callback)与计时器(timer)

3.函数在闭包里执行的时候,不仅可以在闭包创建的时刻点上看到这些变量的值,还可以对其进行更新,闭包不是在创建那一时刻点的状态的快照,而是一个真实的状态封装,只要闭包存在,就可以对其进行修改

C.绑定函数上下文

1.bind()并不是apply()和call()的替代方法,该方法的潜在目的是通过匿名函数和闭包控制后续执行的上下文。这个重要的区别使apply()和call()对事件处理程序和定时器的回调进行延迟特别有帮助

D.偏应用函数

1.“分部应用”一个函数,在函数调用之前,可以预先传入一些函数,实际上,偏应用函数返回了一个含有预处理参数的新函数,以便后期可以调用

2.这种在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称之为柯里化(currying)

E.函数重载

1.每个函数都有自己的上下文,从而可以将上下文变成闭包的一部分

2.如果过多地利用闭包修改函数的逻辑,那会让函数变得不可扩展

F.即时函数

1.(function(){})(),第一组圆括号仅仅是用于划定表达式的范围,而第二个圆括号则是一个操作符,不管第一组圆括号中是什么内容,都会将其作为函数的引用进行支持

2.执行过程

  • 创建一个函数实例
  • 执行该函数
  • 销毁该函数

3.由于函数是立即执行,其内部所有的函数、所有的变量都局限于其内部作用域。可以存储数据状态。

4.在JS中,变量的作用域依赖于变量所在的闭包

5.闭包记住的是变量的引用——而不是闭包创建时刻该变量的值

六、原型与面向对象

A.实例化和原型

1.原型可以让我们预定义属性,包括方法,这些属性和方法会自动应用在新对象实例上

2.在构造器内的绑定操作优先级永远都高于在原型上的绑定操作优先级。因为构造器的this上下文指向的是实例自身,所以我们可以在构造器内对核心内容执行初始化操作

3.查询属性引用时,首先是查询对象自身,如果不存在,才在原型上进行查找

B.疑难陷阱

1.不要扩展原生Object.prototype,使用hasOwnProperty判断属性是对象实例上的还是原型链上的

2.不要扩展数字

七、正则表达式

A.正则表达式进阶

1.在开发过程中,如果正则是已知的,则优先选择字面量语法,而构造器方式则是用于在运行时,通过动态构建字符串来构建正则表达式

B.编译正则表达式

1.每个正则表达式都有一个独立的对象表示:每次创建正则表达式,都会为此创建一个新的正则表达式对象

C.捕获匹配的片段

1.在全局正则表达式的情况下,匹配所有可能的匹配结果,而不仅仅是第一个匹配结果,返回的数组包含了全局匹配结果

2.exec()方法可以对一个正则进行多次调用,每次调用都可以返回下一个匹配的结果

3.要让一组括号不进行结果捕获,正则表达式的语法允许我们在开始括号后加一个?:标记(被动子表达式)

D.利用函数进行替换

1.replace()最强大的特性是可以接受一个函数作为替换值,参数:匹配的完整文本、匹配的捕获、匹配字符在源字符串中的索引、源字符串

八、驯服线程和定时器

A.定时器延迟的最小化及其可靠性

1.浏览器不保证我们指定的延迟间隔,虽然可以指定特定的延迟值,但其准确性却并不总是能够保证,尤其是在延迟值很小的时候

九、忍者点金术:运行时代码求值

A.代码求值机制

1.用eval()方法进行求值

  • 该方法将执行传入代码的字符串,将返回传入字符串中最后一个表达式的执行结果
  • 在调用eval()方法的作用域内进行代码求值
  • 任何不是简单的变量、原始值、赋值语句的内容都需要在外面包装一个括号
  • 求值执行的作用域就是调用eval()时的作用域

2.用函数构造器进行求值:不会创建闭包

3.用定时器进行求值

4.全局作用域内的求值操作:将要执行的代码放在动态的<script>标签内,并将标签注入到文档中,常见的场景是从服务器端返回的代码

5.安全的代码求值:无解,Google Caja

B.函数反编译

1.由函数的toString()方法来执行

C.代码求值实战

1.JSON转换

2.在名称空间内移动定义代码

3.JS代码的最小化及混淆操作

4.动态代码重写和注入

5.创建元语言

十、with语句

A.with是怎么回事

1.with语句会创建一个作用域,在该作用域内,在引用特定对象的属性时,可以不使用前缀

2.在with语句的作用域内,对象属性的优先级绝对高于在更高层级 作用域内定义的同名变量,作用域内的代码意义可能是含糊不清的

3.它降低了所包含JS代码的执行性能

十一、开发跨浏览器策略

A.五大开发关注点

1.浏览器的bug

2.浏览器bug修复

3.与外部代码一起共存

  • 封装代码
  • 处理不太典范的代码
  • 避免植入属性:hasOwnProperty()
  • 贪婪ID复制
  • 样式表排序

4.缺失的功能

  • 优雅降级
  • 向后兼容

5.回归

B.实现策略

1.安全的跨浏览器修复:对其他浏览器来说,没有负面影响或副作用;不需要进行浏览器检测或特性检测;

2.对象检测:确定某一对象或对象的属性是否存在,如果存在,则假设它包含了暗指的功能,多用于在提供重复功能的多个API之间进行选择

3.特征仿真:确保特征能按预期工作,为不能按预期工作的浏览器提供一个备用操作

4.不可检测的浏览器问题

  • 事件处理绑定
  • 事件触发
  • CSS属性效果
  • 不一致的API
  • API性能
  • AJAX问题

C.减少假设

1.应该努力减少所做的假设,有效地减少出错的可能性,以及一些会在背后攻击我们的问题的可能性

十二、洞悉特性、属性和样式

A.DOM特性和DOM属性

1.属性:div.id,特性:div.getAttribute(“id")

2.属性和特性需要考虑

  • 跨浏览器命名
  • 命名限制
  • HTML和XML之间的差异
  • 自定义特性的行为
  • 性能注意事项:属性比特性快

B.跨浏览器的attribute问题

1.DOM中的id/name膨胀:表单中的input元素用id和action做为id时会覆盖表单的属性

2.URL规范化:在访问同一个引用了URL的属性时(href、src或action),该URL值会自动将原始值转换成完整规范的URL

3.style特性

4.type特性:IE中不能修改type

5.tab index 问题:如果不显式设置tab index,就无法获取到一个元素的tab index

6.节点名称:HTML中nodeName返回的都是大写,XML或XHTML中返回正常的

C.令人头疼的样式特性

1.样式属性命名:会将CSS中的样式转成驼峰格式

2.float样式:IE使用styleFloat,其他使用cssFloat

3.测量元素的高度和宽度:offsetHeight()、offsetWidth(),包括border、padding

D.获取计算样式

1.window.getComputedStyle()

十三、不老事件

A.绑定和解绑事件处理程序

1.addEventListener()和removeEventListener(),IE9之前使用attachEvent()和detachEvent()

B.处理程序的管理

1.集中存储相关信息:将所有的数据保存在一个集中对象上可以避免IE浏览器的潜在内存泄露问题

2.管理事件处理程序

C.事件触发

1.优点之一是,我们想创建多少就可以创建多少,并且这些处理程序是完全独立的。

2.自定义事件是模拟真实事件的体验,而无需得到浏览器底层事件的支持

D.冒泡与委托

1.委托(delegation)是表示在DOM上层定义事件处理程序,而不是在触发事件的元素本身上定义

十四、DOM操作

1.将HTML文本片段注入到一个临时元素的innerHTML属性中,是一个可以将HTML文本字符串转换成DOM元素的快速且简单的方式

十五、CSS选择器引擎

A.W3C Selectors API

1.使用querySelector()和querySelectorAll()

2.执行一个元素级查询时,选择器只检查选择器的最后一部分是否包含在元素中

B.利用XPath查找元素

C.纯DOM实现

1.向后兼容、速度尚可、全覆盖

https://github.com/zhangyue0503/html5js/blob/master/javascriptninja/

本文分享自微信公众号 - 硬核项目经理(fullstackpm),作者:ZyBlog

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vim实用技巧

    1.*进行查找,一是光标会跳到下一个匹配项上,二是所有出现这个词的地方都会被高亮显示出来。如果没有高亮,运行:set hls

    硬核项目经理
  • 代码整洁之道【笔记】

    一、整洁代码 A.混乱的代价 1.有些团队在项目初期进展迅速,但有那么一两年的时间却慢去蜗行。对代码的每次修改都影响到其他两三处代码 2.花时间保持代码整洁不但...

    硬核项目经理
  • 编写可维护的JavaScript

    • 在方法中的局部变量(local variable)和第一条语句之间

    硬核项目经理
  • JDK集合框架小结

    前面的一些文章主要分析了 Java 集合框架(Java Collections Framework, JCF)中常用的类和接口,本文打算做个整体的小结。

    WriteOnRead
  • [PHP] 2018年终总结

    ========================================================================== 2018年...

    陶士涵
  • javaScript

    访问HTML元素元素可使用document.getElementById(id)方法

    金GoS
  • 不会用partial,别说你会python

    用户1634449
  • Redis并发问题

    我们都知道Redis 是单线程的,那么如果单单是放一次以供查询,或者使用list追加放入以供查询,基本上没有太大的问题。但是如果说要根据原有的数据进行计算,怎么...

    yaphetsfang
  • linux内核启动流程分析 - efi_pe_entry

    接上一篇文章 linux内核启动流程分析 - efistub的入口函数,我们继续看efi_pe_entry这个函数。

    wangyuntao
  • Python实战(1)模拟wc命令部分功

    1、open(filename) 传入的是变量filename 不要写成open('filename'),不然传入的就是字符串不是变量了

    py3study

扫码关注云+社区

领取腾讯云代金券