javascrpt知识要点梳理——知识清单

写在前面:

这篇文章是我在学习《javascript高级程序设计(第三版)》时随书所做的笔记,由于使用word记录的笔记,转到markdown编辑器里有点吃力,所以文章排版可能会有点问题。感兴趣的朋友可以去github看看我的项目,这篇文章的word文档也在项目文件夹里放着。如果这个项目对你产生了帮助,记得帮我点个小星星呀~
另外,这篇文章更适合有基础的同学当作知识清单使用,可以帮助你回顾知识,大家可以去下载word文档并且打印出来,帮助自己回顾复习,梳理脉络,让我们一起努力学习前端吧!

JavaScript实现:

(1) 核心(ECMAScript):提供核心语言标准
(2) 文档对象模型(DOM):提供访问和操作网页内容的方法和接口
(3) 浏览器对象模型(BOM):提供与浏览器交互的方法和接口

Function类型:

  1. 函数实际上是对象,每个函数都是Function类型的实例。除了通过function关键字定义以外,也可以使用new Function()的方式定义,但该方式需要多次解析代码,效率较低。
  2. 函数名仅仅是指向函数对象的指针。
  3. 函数对象有一个属性caller,保存了调用当该函数的函数的引用。还有一个对象属性prototype是保存他们实例方法的真正所在,该属性不可枚举,主要用于定义自定义引用类型和实现继承。
  4. 函数内部有两个特殊属性:arguments(保存参数)和this。arguments有个一叫做callee的属性,是一个指向拥有该arguments对象的函数的指针。
  5. 函数不能重载,但可通过特殊方法简单实现类似重载的效果,例如使用arguments对象或“…”操作符。
  6. 函数只是在特定环境下执行代码的对象,因此函数的作用域是可以改变的,通过apply()和call()可以改变函数内部this指针的指向,从而设置函数执行时的作用域。还可以用bind()方法。
  7. 函数本身可以作为参数传递给另一个函数,也可以在一个函数中返回另一个函数。

函数表达式:

  1. 函数表达式不同于函数声明,函数声明要有函数名,但函数表达式不需要,没有名字的函数表达式交叫做匿名函数(拉姆达函数)。解析器会优先读取函数声明,即函数声明提升,故执行语句可以放至声明语句之前,而函数表达式则不行。
  2. 递归:递归函数影应始终使用arguments.callee来递归地调用自身,不要使用函数名——函数名可能会发生变化。在无法确定如何引用函数的情况下,递归就会变得比较复杂。
  3. 闭包:指的是(有权访问另一个函数作用域中的变量的)函数。当在函数内部定义了其他函数时,就创建了闭包。
    (1) 在后台的执行环境中,闭包的作用域链包含着自己的作用域、包含函数的作用域、和全局作用域。
    (2) 通常函数的作用域会在函数执行完毕时销毁,但是当一个函数返回一个闭包时,这个函数的作用域将会一直存在到闭包不再存在为止。使用闭包可以在js中模仿块级作用域(js本身没有块级作用域的概念)。
    (3) 创建并立即调用一个函数,既可以执行其中的代码,又不会在内存中留下对该函数的引用。但是结果就是函数总所有变量会被立即销毁——除非将某些变量赋值给了包含作用域(即外部作用域)中的变量。
    (4) 闭包还可用于对象中创建私有变量,即便JavaScript中没有正式的私有变量的概念,但是可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量。有权访问私有变量的公有方法叫做特权方法。
    (5) 使用构造函数模式,原型模式来实现自定义类型的特权方法,也可以用模块模式,增强的模块模式来实现单例的特权方法。
    (6) 每个函数都是闭包,只不过我们常说的闭包是在一个函数中返回另一个函数的这种情况。闭包可能会造成内存泄露(内存无法回收),要格外注意。
  4. 块级作用域:可以用匿名函数模拟。(function(){ /代码区域/ })(),给匿名函数声明套上括号再跟一对括号。相当于定义匿名函数后立即调用。这种方式可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,所以只要函数执行完毕就可以立即销毁其作用域链了。
  5. 私有变量:函数参数、局部变量、函数内部定义的函数。

引用类型:

1. 属性:

Object.getOwnPropertyDescriptor(obj,name)可以取得给定属性的描述符,返回值是一个对象,具有configurable,enumberable属性,若是数据属性还具有writerable、value,若是访问器属性则具还有get,set属性。另外,这些属性的可以自行修改,使用方法Object.defineProperty(obj,name,{})。任何对象都适用。

2. 创建对象:

(1) 工厂模式:使用简单函数创建对象,为对象添加属性和方法,然后返回对象。该方法没有解决对象识别问题,后被构造函数模式代替。
(2) 构造函数模式:可以创建自定义引用类型,可以像创建内置对象实例一样使用new操作符。特点:没有显式创建对象,直接将属性和方法赋值给this对象,没有return语句。问题:成员无法复用,包括属性和方法。由于函数不局限于对象,不局限于执行环境,所以没有理由不复用。
(3) 原型模式:向构造函数的原型对象(prototype)中添加属性,使得创建的实例之间共享这些属性。
问题:
a. 如果原型对象中包含引用类型属性,例如Array类型,会导致所有实例共享修改这一属性。
b. 在创建实例后修改原型对象,会切断现有实例和新原型之间的联系。
(4) 组合模式:使用构造函数模式和原型模式。前者用于定义实例属性,后者用于定义方法和共享属性。
① 优点:最大限度的节省了内存。
② 缺点:构造函数和原型分开编写,没有整体的感觉,可能会给编程人员带来困惑。
(5) 动态原型模式:将组合模式中的的两部分代码封装到一起。对于需要创建的方法进行判断,若不存在则创建,否则什么也不干(原型部分只会在初次调用构造函数时创建,且不能使用对象字面量方式定义,会切断已创建实例与原型之间的联系)。
(6) 寄生构造函数模式:通过构造函数末尾添加return语句,重写调用构造函数时的默认返回值。特殊情况下(如创建一个有额外方法的特殊数组,因为不能直接修改Array构造函数)使用。
缺点:返回对象与构造函数或其原型属性没有关系,与在构造函数外部直接创建对象等价,本质上是工厂模式的一个变形。
(7) 稳妥构造函数模式:模式基本与寄生模式先相同,但不用new,也不用this。返回一个稳妥的对象。特点:安全性高,类似于c++中的类,可以包含私有属性。该方法创建的对象和寄生构造函数模式一样,与构造函数之间没有什么关系,是工厂模式的变形。

3. 继承

  1. 构造函数与函数原型的关系:构造函数Person(对象)有个属性prototype(指针)指向原型对象。 Person.prototype(原型对象,实质也是对象)他有个属性constructor(指针) ,又指向 Person函数对象。constructor属性指向创建此对象的构造函数的引用
  2. 原型链继承:原型对象等于另一个类型的实例,进而构成原型链。是javascript的主要继承手段。问题:包含引用类型的原型会导致共享修改。创建子类时不太容易向父类传递参数(使用借用构造函数的方式)。故一般不会单独使用。
  3. 借用构造函数继承:函数只是在特定环境下执行代码的对象,因此通过使用apply(),call()方法,可以在新创建的对象上执行构造函数并传递相应参数。但是由于使用了构造函数,无法避免构造函数模式下的一些问题——方法都在构造函数中定义无法复用。因此该方法一般不单独使用。
  4. 组合继承:将原型链方式和借用构造函数方式的继承思想结合,原型链继承公共属性和方法,借用构造函数继承实例属性。问题:无论什么情况下,总会调用两次父类的构造函数,一次是创建子类原型时,一次是在子类构造函数内部。解决办法——寄生组合式继承。
  5. 原型式继承:Object.create(父类,属性值)或object(父类)。将返回一个将父类作为原型的对象。本质上是对给定对象的浅复制,用于只想让一个对象和另一个对象保持类似,不必兴师动众的创建构造函数时。注意包含引用类型的值属性始终共享。
  6. 寄生式继承:创建一个仅用于封装继承过程的函数,函数内部克隆对象,增强并返回该对象(object和create函数不是必须的)。基本思路类似于寄生构造函数与工厂模式。缺点也类似,由于不能复用函数而降低效率等。
    a. 调用函数复制父类原型创建一个新对象(利用Object.create()或object())。
    b. 通过某些步骤增强该对象,例如添加方法。
    c. 返回创建的新对象。
  7. 寄生组合式继承: 通过借用构造函数继承属性,通过原型链混成形式来继承方法。本质上是使用寄生式继承父类的原型来继承属性(替代了之前在子类型里调用父类构造函数的过程,我们所需的不过是父类原型的一个副本而已),然后再将结果指定给子类原型。
    a. 复制父类原型。
    b. 添加因重写原型失去的constructor属性。
    c. 将复制的原型赋值给子类原型。
    其高效性体现在只调用了一次父类的构造函数,避免了在子类原型中创建不必要的多余的属性。与此同时原型链也不变。可以利用instanceof和isPrototypeOf()确定类型。开发人员普遍认为该方式是引用类型最理想的继承范式。

4. BOM

浏览器对象模型(BOM)以window对象为依托,表示浏览器窗口以及页面可见区域。同时,window对象还是ECMAScript中的Global对象,因而所有全局对象都是他的属性,所有原生构造函数以及其他函数也都存在于他的命名空间下。

  1. 使用框架时,每个框架有自己的window对象以及所有原生构造函数和其他函数的副本。每个框架都保存再frames的集合中,可以通过位置或者名称来访问到。
  2. 有一些窗口指针可以用来引用其他框架,包括父框架。
  3. top对象始终指向最外围的框架,也就是整个浏览器窗口。
  4. parent对象表示包含当前框架的框架,即父框架,而self对象则指向window。
  5. 使用location对象可以通过便程的方式来访问浏览器中的导航系统。设置相应的属性,可以逐段或者整体性地修改浏览器的URL。
  6. 调用页面的replace()方法可以导航到一个新的URL,同时该URL会替换浏览器历史记录中当前显示的页面。
  7. navigator对象提供了与浏览器相关的信息。到底提供哪些信息,很大程度上取决于用户的浏览器;不过,也有一些公共的属性例如userAgent存在于所有浏览器中。

BOM中还有两个对象:screen和history,但他们的功能有限。screen对象中包含着与客户端显示器有关的信息,这些信息一般只用于站点分析。history对象为访问浏览器历史记录开了一个缝隙,开发人员可以据此判断历史纪录的数量,也可以在历史中向后或者向前导航到任意界面。

5. DOM

DOM用于访问和操作HTML和XML文档。DOM1级将HTML和XML文档视作节点树,可以使用js操作,改变其外观和结构。DOM由各种节点构成。

  1. 最基本的节点类型是Node,用于抽象表示文档中一个独立的部分;所有其他类型都继承自Node。
  2. Document类型表示整个文档,是一组分层节点的根节点。再JavaScript中,document对象是Document类型的一个实例。使用document对象可以查找并获取节点。
  3. Element类型节点表示文档中所有HTML和XML元素,可以用来操作这些元素的内容和特性。
  4. 还有一些节点类型,Text,Comment,Attr,DocumentType,CDATASection,DocumentFragment等
    理解DOM的关键在于理解DOM对性能的影响。DOM操作往往是JavaScript中开销最大的部分,而 因访问NodeList导致的问题最多。NodeList对象都是“动态的”,这意味着每次访问都会运行一次查询。有鉴于此,最好的办法是尽量减少DOM操作。

6. 事件(注意点)

  • 有必要限制页面中事件处理程序的数量,数量太多会导致占用大量内存,降低性能。
  • 可以使用建立在事件冒泡机制上的事件委托技术,可以有效减少事件处理程序的数量。
  • 建议在浏览器卸载页面之前移除页面中所有的事件处理程序,防止内存泄漏。

7. JSON

EMCAScript5定义了原生的JSON对象,可以用来将对象序列化为JSON字符串(JSON.stringify()方法)或者将JSON数据解析为JavaScript对象(JOSN.parse()方法)。

1.JSON.stringify()方法

a) 接受三个参数,第一个参数是相应的JavaScript对象,必须。其他两个非必须。
b) 第二个参数用来过滤结果,可以是数组,或函数。若是数组则过滤后的结果只包含数组中列出的属性,若是函数则会执行更精确的操作。函数接受两个参数(key,value),并返回过滤结果
c) 第三个参控制缩进,若是数字则是空格缩进,数字代表空格数量,最大为10。若是字符串,则会用字符串作为缩进,最大长度也为10,多余会截取
d) toJSON()方法可以作为函数过滤器的补充。可以为任何对象添加toJSON()方法,返回其自身的JSON数据格式
stringify()方法执行过程

  • 若存在toJSON()方法,,而且能通过它取得有效的值,则调用该方法。否则返回对象本身。
  • 如果提供了第二个参数,应用这个过滤器,传入的值是第一步返回的值
  • 对第二部返回的值进行相应的序列化。
  • 如果提供了第三个参数,执行相应的格式化。

2. JSON.parse()方法

  • 可接受两个参数,第一个是JSON字符串,必须。另一个是还原函数,可选。
  • 在将JSON字符串中的日期对象转换为Date对象时,经常需要用到还原函数,参数(key, value),类似于过滤函数,也需要返回值。

8. 表单脚本

1. 表单基础操作:submit事件,change事件,focus事件等。

2. 文本框脚本:

  1. 选择文本——包括select事件。
    1. 取得选择的文本——selectionStart,seletionEnd等。
    2. 选取部分文本——setSelectionRange(),利用DOM范围。
  2. 过滤输入——不同浏览器对剪切板对象的访问具有不同限制。如chrome只允许在发生相关的剪切板事件的时候才允许访问剪切板对象。
    1. 屏蔽字符——利用keypress事件检测按键,屏蔽掉不合法的输入字符。
    2. 剪切板事件——ocopy,beforecopy,paste,beforepaste等,利用event.clipboardData对象访问数据,主要方法:
    1. getData(取得数据的格式,text或者text/plain)
    2. setData(格式,值)
    3. clearData()。
  3. 自动切换焦点——如电话号码,身份证号,生日等,可以由多个输入框控制输入,当一个输入框达到最大字符限制的时候,自动将焦点切换至下个输入框。
  4. HTML5约束验证API
    1. 必填字段——require
    2. 其他输入类型——url,email
    3. 数值范围——number,range,datetime,datetime-local,date,month,week,time,min,max,step等
    4. 输入模式——pattern属性,值为正则表达式,用来验证输入内容
    5. 检测有效性——必填字段无值或与pattern属性不匹配都视为无效。使用checkValidity()检测,可于表单整体检测,也可于某一个表单元素上检测。还有一个更精细的检测,使用validity属性。
    6. 禁用验证——通过设置表单的novalidate属性,可以告诉表单不进行验证。如果一个表单中有多个提交按钮,为了指定点击某个提交按钮不必验证表单,可以在相应的按钮上添加formnovalidate属性。

3. 选择框脚本:选择框基本元素——的add方法。
  • 移除选项
    ① DOM的removeChild()方法
    ② 选项框调用remove()方法
    ③ 相应选项设置为null
  • 移动和重排选项——appendChild()传入已有元素会删除原元素并添加到指定为位置,类似的还有insertBefore()
  • 4. 表单序列化要点:

    • 对表单字段名称和值进行URL编码,使用“&”分隔
    • 不发送禁用的表单字段
    • 直达送勾选的复选框和单选按钮
    • 不发送type为“reset”和“button”的按钮
    • 多选框中每个选中的值单独一个条目
    • 单击按钮提交表单,也会发送提交按钮;否则不发送提交按钮。也包括type为image的input元素。