* 文中部分内容为《JavaScript高级程序设计》一书中的内容
第一章 JavaScript简介
JavaScript诞生于1995年,最开始仅用于处理输入验证操作,之后发展成为一门功能全面的编程语言。
JavaScript有三个重要组成部分:
1. ECMAScript —— 核心:对实现该标准规定的各个方面内容的语言描述,ECMAScript是标准,JavaScript是对ECMAScript的实现。本书基于ES5(ECMAScript5)进行讲解,即于2009年12月3日发布的版本。
2. DOM —— 文档对象模型:其将整个页面映射为一个多层结点的结构,提供访问和操作页面内容的方法和接口。DOM1(映射结构)、DOM2(视图、事件、样式,遍历和范围)、DOM3级(扩展:统一方式加载和保存文档、文档验证、对DOM核心进行扩展)。
3. BOM —— 浏览器对象模型:提供与浏览器交互的方法和接口。
第二章 在HTML中使用JavaScript
通过<script>标签进行JavaScript代码的插入
标签位置一般放在页面最后,即</body>前
<noscript>标签:早期浏览器有些不支持script标签,用此标签来平稳退化
重要属性:
1. src:外部文件路径
2. defer: 延迟脚本,立即下载,延迟执行,当文档完全被解析和显示之后再执行,仅对外部文件有效,总是按照插入顺序执行。
3. async: 异步脚本,立即下载该脚本,但不妨碍页面的其他操作,仅对外部文件有效,不保证执行顺序
两种使用方法:
1. 直接在页面中嵌入JavaScript代码,并用<script>包裹
2. 使用src,将外部JavaScript文件引入
第三章 基本概念
大小写敏感,语句末尾;号,可省略但不建议,建议始终在控制语句中使用代码块
松散类型变量,变量可存储任何类型的数据
未初始化的变量,值为undefined,未声明的变量也保存undefined
标识符:即变量、函数、属性名字、函数参数
命名规范:字母、数字、下划线、$符号,其中不能以数字开头,ES标准采用驼峰大小写格式
注释:
// 单行注释
/*
* 多行注释
*/
关键字&保留字:
不能作为标识符
var用于声明变量,不使用var则声明了一个全局变量
数据类型:
1. Undefined: 其值为undefined,派生自null,因此undefined == null返回true
2. Null: 其值为null,表示一个空对象指针
3. Boolean:其值有true和false,大小写敏感,因为JS大小写敏感,JS中所有类型的值都有其对应的Boolean值;“”、0、undefined、null、NaN——false;非空字符串、非零数字、任何对象——true
4. Number:十进制为最基本数字字面量格式,浮点数值、整数值、NaN
5. String:"&' 都可以,但必须成对出现,length属性,字符串由0或多个Unicode字符组成
6. Object: 数据和功能的集合,Object是所有它的实例的基础,是所有对象的基础
前五个为简单数据类型,也称为基本数据类型;最后一个为复杂数据类型
typeof操作符,不是函数,用于判断变量的数据类型,其中 typeof null == Object
typeof 一个函数 == function
Number:
NaN非数值,其与任何值都不相等,包括它自己;任何涉及NaN的操作,都会返回NaN,isNaN()函数,该函数接收到参数时,会尝试将参数转为数值,同Number()函数
数值转换:Number(), null=>0、undefined=>NaN、"123hhhh"=>NaN
parseInt(), 在处理字符串时,若第一个字符不是数字或符号,则返回NaN,其他如: “123hhh”=>123
parseFloat(),仅解析十进制,且始终忽略前导零
String:
字符串转换:toString()方法,其中undefined和null没有此方法
String()转型函数,若存在toString()方法,则调用toString(),若为null=>"null"、undefined=>"undefined"
Object:
每个Object都有如下属性和实例:constructor、hasOwnProperty()、isPrototypeOf()、propertyIsEnumerable()、toLocalString()、toString()、valueOf()
操作符:
一元操作符
递增/递减:++、--,分前置和后置,前置则表示先执行++/--,再执行当先所在表达式,后置相反
一元加/减:+、- ,其中一元加,可用于将变量转为数值,同Number()
位操作符:
布尔操作符:
非:!,此操作符始终返回一个布尔值,同时使用!!可获得当前变量对应的Boolean值,同Boolean()函数
与:&& 短路操作
或:|| 短路操作、可用于赋值时,提供备用值,避免出现变量赋值为null/undefined
乘性操作符:* / % 乘 除 模
加性操作符: + -,其中加法可用于字符串拼接
关系操作符:> < >= <= 均返回一个布尔值,优先数值比较,即当一个操作数为数值,则将另一个操作数转为数值再进行比较;若一个操作数是布尔值,也将其先转化为数值;两个字符串的比较,实际上是比较字符串的编码值。
相等操作符: == != 相等&不相等,先转换再比较 === !== 全等&不全等,仅比较
转换时优先数值比较,但是null和undefined做比较前,不会被转换为数值。
null == undefined =>true
null == 0 =>false undefined == 0 =>false
条件操作符:variable = boolean_expression ? true_value : false_value
赋值操作符:= 右边赋值给左边
语句:
if...else...
do-while
for: 其中通过var在for语句中定义的变量,在外部也可以访问
for-in: 可以用来枚举对象中的属性,ES对象中的属性没有顺序,因此该语句循环出的属性顺序是不可预测的
break & continue 跳出循环 :跳出当前循环,进入下一循环
switch语句: 语句中比较使用的是===全等,所以不会发生类型转换
函数:
1. 函数在任何时候都可以通过return返回想要返回的值,仅返回return,则函数会停止执行后返回undefined
2. ES中不介意传入多少参数,同时,命名的参数只提供便利并非必须。且在函数内部可以使用arguments对象访问参数,arguments对象与数组类似但不是Array的实例。使用length属性可以确定传入参数的个数。
3. 函数没有重载,后声明的覆盖前面的同名函数,因为ES函数不存在函数签名的特性
第四章 变量、作用域和内存问题
ES存在两种数据类型的值:基本类型值 & 引用类型值
1. 基本类型值: Undefined、Null、Number、Boolean、String,按值访问,保存在栈内存
2. 引用类型值: Object、Array、Date 、RegExp 、Function 等,内存中的对象,按引用访问,保存在堆内存
两种类型的值复制变量的过程如下图:
ES中所有函数的参数都是按值传递的,参数是函数的局部变量。
检测类型:
两个操作符:
1. typeof
2. instanceof 返回true或false
作用域链:保证对执行环境有权访问的所有变量或者函数的有序访问,全局执行环境的变量对象始终是作用域链中的最外层,即兜底。
内部可往外部访问查找想要的标识符,外部不能下钻查找。
ES5中使用var声明变量,是不存在块级作用域的。如:if语句中声明的变量,在外部依然能访问。
使用var声明的变量会被自动加到最接近的环境中。
JS有自动垃圾收集机制
第五章 引用类型
描述的是一类对象所具有的属性和方法,对象是某个引用类型的实例
1. Object 类型: ES中使用最多的一个类型 person = new Object() person={x:123,y:123} 构造函数&字面量,属性的访问支持. 或者 [ ],当要访问的属性名为变量时使用方括号
2. Array 类型:new Array() 当传给构造函数的参数为数值,则为数组长度; arr = [1,2,]这样则会创建一个长度为2或3的数组 Array.length 不是只读的,可以通过设置其值来改变数组长度
数组检测:Array.isArray(value)
Array.toString()返回以逗号分隔的字符串
Array.join() 可传参,参数即为分隔符,不传则默认是逗号分隔
push()末尾添加,返回修改后的数组长度
pop()末尾删除,返回删除项的值
shift()头部移除,返回删除项的值
unshift()投不添加,返回修改后数组长度
reverse(),反转数据项顺序
sort()排序,其原理是先调用toString()再排序,再比较字符串,可向其传递一个比较函数,若第一个数要在第二个数之前,则返回负数。
concat()数组拼接,原数组不变,生成一个新数组
slice()数组截取,既是截取,因此原数组不变,单拎出截取片段而已。接收两个参数,截取一头一尾不包含尾巴。若传入数值为负,则加上数组长度再截取。若头大于尾,返回空数组。
splice()拼接方法,在原数组基础上变化,接收三个参数,分别代表,要操作起始位置,要删除的项数,要插入的具体值,始终返回从原始数组删除的项的值。
indexOf() lastIndexOf() 位置方法,找对应值的索引,接收两个参数,分别为,要查找的值、开始查找的索引
every() 每个值都满足给定函数则返回true
filter() 返使函数返回true的项所组成的数组
forEach() 对数组中每一项执行函数,该方法没有返回值
map() 对数组中每一项执行函数,并返回函数执行后组成的数组
some() 任意一个值满足给定函数则返回true
以上五个迭代函数都接受两个参数,1️⃣要运行的函数,其中此函数有三个参数,分别是当前item,index,array本身;2️⃣函数的作用域对象。
reduce() reduceRight(): 归并方法,接受两个参数:1️⃣要执行的函数,该接受四个参数,前一个值,当前值,项的索引,数组对象;2️⃣可选,最为归并基础的初始值
两个函数遍历方向相反!
3. Date 类型:var date = new Date()不传参,则自动获取当前时间
常用方法:Date.now() 获取当前时间戳 也可使用 +new Date()
getFullYear() 获得年份 getMonth()获得月份,0:1月 11:12月
getDate() 获得日期月份中的天数(1-31) getDay()获得星期几 0:周日 6:周六
4. RegExp 类型: 见JS正则基础
5. Function 类型: JS中函数即使对象,都有其属性和方法。同时,函数名则仅仅是一个指向函数对象的指针。
函数声明(变量提升) & 函数表达式
使用不带圆括号的函数名仅仅是访问函数指针,并没有调用函数
函数没有重载,函数可作为值来传递,也可从一个函数中返回另一个函数
函数内部属性:arguments对象 & this函数执行环境的对象 & caller属性(调用该函数的函数的引用),其中arguments对象中有一个callee的属性,该属性指向拥有该arguments对象的函数。
函数的属性和方法:每个函数都包含的两个属性length & prototype,分别表示:函数希望接收到的命名参数的个数 & 对于ES中的引用类型而言,prototype保存了他们的所有实例方法(ES5中prototype属性本身是不可枚举的,因此使用for-in发现不了)
同时,每个函数还包含两个非继承而来的方法apply(在其中运行函数的作用域, [参数数组]) & call(在其中运行函数的作用域, 参数)
bind()方法,该方法会创建一个函数实例,则函数执行的this值会绑定到传给bind方法的值。
基本包装类型:Boolean、Number、String,与引用类型最大的区别就是对象的生命周期。自动创建的基本包装类型的对象,只存在于一行代码的执行瞬间,之后立即被销毁。
Number类型常用方法:toFixed(),按照指定的小数位,返回数值的字符串表示
toExponential(),按照指定的小数位,返回指数表示法的字符串
String类型的常用方法:
length属性:返回字符串长度
charAt(),返回给定位置上的字符 charCodeAt(),返回指定位置的字符编码
concat()字符串拼接,得到拼接后的新字符串,不过更常用+
三个基于子字符串创建新字符串的方法:slice()、subStr()、subString()
slice():一或两个参数、一头一尾不包含尾,截取字符串,若传入负数则加上字符长度再截取,若头大于尾,返回空字符串;若不传第二个参数,则截取到末尾
subString():一或两个参数、一头一尾不包含尾,截取字符串。若传入参数为负,则转为0.
subStr():一或两个参数,头和截取长度,不传长度则截到末尾。若传入参数为负,第一个加长度,第二个转为0.
indexOf(): 返回指定字符的索引,一个或两个参数:指定的字符,开始查找的位置
lastIndexOf():查找方向相反,其余同上
trim():创建字符串副本,去掉字符串前置及后缀所有空格
trimLeft()、trimRight()同方法字面意思
字符大小写转换方法:toLowerCase() toLocaleLowerCase() toUpperCase() toLocaleUpperCase()
match(),字符串匹配方法,仅接受一个参数,RegExp对象 || 正则表达式
search(),参数同上,若找到则返回第一个匹配索引,若没找到,则返回-1
replace(a,b):将字符串中a替换为b,a可为字符串或RegExp对象,b可为字符串或者一个函数
split(),将字符串以指定的参数切割为多个字符串,返回数组,还可以接受第二个参数,用来指定返回数组的长度。
单体内置对象:Global & Math
Global对象:
encodeURI() & encodeURIComponent() : 对URI进行编码,对URI中无效的字符进行编码
encodeURI() 针对整个URI,不会对本身属于URI的特殊字符进行编码,如# ?等
encodeURIComponent() 针对URI中某一段,对任何非标准字符串进行编码。
decodeURI() & decodeURIComponent()
eval(), ES中最强大的一个方法,仅接受一个参数,即要执行的ES字符串
当中创建的变量不会被提升。*恶意用户输入威胁站点的或应用程序安全的代码,即为代码注入。
虽然没有直接访问Global的方式,但是Global对象作为window对象的一部分,因此在全局作用于声明的变量和函数,都成为了window的属性,都可以通过window对象访问。
Math 对象:
常用方法 max() min() random() floor() ceil()
Math.max.apply(Math,[1,2,3,2,5]) 返回5
random():返回一个[0,1)之间的整数
随机生成一个范围内的数:Math.floor(Math.random() * 可能的值的总数 + 第一个可能的值)
第六章 面向对象的程序设计
ECMA-262把对象定义为:无序属性的集合、其属性可包含基本值、对象或者函数
属性类型:数据熟悉 & 访问器属性
属性有其内部特性,其中数据属性有如下特性:[[Configurable]] [[Enumerable]] [[Writable]] [[Value]] 直接在对象上定义的属性前三个特征默认值均为true,Value则默认undefined。
若要修改属性内部特性,使用object.defineProperty(属性所在对象,属性名字、描述符对象)
*若一个属性一旦被定义为不可配置,则不能再将其变为可配置
访问器属性:不包含数据值、包含一对getter & setter 函数,不过这两个函数都不是必需的。
访问器属性有如下特性:[[Configurable]] [[Enumerable]] [[Get]] [[Set]] 直接在对象上定义的属性前两个特征默认值均为true,后两个为undefined
访问器属性不能直接定义,必须通过object.defineProperty()定义
同时定义多个属性可用object.defineProperties()
访问属性特性:object.getOwnPropertyDescriptor(所在对象,要读取其描述的符的属性名称)
创建对象
1. 工厂模式:在一个函数里面创建对象、定义属性和方法、返回对象
存在的问题:虽然创建了对象,但是不能确定该对象是一个什么类型
2. 构造函数模式:没有显示创建对象、属性和方法直接赋给this对象,没有return。
构造函数函数名首字母大写,通过new操作符创建新实例。eg: var p = new Person("shu","24","teacher")
这种方式内部步骤如下:1️⃣创建一个新对象,2️⃣将构造函数的作用域指定给该对象、3️⃣执行构造函数中的代码给该对象定义属性和方法,4️⃣返回该对象
这样创建出来的实例都有一个constructor属性,是一个指向函数prototype属性所在函数的指针
,可以使用constructor属性来表示对象类型,但一般都用instanceof操作符
存在的问题:每个方法都要在每个实例上创建一遍,不同实例上的同名函数是不相等的。因此可以通过把函数定义到构造函数外部这个方式来解决这个问题。但是,如果一个对象要定义很多方法,则该方式就很不实用了。
3. 原型模式:不用在构造函数中定义对象实例的信息,将这些信息直接添加到原型对象中
每个函数都有一个prototype属性,该属性是一个指针,指向一个对象(原型对象),该对象包含了特定类型的所有实例对象共享的属性和方法。
无论什么时候,只要创建了一个函数,就回为函数创建一个prototype属性,该属性指向函数的原型对象。默认情况下,原型对象都会自动获得一个constructor属性,该属性是一个指向prototype属性所在函数的指针。
当调用构造函数创建一个实例之后,该实例内部包含一个指向构造函数原型对象的指针[[prototype]],针对该指针,在脚本中没有标准的访问方式,但部分浏览器在每个对象上都支持一个属性__proto__
虽然在所有实现中都无法访问[[prototype]],但可以使用isPrototypeOf()方法来确定实例和构造函数原型对象之间的关系
ES5中增加了一个获得指定实例原型对象的方法getPrototypeOf(),可以通过对象实例访问保存在原型中的值,但是却不能通过对象实例重写原型中的值。
当为对象添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性,但不会修改原型对象中的属性。
hasOwnProperty(),用来检测某一属性是存在于实例中还是原型中,存在实例中时,返回true
in:单独使用时,不论是实例中还是原型中的属性都会返回true
for-in:则返回所有所有能够通过对象访问的、可枚举的属性,既包括实例中、也包括原型中。
object.key(),返回对象上所有可枚举的实例属性。
通过字面量方法一次性定义属性和方法,实际上是重写了prototype,因此constructor属性也就变成了新对象的constructor属性,指向object,不再指向原来的构造函数。
原型模式的问题:方法虽然共享了,但是属性也共享了,若是存在引用类型的属性,则不同实例间会互相影响。
4. 综上——组合使用构造函数模式和原型模式
在构造函数中定义属性,在原型对象上添加方法。此方法数目前ES中使用最广、认同度最高的一种创建自定义类型的方法。
5. 动态原型模式:把所有信息都封装在构造函数中,并在需要的情况下,在构造函数中初始化原型
动态原型模式中,不能使用字面量来重写原型,因为在已经创建了实例之后,再重写原型,会切断实例和原型间的联系!!!
继承
ES仅支持实现继承
所有函数的默认原型都是Object的实例
1. 原型链继承
给原型添加方法的代码一定要在替换原型的语句之后,因为替换原型本质是重写原型,重写原型会导致原型和实例间的联系被切断。同理,通过原型链实现继承的时候,也不能使用字面量创建原型方法。
存在的问题:由于存在原型变成另一个类型的实例的现象,因此会导致实例属性变成原型属性。当原先的实例属性包含引用类型时,则会相互影响。
2. 借用构造函数继承(伪造对象或经典继承)
在子类型的构造函数中调用超类型的构造函数(个人理解就是把构造函数集中起来创建独立的实例属性)
优势:可以在子类型调用超类型构造函数的时候,向超类型构造函数传递参数
存在的问题:同构造函数模式一样,函数复用不能实现
3. 综上——组合继承(原型链实现对方法的继承,借用构造函数实现对实例属性的继承)
存在的问题:无论什么情况下,都会调用两次超类型的构造函数,分别是1️⃣创建子类型原型的时候2️⃣在子类型构造函数内部。在第一部时子类型已经包含了超类型的对象的全部实例属性、为了防止实力类型中的引用类型共用的问题,在第二步的时候重写了实例属性,阻止我们访问超类型的实例属性。
4. 原型式继承
5. 寄生式继承
6. 寄生组合式继承
寄生组合式继承是引用类型最理想的继承范式
第七章 函数表达式
函数的两种定义方式:1️⃣函数声明,函数会被提升 2️⃣函数表达式,该方法不会被提升
function后面若是没有标识符,则创建出的函数就是匿名函数(拉姆达函数)
闭包:指有权访问另一个函数作用域中的变量的函数
闭包只能取得包含函数中任何变量的最后一个值
this对象:该对象是在运行时基于函数的执行环境绑定的
在全局函数中,this是window;当函数被某个对象调用时,this是那个对象;匿名函数的执行环境具有全局性,因此this对象通常指向window
将函数声明包含在一对圆括号中,实际上表示的是一个函数表达式。之所以要用圆括号包起来实现立即执行,是因为JS会将function关键字当做一个函数声明的开始,而函数声明后面不能直接i跟有圆括号来表示立即执行。