- 一个完整的JavaScript实现三个部分组成:
- 核心(ECMAScript)
- 文档对象模型(DOM)
- 浏览器对象模型(BOM)
-
<script>的几个属性:
- async 不等待脚本下载和执行,异步地加载页面的内容
- charset
- defer 设置该属性,告诉浏览器立即下载,但延迟执行(只适用于外部脚本文件)
- src
- type
- 带有 src 属性的 <script> 不应再嵌入JavaScript代码,如果又嵌入了JavaScript代码,只会下载执行外部脚本文件,嵌入的代码会被忽略
- 在 <head> 中包含所有的JavaScript文件,意味着必须等到全部的代码都被下载、解析、执行完成以后才开始呈现页面的内容(浏览器在遇到 <body> 标签时才开始呈现内容),所以为了避免在呈现页面时出现延迟,一般都把JavaScript引用放在<body>中的页面内容的后面。
- 使用外部脚本的优点:
- 可维护性
- 可缓存
- 适应未来
- 包含在 <noscript> 中的内容(除<script>以外的任何HTML元素)只在以下情况下才显示:
- 浏览器不支持脚本
- 浏览器支持脚本,但脚本被禁用
- ECMAScript区分大小写
- 标识符以字母、下划线、美元符号开头
- 标识符推荐使用驼峰大小写格式,第一个字母小写,剩下的每个有意义的单词首字母大写
- 注释采用C风格的注释,有单行注释和多行注释两种
- ECMAScript 5 引入严格模式,严格模式为JavaScript定义了一种不同的解析与执行模型
- 在整个脚本中启用严格模式:在脚本顶部添加"use strict;"
- 指定函数在严格模式下执行:在函数内部的上方包含"use strcit;"
- 支持严格模式的浏览器:IE10+、Firefox4+、Safari5.1+、Opera12+、Chrome
- 建议在语句的末尾不要省略分号
- 关键字和保留字
- 未经过初始化的变量会保存一个特殊的值:undefined
- 初始化的过程仅仅是给变量赋一个值,不会将其标记为特定的数据类型
- 使用 var 定义的变量会成为定义该变量的作用域中的局部变量
- 省略 var 定义全局变量
-
数据类型
- 5种简单数据类型:
- Undefined
- Null
- Boolean
- Number
- String
- 1种复杂数据类型
- Object
- 5种简单数据类型:
-
typeof 操作符负责检测变量的数据类型
- 值未定义----"undefined"
- 布尔值----"boolean"
- 字符串----"string"
- 数值----"number"
- 对象或null----"object"
- 函数----"function"
- undefined 值实际上是派生自 null 值的,因此,对它们进行相等性测试时会返回true
- 可以对任何数据类型的值调用Boolean()函数将其转换为一个对应的布尔值
- 使用IEEE754标准的浮点数计算时会产生舍入误差,因此不要测试某个具体的浮点数的值
- 最小数值:Number.MIN_VALUE
- 最大数值:Number.MAX_VALUE
- 超出最大/小值,会被自动转换成 Infinity 值(有正\负),该值不能参与计算
- isFinite() 确定参数是否位于最大值和最小值之间,如果是,返回true
- 任何数值除以0会返回NaN
- 任何涉及NaN(如NaN/10)的操作都会返回NaN
- NaN与任何值都不相等,包括NaN本身
- isNaN()函数确定参数是否"不是一个数值"
- 数值转换
- Number()可用于任何数据类型,另外两个专门用于把字符串转换成数值(一元加操作符的操作与Number()函数相同)
-
parseInt()
- 在解析像八进制的字符串时,ES3和ES5存在分歧,为了消除这样的困惑,可以为parseInt()指定第二个参数--转换基数(多少进制)
-
parseFloat()
- 与parseInt()的区别:
- 字符串中的第一个小数点有效,第二个小数点及以后的无效
- 始终会忽略前导的"0",十六进制的字符串始终会被转换成0
- 与parseInt()的区别:
- 以上3个函数对于同样的输入会返回不同的结果
-
String类型
- 单\双引号表示都可
- 字符字面量(转义序列)
- 访问字符串的 length 属性可获得字符串的长度
- ECMAScript中的字符串一旦被创建,值就不能改变,要改变某个变量保存的字符串,要先销毁原来的,然后用另一个新的字符串填充该变量
- 转换为字符串--toString()
- 几乎每个值都有toString()方法,除了null 值和 undefined 值没有
- 多数情况下不必为toString()传递参数,调用数值的toString()方法时,可传递一个参数:输出数值的基数(默认基数为10)
- 在不确定转换的值是否是null或undefined时,可使用String()函数,它能将任何类型的值转换为字符串,其转换规则如下:
- 技巧:要把某个值转换为字符串,可以使用加号操作符把它和一个字符串("")加在一起
-
Object类型
- ECMAScript中的对象,就是一组数据和功能的集合
- 创建对象:使用 new 运算符后跟要创建的对象类型的名称
1 var a = new Object();
- 类似Java中创建对象的语法
- 如果不给构造函数传递参数,可以省略后面的那对括号
- 创建自定义对象,其实就是创建Object实例,并为其添加属性和(或)方法
- 一个重要的思想:Object类型所具有的任何属性和方法也同样存在于更具体的对象中
- Object 的每个实例都有以下属性和方法
- Constructor
- hasOwnProperty(propertyName) (propertyName必须以字符串的形式指定)
- isPrototypeOf(object)
- propertyIsEnumerable(propertyName) (propertyName必须以字符串的形式指定)
- toLocaleString()
- toString()
- valueOf()
- 操作符
- 一元操作符
- 递增(++)
- 前置:。。。。。。。。。。。。。。。。。之前(简记为:“先加后用”),计算机科学领域通常称这种情况为“副效应”
- 后置:递增操作发生在包含它们的语句被求值之后(简记为:”先用后加“)
- 递减(--)
- 前置:。。。。。。。。。。。。。。。。。之前(简记为:”先减后用“),计算机科学领域通常称这种情况为“副效应”
- 后置:递减操作发生在包含它们的语句被求值之后(简记为:”先用后减“)
- 递增和递减不仅适用于整数,还可用于字符串、布尔值、浮点数、对象
- 一元加(+)和减(-)
- 与数学上的完全一样
- 对非数值应用一元加(+)/减(-)时,会像Number()一样,对值执行转换
- 主要用于基本的算术运算,还用于转换数据类型
- 递增(++)
- 位操作符
- 按内存中表示数值的位来操作数值,但并不直接操作64位的值,而是先将64的值转成32位的整数,再执行操作,最后将结果转换回64位
- 对非数值应用位操作符,会先使用Number()函数将该值转换为一个数值(自动完成),然后再应用位操作,得到的结果最终是一个数值
- 按位非(~)
- 本质:操作数的负值减1
- 按位与(&)
- 按位或(|)
- 按位异或(^)
- 左移(<<)
- 有符号右移(>>)
- 以符号位的值填充空位
- 数值向右移动,保留符号位
- 无符号右移(>>>)
- 以0填充空位
- 所有32都向右移动
- 对正数而言,有符号右移与无符号右移的结果相同
- 布尔操作符
- 逻辑非(!)
- 将操作数先转换为一个布尔值,然后对其求反
- 应用两个逻辑非(!!),可模拟Boolean()的行为,将一个值转换为与其对应的布尔值
- 逻辑与(&&)
- 逻辑或(||)
- 利用逻辑或的行为构造的赋值语句
1 var myObject = preferredObject || backObject; 2 //变量myObject将被赋予等号后面两个值中的一个,变量preferredObject包含优先赋给变量myObject的值,变量backObject负责在preferredObject中不包含有效值的情况下提供后备值。如果preferredObject的值不是null,它的值将被赋给myObject,如果是null,则将backObject的值赋给myObject
- 利用逻辑或的行为构造的赋值语句
- 逻辑非(!)
- 乘性操作符
- 包括乘法、除法和模,与Java、C等语言中的相应操作符类似
- 在操作数为非数值的情况下会执行自动的类型转换
- 乘法(*)
- 除法(/)
- (接上面图片中的内容)如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则
- 求模(%)
- 加性操作符
- 加法(+)
- 减法(-)
- 关系操作符(<、>、<=、>=)
- 相等操作符
- 条件操作符(?:)
- 赋值操作符(=)
- 把右侧的值赋给左侧的变量
- 在等号前添加乘性操作符、加性操作符或位操作符,完成复合赋值操作(简化赋值操作,不会带来任何性能的提升)
- 逗号操作符(,)
- 在一条语句中执行多个操作,如:声明多个变量
1 var num1 = 1, num2 = 2, num3 = 3;
- 用于赋值,总是返回表达式中的最后一项
1 var num = (5,1,4,8,0); //num的值为0
- 在一条语句中执行多个操作,如:声明多个变量
-
- 一元操作符
- 语句
- if语句
- do-while语句(后测试循环语句)
- while语句(前测试循环语句)
-
for语句(前测试循环语句)
- ECMAScript不存在块级作用域,在循环内部定义的变量可以在循环外部访问到
-
for-in语句
- 常用于枚举对象的属性
- ECMAScript对象的属性没有顺序,通过for-in循环返回的属性的先后次序因浏览器而异
- 如果要迭代的对象的变量值为null或undefined,for-in语句会抛出错误或者不执行循环体
- label语句(建议使用时命名描述性的标签)
- continue和break语句
-
with语句
- 将代码的作用域设置到一个特定的对象中,可简化多次编写同一对象的工作
1 //用with改写前: 2 var qs = location.search.substring(1); 3 var hostName = location.hostname; 4 var url = location.href; 5 6 7 //用with改写后: 8 with(location){ 9 var qs = search.substring(1); 10 var hostName = hostname; 11 var url = href; 12 }
- 大量使用with语句会导致性能下降,同时会给调试造成困难,开发大型应用时不建议使用
- 严格模式下不允许使用with语句,会被视为语法错误
- 将代码的作用域设置到一个特定的对象中,可简化多次编写同一对象的工作
-
switch语句
- 语法借鉴于C语言
- 自己的特色:
- switch语句中可以使用任何数据类型
- 每个case可以是常量、变量甚至表达式
- 函数
- 使用function关键字来声明,函数名后跟参数、函数体
- 不必指定是否返回值
- 位于return语句后面的代码不会执行
- return语句可以不带任何返回值,这时函数在停止执行后将返回undefined值
- 严格模式下对函数有一些限制:
- 不能把函数命名为eval或arguments
- 不能把参数命名为eval或arguments
- 不能出现两个形参同名的情况
- 关于参数
- 与大多数其他语言有所不同,ECMAScript中的参数在内部是用一个数组来表示的,函数接受到的始终是这个数组,并不关心其中有哪些参数,有几个参数,参数是什么类型。即便你定义的函数只接收两个参数,你也未必只能传递两个参数,可以传递一个、三个甚至不传递参数
- 实际上,在函数体内可以通过arguments对象来访问这个参数数组
- arguments对象只是与数组类似,并不是的Array实例
- 可以使用方括号访问它的每个元素:arguments[0]是第一个元素,arguments[1]是第二个元素。。。。。
- 使用length属性确定传入的参数个数
- arguments对象中的值会自动反映到对应的命名参数,但这种影响是单向的:修改命名参数不会改变arguments中对应的值
- arguments对象与命名参数的内存空间是独立的
- arguments对象可以与形式参数(命名参数)同时使用
- arguments对象的长度由传入的参数个数决定,不是由定义函数时命名参数(形式参数)的个数决定
- 没有传递值的命名参数(形式参数)将自动被赋予undefined值,就跟定义了变量但又没有初始化一样
- 严格模式对arguments对象的限制:
- 通过arguments对象对命名参数赋值无效
- 重写arguments的值会导致语法错误
- ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数
-
ECMAScript函数没有重载
- 因为没有函数签名(接受参数的类型和数量),真正的重载是不存在的
- 如果定义了两个名字相同的函数,该名字只属于后定义的函数
- 通过检查传入的参数类型和数量并作出不同的反应,可以模仿方法的重载
- JavaScript不允许直接访问内存空间,操作对象时,实际是在操作对象的引用
- 只能给引用类型动态地添加属性
- 关于复制变量值
- 基本类型
- 引用类型
- ECMAScript中所有函数的参数都是按值传递的,即:把函数外部的值复制给函数内部的参数,基本类型的值的传递如同基本类型的变量的复制一样,引用类型的值的传递如同引用类型的变量的复制一样
- 检测对象的类型--instanceof运算符
- 如果变量是给定引用类型的实例,instanceof 运算符会返回true
1 person instanceof Array; //变量person是Array吗
- 如果变量是给定引用类型的实例,instanceof 运算符会返回true
-
执行环境(execution context),也称环境
- 定义了变量或函数有权访问的其他数据,决定了它们各自的行为
- 每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中
- 根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样,全局执行环境是最外围的一个执行环境,在web浏览器中,全局执行环境是 window 对象,所有全局变量和函数都是作为 window 对象的属性和方法创建的
- 某个执行环境中的所有代码执行完毕后,执行环境被销毁,保存在其中的所有变量和函数定义也随之销毁
- 全局执行环境直到应用程序退出时(如关闭网页或浏览器)才会被销毁
- 定义了变量或函数有权访问的其他数据,决定了它们各自的行为
-
作用域链
- 概念
- 实例解析
- 延长作用域链
- 没有块级作用域
- 垃圾收集
- 性能问题
- 管理内存
- 尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构
- 引用类型有时也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法
- 注意:虽然引用类型和类看起来相似,但它们是不同的概念
- 对象是某个特定引用类型的实例,通过new 操作符后跟构造函数来创建:
1 var person = new Object();
- 构造函数只为新对象定义了默认的属性和方法
-
Object类型
- 创建Object实例
- 方式一:使用new操作符后跟Object构造函数
1 var person = new Object(); 2 person.name = "Nicholas"; 3 person.age = 29;
- 方式二:对象字面量表示法(简化了创建包含大量属性的对象的过程)
1 var person = { 2 name = "Nicholas", 3 age = 29 4 };
- 如果留空花括号,可以定义只包含默认属性和方法的对象(与方式一相同)
- 对于函数的参数传递方式,最好的做法是对那些必需值使用命令参数,而使用对象字面量来封装多个可选参数
- 方式一:使用new操作符后跟Object构造函数
- 访问对象属性可以使用点表示法,在JavaScript中也可以使用方括号表示法(属性名以字符串形式放在方括号中)来访问对象的属性
- 在以下情况时,使用方括号表示法访问属性:
- 必须使用变量来访问属性,这时可以把属性名赋值给一个变量再用方括号表示法访问
- 属性名包含会导致语法错误的字符,或是属性名使用的是关键字或保留字
- 创建Object实例
-
Array类型
- ECMAScript中的数组与其他多数语言中的数组有相当大的区别:
- 数组中的每一项可以保存任何类型的数据
- 数组的大小是动态调整的,随着数据的添加自动增长
- 创建数组的方式:
- 使用Array构造函数
1 var colors = new Array();
- 预先知道数组的大小时,可以给构造函数传入该数量
- 也可以给构造函数传入数组中包含的项
- 可以省略new操作符
- 数组字面量表示法
1 var colors = ["red","green","blue"];
- 使用Array构造函数
- 数组的索引从0开始
- 通过length属性获取数组的长度
- 数组长度动态增长,把一个值放在超出当前数组大小的位置上时,数组会重新计算长度
- 数组最多可以包含4294967295个项,超出这个上限会发生异常
- 检测数组
- Array.isArray()确定某个值到底是不是数组,无论它是在哪个全局执行环境中创建的
- 所有对象都具有toLocaleString()、toString()和valueOf()
- 转换方法
- toString()
- valueOf()
- toLocaleString()
- join() 接受一个参数:用作分隔的字符串
- 如果数组中的某一项的值是null或者undefined,那么该值在以上四个方法返回结果中以空字符串显示
- 栈方法——实现类似栈的行为
- push() 接受任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度
- pop() 从数组末尾移出最后一项,减少length值,返回移除的项
- 队列方法——结合使用shift()和pop()
- shift() 移除数组中的第一项并返回该项,同时将数组长度减1
- pop()
- unshift() 在数组前端添加任意个项并返回新数组的长度
- 重排序方法
- reverse() 反转数组项的顺序,返回排序后的数组
-
sort() 按升序排列数组项,比较的始终是字符串,可能得不到我们希望的排序结果,这时可传入一个自定义的比较函数,返回排序后的数组
-
- 比如,可以定义这样的一个比较函数,按照升序返回我们想要的结果
1 function compare(value1,value2){ 2 if(value1<value2){ 3 return -1; 4 } 5 else if(value1>value2){ 6 return 1; 7 } 8 else{ 9 return 0; 10 } 11 } 12 //适用于大多数数据类型,将其作为参数传给sort()方法即可
- 比如,可以定义这样的一个比较函数,按照升序返回我们想要的结果
-
- 操作方法
- concat() 创建当前数组的一个副本,然后将接受到的参数添加到这个副本的末尾,最后返回新构建的数组
- slice() 基于当前数组中的一或多项创建一个新数组
- splice() 给数组的指定位置删除、插入、替换项(根据参数而定),返回一个数组,该数组包含删除的项
- 位置方法
- indexOf() 从数组开头开始向后查找,返回要查找的项在数组中的位置,没找到时返回-1
- lastIndexOf() 从数组末尾开始向前查找,返回要查找的项在数组中的位置,没找到时返回-1
- 迭代方法
- every() 对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true
- filter() 对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组
- forEach() 。。。。。。。。。。。。。。,这个方法没有返回值,本质上与使用for循环迭代数组一样
- map() 。。。。。。。。。。。。。。,返回每次函数调用的结果组成的数组
- some() 。。。。。。。。。。。。。。,如果该函数对任一项返回true,则返回true
- 缩小方法
- reduce() 从数组的第一项开始,运行给定的函数,迭代数组的所有项,逐个遍历到最后
- reduceRight() 。。。最后一项开始,。。。。。。。。。。。。。。。。,向前遍历到第一项
- ECMAScript中的数组与其他多数语言中的数组有相当大的区别:
-
Date类型
- Date类型使用自UTC(Coordinated Universal Time,国际协调时间)1970年1月1日0时开始经过的毫秒数来保存日期,保存的日期能精确到1970年1月1日之前或之后的285616年
- 创建一个日期对象,使用 new 操作符或Date构造函数均可
- 调用构造函数时,不传入参数时新创建的对象自动获得当前日期和时间,如果要根据特定的日期和时间创建日期对象,必须传入该日期的毫秒数,为了简化这一操作,可以使用以下两个方法:
- Date.parse() 日期格式因实现而异
- Date.UTC() 与Date.parse()方法的不同:日期和时间都基于本地时区而非GMT创建
- 如果直接将表示日期的字符串传递给Date构造函数,也会在后台调用Date.parse()
- Date.now() 返回表示调用这个方法时的日期和时间的毫秒数
- PST(Pacific Standard Time,太平洋标准时间)
- 继承的toString()、toLocaleString()、valueOf()方法
- 专门用于将日期格式化为字符串传的方法(因浏览器而异)
- toDateString()
- toTimeString()
- toLocaleDateString()
- toLocaleTimeString()
- toUTCString()
- UTC日期指的是在没有时区偏差的情况下(将日期转换为GMT时间)的日期值
- 日期/时间组件方法
-
RegExp类型
- ECMAScript 通过RegExp类型来支持正则表达式
- 创建正则表达式的语法(字面量形式):
1 var <expression> = /<pattern>/<flags>;
- 模式部分(pattern)可以是任何简单或复杂的正则表达式
- 标志部分(flags)可带有一个或多个标志,用以标明正则表达式的行为,正则表达式的模式匹配支持下列3个标志
- 一个正则表达式就是一个模式与3个标志的组合体,不同的组合产生不同的结果
- 元字符在正则表达式中有一种或多种特殊用途,正则表达式中的元字符包括:
1 ( [ { \ ^ $ | ) ? * + . ] }
- 如果想要匹配字符串中包含的这些元字符,必须对它们进行转义
1 var pattern1 = /[bc]at/i; //匹配第一个"bat"或"cat",不区分大小写 2 var pattern2 = /\[bc\]at/i; //匹配第一个"[bc]at",不区分大小写 3 var pattern3 = /.at/gi; //匹配所有以"at"结尾的3个字符的组合,不区分大小写 4 var pattern4 = /\.at/gi; //匹配所有".at",不区分大小写
- 使用RegExp构造函数创建正则表达式
- RegExp构造函数接受两个参数(都是字符串),分别是表示模式部分和标志部分的字符串
1 var pattern = new RegExp("[bc]at","i"); //匹配第一个"bat"或"cat",不区分大小写
- 由于模式参数是字符串,有时要对字符进行双重转义
- RegExp构造函数接受两个参数(都是字符串),分别是表示模式部分和标志部分的字符串
-
使用正则表达式字面量和使用RegExp构造函数创建的正则表达式不一样:
- 在ES3中,正则表达式字面量始终会共享同一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例
- ES5明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,,每次都创建新的RegExp实例
- RegExp实例属性
- RegExp实例方法()、构造函数属性、模式的局限性
-
- exec()
- test()
- test()方法接受一个字符串参数,在模式与该参数匹配的情况下返回true,否则返回false,用于查看目标字符串与某个模式是否匹配
- 继承的toString()、toLocaleString()会返回正则表达式的字面量,与创建正则表达式的方式无关
- 继承的valueOf()会返回正则表达式本身
-
Function类型
- 每个函数都是Function类型的实例,与其他引用类型一样,都有属性和方法
- 函数名实际上是一个指向函数对象的指针,不会与某个函数绑定
- 定义函数的方式
- 函数声明语法,例如:
1 function sum(num1,num2){ 2 return num1+num2; 3 }
- 函数表达式,例如:
1 var sum = function(num1,num2){ 2 return num1+num2; 3 };
- 使用Function构造函数
1 var sum = new Function("num1","num2","return num1+num2");
- Function构造函数可接受任意个参数,最后一个参数被视为函数体
- 这这种方式不推荐使用,会影响性能,但对于理解“函数是对象,函数名是指针”非常直观
- 函数声明语法,例如:
- 使用不带括号的函数名时,是访问函数指针,而非调用、执行函数
- 由于函数名仅仅是指向函数的指针,因此和包含对象指针的其它变量并没有什么不同,一个函数可以有多个函数名,同时,这也有助于理解ECMAScript中没有函数重载的概念
- 使用函数声明与使用函数表达式定义函数的区别
- 不仅可以像传递参数一样把一个函数传递给另一个函数,还可以将一个函数作为另一个函数的结果返回
- 函数内部属性
- 函数属性和方法
- 每个函数都包含两个属性:
- length 函数希望接受的命令参数的个数
- prototype 保存着所有引用类型的实例方法,是不可枚举的,使用for-in无法发现
- 每个函数都包含两个非继承来的方法:
- apply()
-
call()
- 这两个方法都是在特定的作用域中调用函数,等于设置函数体内this 对象的值
- 在严格模式下,未指定环境对象而调用函数,this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或call(),否则this值将是undefined
- 这两个方法作用相同,区别在于接受参数的方式不同
- apply()接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组,第二个参数可以是Array的实例,也可以是arguments对象
- call()的第一个参数是在其中运行函数的作用域,其余参数是传递给函数的参数,必须逐个罗列出来
- 可使用这两个方法扩充作用域,对象不需要与方法有任何耦合关系
- bind() 创建一个函数的实例,其this值被绑定到传给bind()的值
- 函数继承的toString()、toLocaleString()、valueOf()方法返回函数的代码,返回代码的格式因浏览器而异
- 每个函数都包含两个属性:
-
基本包装类型
- 为了便于操作基本类型的值,ECMAScript提供了3个特殊的引用类型,它们既有与其他引用类型相似的地方,也有与各自基本类型相应的行为:
-
Boolean类型
- 重写了valueOf()方法,返回基本类型值true或false
- 重写了toString()方法,返回字符串"true"或"false"
- 创建Boolean对象:构造函数传入true或false
1 var booleanObject = new Boolean(true);
- 回顾:布尔表达式中的所有对象都会被转换为true
- 理解基本类型的布尔值与引用类型的布尔值的区别:
- typeof返回的值不同
- instanceof返回的值不同
- 建议不要使用Boolean对象
-
Number类型
- 创建Number对象
1 var numberObject = new Number(10);
- 重写了valueOf()方法,返回对象表示的基本类型的数值
- 重写了toString()方法(可传递一个表示基数的参数)和toLocaleString()方法,返回字符串形式的数值
- 将数值格式化为字符串的方法
- toFixed() 按照指定的小数位数返回数值的字符串表示,数值本身包含的小数位数比指定的位数多时,会自动舍入,具体的舍入规则因浏览器而异
- toExponential() 返回以指数表示法表示的数值的字符串形式,接受一个参数,指定输出结果中的小数位数
- toPrecision() 以最合适的格式返回数值的字符串形式,接受一个参数,指定数值的所有数字的位数(不包括指数部分)
- 不建议直接实例化Number类型
- 创建Number对象
-
String类型
- 创建String对象
1 var stringObject = new String("hello world");
- 继承的valueOf()、toString()、toLocaleString()返回String对象所表示的字符串的值
- 每个String类型的实例都有一个length属性,表示字符串中包含多少个字符
- 访问字符串中特定字符的方法(使用从0开始的数字索引):
- charAt()
- charCodeAt()
- 还可用方括号表示法访问特定的字符
- 字符串操作方法
- concat()
- slice()
- substr()
- substring()
- 字符串位置方法
- indexOf()
- lastIndexOf()
- trim() 创建一个字符串的副本,删除前置及后缀的所有空格
- 字符串大小写转换方法
- toLowerCase()
- toLocaleLowerCase()
- toUpperCase()
- toLocaleUpperCase()
- 字符串的模式匹配方法
- match()
- search()
-
replace()
- 接受连个参数:第一个参数可以是一个RegExp对象或一个字符串,第二个参数可以是一个字符串或一个函数
- 可以使用一些特殊的字符序列,将正则表达式操作得到的值插入到结果字符串中
- split()
- localeCompare()
- fromCharCode() 接收一个或多个字符编码,将它们转换成一个字符串
- HTML方法
- 创建String对象
-
Boolean类型
- 每读取一个基本类型的值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据
- 操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象
- 引用类型和基本包装类型的主要区别是对象的生存期
- 对基本包装类型的实例调用typeof会返回object,且所有基本包装类型的对象都会被转换为布尔值true
- Object构造函数会像工厂方法一样,根据传入值的类型返回相应的基本包装类型的实例
- 不建议显式地创建基本包装类型的对象
- 使用new调用基本包装类型的构造函数与直接调用同名的转型函数所得到的变量的类型是不一样的
- 为了便于操作基本类型的值,ECMAScript提供了3个特殊的引用类型,它们既有与其他引用类型相似的地方,也有与各自基本类型相应的行为:
-
单体内置对象
- “由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在”
- 这些对象不必显示地实例化,它们已经实例化了
- 内置对象:
- Object
- Array
- String
-
Global
- 不属于任何其他对象的属性和方法,最终都是Global对象的属性和方法
- 事实上,没有全局变量和函数,所有在全局作用域中定义的属性和函数,都是Global对象的属性,如前面介绍过的isNaN()、parseInt()、parseFloat()等
- URI(Uniform Resource Identifiers,通用资源标识符)
- URI编码方法
- encodeURI()
- encodeURIComponent()
- decodeURI()
- decodeURIComponent()
- eval()方法
- Global对象的属性
- window对象
-
Math
- Math对象的属性
- min()
- max()
- 舍入方法
-
random() 返回一个0到1之间的随机小数
1 //从1到10之间的随机选一个值赋给变量 num 2 var num = Math.floor(Math.random()*10+1);
- 其他一些方法
- OO(Object-Oriented,面向对象)
- “无序属性的集合,其属性可以包含基本值、对象或函数”——ECMAScript中对象的定义
- 可以把对象想象成散列表:无非就是一组名/值对,其中的值可以是数据或函数
- 创建自定义对象就是创建一个Object实例,然后为它添加属性和方法
- 推荐使用对象字面量的形式创建对象
1 var person = { 2 name:"Nicholas", 3 age:29, 4 job:"Software Engineer", 5 sayName:function(){ 6 alert(this.name); 7 } 8 };
-
属性类型
- ECMA-262中定义的内部特性(attribute),描述了属性(property)的各种特征,实现了JavaScript引擎,在JavaScript中不能直接访问它们,内部特性放在两对方括号中,如[[Enumerable]]
- ECMAScript中有两种属性:
-
数据属性
- 包含一个数据值的位置,在这个位置可以读取和写入值,有4个描述其行为的特性:
- [[Configurable]]
- 表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为访问器属性
- 直接在对象上定义的属性,其[[Configurable]]值为true
- 注意:一旦把属性定义为不可配置的,就不能再改回可配置的,而且,此时修改[[Writable]]特性之外的其他特性都会导致错误
- [[Enumerable]]
- 表示能否通过for-in循环返回属性
- 直接在对象上定义的属性,其[[Enumerable]]值为true
- [[Writable]]
- 表示能否修改属性的值
- 直接在对象上定义的属性,其[[Writable]]值为true
- [[Value]]
- 包含着这个属性的数据值
- 读取和写入属性值的时候,从这个位置读/写
- 默认值为undefined
- [[Configurable]]
- 要修改一个属性的默认特性,必须使用Object.defineProperty()
- 接收3个参数:
- 属性所在的对象
- 属性名
- 描述符(descriptor)对象:必须设置上面4个特性中的一个或多个的值
- 举例:
- 接收3个参数:
- 调用Object.defineProperty()时,如果不指定,[[configurable]]、[[enumerable]]、[[writable]]的默认值都为false
- 包含一个数据值的位置,在这个位置可以读取和写入值,有4个描述其行为的特性:
-
访问器属性
- 不包含数据值
- 包含一对getter和setter函数(不是必需的)
- 读取访问器属性时,调用getter函数,其负责返回有效的值
- 写入访问器属性时,调用setter函数并传入新值,其负责决定如何处理数据
- 有如下4个特性:
- [[Configurable]]
- 表示能否通过delete删除属性,能否修改属性的特性,能否把属性修改为数据属性
- 直接在对象上定义的属性,其[[Configurable]]值为true
- [[Enumerable]]
- 表示能否通过for-in循环返回属性
- 直接在对象上定义的属性,其[[Enumerable]]值为true
- [[Get]]
- 读取属性时调用的函数
- 默认值为undefined
- [[Set]]
- 写入属性时调用的函数
- 默认值为undefined
- [[Configurable]]
- 访问器属性不能直接定义,必须使用Object.defineProperty()来定义
- 只指定getter函数,意味着属性不可写,只指定setter函数,意味着属性不可读
- 属性名前面使用双下划线表示该属性只能通过对象方法访问
- 以前使用的两个创建访问器属性的方法:
- __defineGetter__()
- __defineSetter__()
-
数据属性
-
定义多个属性
- 使用Object.defineProperties()方法
-
读取属性的特性
- 使用Object.getOwnPropertyDescriptor()可取得给定属性的描述符,包括数据属性和访问器属性
- 可以针对任何对象,包括DOM和BOM对象使用Object.getOwnPropertyDescriptor()
- 创建对象
- 使用构造函数或对象字面量都可以创建对象,但有个缺点:使用同一个接口创建很多对象会产生大量的重复代码
- 为了解决这个问题,使用工厂模式的一种变体
-
工厂模式
- 抽象了创建具体对象的过程
- 由于在ECMAScript中无法创建类,开发人员发明了一种函数,用来封装以特定接口创建对象的细节
- 工厂模式虽然解决了创建多个相似对象的问题,但没有解决对象识别的问题(怎么识别一个对象的类型)
-
构造函数模式
- 简言之,就是创建自定义的构造函数,从而定义自定义对象类型的属性和方法
- 构造函数名以大写字母开头,非构造函数以小写字母开头
- 对象的constructor属性用于标识对象类型,在这个例子中,两个对象的constructor属性指向Person
- 要检测对象类型,instanceof运算符更可靠(person1和person2是Person的实例,同时也是Object的实例)
- 以构造函数模式定义的构造函数是定义在Global对象中的(在浏览器中是window对象)
- 将构造函数当作函数
- 任何函数,只要通过new运算符来调用,就可以作为构造函数
- 任何函数,如果不通过new运算符来调用,就跟普通函数一样
- 使用构造函数模式创建对象存在的问题
- 不同实例上的同名的方法是不相等的
- 解决方法
- 带来的新问题——对象失去封装性(如何解决这个问题:原型模式)
- 在全局作用域中定义的函数只能被某个对象调用
- 如果对象需要定义很多方法,就要定义很多全局函数
-
原型模式
- 我们创建的每一个函数都有一个prototype(原型)属性,它是一个指针
- prototype 指向一个对象,这个对象包含着可以由特定类型的所有实例共享的属性和方法,既:prototype 就是通过构造函数而创建的那个对象实例的原型对象
- 使用原型对象可以让所有对象实例共享它所包含的属性和方法,这样,就不必在构造函数中定义对象实例的信息,而是将这些信息直接添加到原型对象中
- 理解原型对象
- 原型与 in 操作符
- in 操作符应用在两个地方:
- for-in循环中
- 返回所有能够通过对象访问的、可枚举的(Enumerable)属性,既包括存在于实例中的属性,也包括存在于原型中的属性
- 屏蔽了原型中不可枚举属性的实例属性也会在for-in循环中返回
- 注意:根据规定,所有开发人员定义的属性都是可枚举的
- 单独使用
- 通过对象能够访问给定属性时返回 true ,无论该属性存在于实例中还是原型中
- for-in循环中
- 要取得对象上的所有可枚举属性的实例属性,可以使用ES5的Object.keys()
- 接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
- 如果想得到所有实例属性,无论是否可枚举,可以使用Object.getOwnPropertyNames()
- in 操作符应用在两个地方:
- 更简单原型语法
- 在上面的例子中,为了避免每添加一个属性和方法就要敲一遍 Person.prototype,减少不必要的输入,更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,比如,上面例子中创建原型对象可以这样简写:
- 这样简写后,带来一个问题:由于实际上完全重写了 prototype 对象,因而 constructor 属性变成了新对象的 constructor 属性(指向了 Object 对象),不再指向 Person 函数
- 值得注意的是,此时用 instanceof 运算符来测试还是会得到与简写之前相同的结果,但 constructor 属性则等于 Object 而不等于 Person 了
- 为了避免使 constructor 属性改变,可以在对象字面量上显式地设置 constructor 属性的值,但这样重新设置 constructor 属性的值后,会导致它的 [[Enumerable]] 特性被设置为 true
- 为此,如果使用ECMAScript5兼容的浏览器,可以使用前面介绍过的Object.defineProperty()修改 constructor 属性的特性
- 在上面的例子中,为了避免每添加一个属性和方法就要敲一遍 Person.prototype,减少不必要的输入,更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,比如,上面例子中创建原型对象可以这样简写:
-
原型的动态性
- 在原型中查找值的过程是一个搜索的过程,对原型对象所作的任何修改都能立即从实例上反映出来,即使是先创建实例后修改原型也是如此
- 如果重写了整个原型对象,即把原型对象修改为另外一个对象,就等于切断了构造函数与最初的原型对象之间的联系
- 注意:实例中的指针仅指向原型,而不指向构造函数
-
原生对象的原型
- 原型模式的重要性不仅体现在创建自定义类型方面,所有原生的引用类型都是通过原型模式创建的
- 所有的原生引用类型(如Object、Array、String、......)都在其构造函数的原型上定义了方法
- 通过原生对象的原型,不仅可以取得所有默认方法的引用,还可以定义新方法
- 可以像修改自定义对象的原型一样修改原生对象的原型,随时添加方法
- 不推荐修改原生对象的原型
-
原型模式存在的问题
-
组合使用构造函数模式和原型模式
- 这是创建自定义类型最常见的方式
- 具体来讲,使用构造函数模式定义实例属性,使用原型模式定义方法和共享的属性
- 这样一来,每个实例都会有自己的一份实例属性的副本,同时又共享着对方法的引用,最大限度地节省了内存,而且,这种模式还支持向构造函数传递参数
- 使用这种混合模式重写前面的例子:
-
动态原型模式
- 有其他OO语言经验的人在看到独立的构造函数和原型时,还是会感到困惑,为此,提出了动态原型模式
- 基本思想:把所有信息都封装在构造函数中,仅在必要的情况下初始化原型,保持了同时使用构造函数和原型的优点,具体地,前面的例子使用动态原型模式重写如下:
-
寄生(parasitic)构造函数模式
- 基本思想:创建一个函数,封装创建对象的代码,然后返回新创建的对象
- 除了使用new操作符并把使用的包装函数叫做构造函数外,跟工厂模式相同
- 提示:构造函数在没有返回值的情况下,默认会返回新对象实例
- 提示:在构造函数末尾添加return语句可以重写调用构造函数时返回的值
- 注意:使用这种方式时,返回的对象与构造函数或者与构造函数的原型属性之间没有关系,不能依赖 instanceof 操作符确定对象类型
- 应避免使用这种模式
-
稳妥构造函数模式
-
稳妥对象(durable object)
- 指的是没有公共属性,而且其方法也不引用 this 的对象
- 适合在一些安全的环境中(禁止使用 this 和 new),或者在防止数据被其他应用程序改动时使用
- 稳妥构造函数遵顼与寄生构造函数类似的模式,但也有不同:
- 新创建对象的实例方法不引用 this
- 不使用 new 操作符调用构造函数
- 使用稳妥构造函数模式将前面的例子重写如下:
- 使用稳妥构造函数模式创建的对象与构造函数之间也没有关系,因此不要依赖 instanceof 操作符确定对象类型
-
稳妥对象(durable object)
-
继承
- 许多OO语言都支持两种继承方式:
- 接口继承:只继承方法签名
- 实现继承:继承实际的方法
- 由于ECMAScript中函数没有签名,无法实现接口继承,只支持实现继承
- ECMAScript的实现继承主要是依靠原型链来实现的
-
原型链
- 原型链实现继承的基本思想:利用原型链让一个引用类型继承另一个引用类型的属性和方法
- 原型链的基本概念:让一个原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例,上述关系依然成立,如此层层递进,就构成了实例与原型的链条
- 实现原型链的一种基本模式
-
借用构造函数(constructor stealing)
- 目的:解决原型链在包含引用类型的值时所带来的问题(具体见上)
- 别名:伪造对象、经典继承
- 基本思想:在子类型构造函数内部调用超类型构造函数
-
组合继承(combination inheritance)
- JavaScript中最常用的继承模式
- 原型式继承
- 寄生式继承
-
寄生组合式继承
- 被认为是引用类型最理想的继承范式
- 许多OO语言都支持两种继承方式:
- 前面介绍的几种继承模式的关系
- 第七章 函数表达式
- 定义函数的两种方式:
- 函数声明
1 function functionName(arg0,arg1,arg2,...){ 2 //函数体 3 }
- 函数表达式
1 var functionName = function(arg0,arg1,arg2,...){ 2 //函数体 3 };
- 函数表达式有多种语法形式,这是最常见的一种
- 这种情况下创建的函数,因为function关键字后面没有标识符,故叫做匿名函数(anonymous function),有时也叫拉姆达函数
- 匿名函数的 name 属性是空字符串
- 函数声明
- 在一些浏览器(Firefox、Safari、Chrome、Opera)中,可以通过函数的 name 属性访问到给函数指定的名字
- 函数声明提前(function declaration hoisting)
- 在执行代码之前,会先读取函数声明
- 意味着可以把函数声明放在调用它的语句后面
- 函数表达式和其他表达式一样,在使用前必须先赋值
- 递归
-
闭包(¤)
- 闭包是指有权访问另一个函数作用域中的变量的函数
- 创建闭包的方式:在一个函数内部创建另一个函数
- 使用匿名函数模仿块级作用域(利用了闭包)
- 私有变量
- 第七章小结
- 定义函数的两种方式:
相关文章
- 09-23高级语言程序设计:综合程序设计实验
- 09-23高级综合英语写作(2020秋)Week1-Lesson1 讲解内容翻译笔记
- 09-23Windows程序设计学习笔记(1):一个简单的windows程序
- 09-23【课程·研】高级人工智能 | MOOC习题及课后作业:第1~3章
- 09-23团体程序设计天梯赛-练习集 [ L1-005 考试座位号 (15 分) ]
- 09-23L1-023 输出GPLT (20 分) — 团体程序设计天梯赛
- 09-23团体程序设计天梯赛-练习集L1-016. 查验身份证
- 09-232013年信1204-1-2班小学期<程序设计技能训练>作品
- 09-23L1-014 简单题 (5 分)—团体程序设计天梯赛
- 09-23HIVE高级(16):底层原理(1) Hive SQL底层执行原理