JavaScript权威指南笔记(上)-语言核心
词法结构
字符集
使用Unicode编写
- ES3 Unicode2.1+
- ES5 Unicode3+
区分大小写
注释
// 注释 /* 注释 */
标识符和保留字
必须以字母、下划线、美元符开始,后续字符可以是字母、数字、下划线、美元符,即数字不能作为首字符
保留字
可选的分号
Javascript只有在缺少了分号就无法正确解析代码的时候才会填补分号。
一般,一条语句以(、[、/、+、-开始,它极有可能和前一条语句一起解析。return、break、continue除外
类型、值和变量
数据类型:在编程语言中、能够表示并操作的值的类型
变量:一个值的符号名称,可以通过该名称获得值的引用。
数据类型
一般分类
- 原始类型:数字、字符串、布尔值、null、undefined
- 对象(属性的键值对集合)类型:数组、普通对象、函数类、日期类、正则类、错误类
其他分类
- 可以拥有方法的类型和不能拥有方法的类型
- 可变类型(数字、布尔值、null、undifined、字符串)和不可变类型(对象、数组)
数字
javascript采用IEEE 754标准定义的64位浮点格式表示数字,最大值±1.7976931348623157E+308,最小值±5E-324
能够表示的整数范围为-2E+53~2E+53。实际操作(如数组索引)是基于32位整数。
注意: 小数精度问题,如0.1+0.2 != 0.3 ,需要转成整数计算,计算完成再转回小数。产生原因是Number采用的时IEEE 754 64位双精度浮点数编码,浮点数无法精确表示其值范围内的所有数值,导致十进制转换成二进制时有舍入模式,产生了误差
格式
- 整形直接量
- 浮点直接量
算术运算
上溢出(正负无穷)使用±Infinity表示,下溢出(无限接近于0)则返回0(±0)。
NaN和任何值都不相等,包括自身。
二进制浮点数和四舍五入错误
在javascript使用实数时,常常只是真实值的一个近似表示。
let x=0.3-0.2 let y=0.2-0.1 x==y // false x==0.1 // false y==0.1 // true // 由于舍入误差,0.3和0.2之间的近似差值实际上不等于0.2和0.1之间的近似差值
文本
字符串是一组由16位值组成的不可变的有序序列。字符串长度是其所含的16位值的个数。
转义字符
布尔值
true或者false
可转换为false的值:undefined、null、0、-0、NaN、''。
null和undifined
- typeof null为object,含义为非对象
- undifined 未定义值
全局对象
全局属性、全局函数、构造函数、全局对象
包装对象
存取字符串、数字或布尔值的属性时创建的临时对象叫包装对象。
不可变的原始值和可变的对象引用
可变类型(数字、布尔值、null、undifined、字符串)和不可变类型(对象/引用类型、数组)。
类型转换
转换和相等性
显式类型转换
变量声明
如果给一个未声明的变量赋值(不可配置),实际上会给全局对象创建一个同名属性(可配置),不建议这样用。
创建一个全局变量实际上是给全局对象创建了一个属性。
变量作用域
一个变量的作用域是程序源代码中定义这个变量的区域。
- 函数作用域、块级作用域。
- 声明提前
类型检测
- typeof 用于基础类型和函数判断
- instanceof用于对象类型判断
Object.prototype.toString.apply([])==='[object Array]'
null、undifined失效
表达式和运算符
表达式
表达式分为简单表达式(常量、变量名)和复杂表达式(由简单表达式组成)。
原始表达:
表达式的最小单位,
- 直接量(包括:数字、字符串、布尔,不包括数组、对象)
- 关键字
- 变量
由简单表达式可以组合成复合表达式
复杂表达式
- 对象和数组的初始化表达式
- 函数定义表达式
- 属性访问表达式
- 调用表达式
- 对象创建表达式
运算符
说明:
- 下图按照优先级高到低排序,水平线分割的具有不同的优先级
- A列表示运算符结核性,L(左到右) R(右至左)
- N列表示操作数的个数
- 类型列表示期望的操作数类型以及运算符的结果类型
分类
按照操作数个数分:一元(+1)、二元(1+2)、三元(?:)
左值
表达式只能出现在赋值运算符的左侧。变量、对象属性、数组元素均是左值。
算术表达式
一元算术运算符
作用于一个单独的操作数,并产生一个新值,具有很高的优先级,且均为右结合。
+
:转换为数字或者NaN,并返回转换后的值-
:和+
一样,但是会改变结果的符号。++
和--
:运算符在操作数前,操作数±1并返回计算后的值;运算符在操作数之后,操作数±1,并返回计算前的值。,
逗号运算符,从左到右一次执行,返回最右边的值
关系表达式
- in
- instanceof
逻辑表达式
赋值表达式
其他运算符
- ?:
- typeof
- delete
- viod
- ,逗号运算符,从左到右计算,最后返回最右边的值
语句
声明语句
- 变量var let
- 函数function
条件语句
switch
switch(expression){ statements } // expression中计算是使用===
循环
do/while
do{ statements } while(expression) // 至少执行一次
for/in
// 将对象中的所有属性复制到一个数组中 var o = {x:1,y:2,z:3}; var a = [], i = 0; for(a[i++] in o) /* empty */;
跳转
标签语句
mainloop: while(token I= null) { // 忽略这里的代码... continue mainloop; //跳转到下一次循环 // 忽略这里的代码... } //从标签名开始,以便在报错时退出程序 compute_sum: if (matrix) { for(var x = o; x < matrix.length; x++) { var row= matrix[x]; if (!row) break compute_sum; for(var y = o; y < row.length; y++) { var cell= row[y]; if (isNaN(cell)) break compute_sum; sum+= cell; } } success= true; } // break语句跳转至此II如果在success== false的条件下到达这里, 说明我们给出的矩阵中有错误//否则将矩阵中所有的元素进行求和
其他语句类型
width
// 临时扩展作用域链 with(document.form[0]){ name.value="" }
try/catch
try{ xxxx }catch(e){ xxx }finally{ xxx }
对象
创建对象
- 对象直接量创建的对象原型为Object.prototype
- 通过new创建的对象原型为使用的原构造函数的prototype
- Object.create()创建的对象原型为第一个参数,也可设置为null
属性的查询和设置
属性访问错误
查询属性和原型有关,设置与原型无关(如果设置属性为继承属性,且具有setter方法时,将执行setter,而不是给当前对象创建新的属性)
下列情况给对象O设置属性P会失败
- O中属性P是只读的(defineProperty()方法中有例外)
- O中的P是继承的,且是只读的
- O中不存在属性P,O中没有使用setter方法继承属性P,并且O的可扩展性()是false
检测属性
- in:
x in o
- hasOwnProperty:
O.hasOwnProperty(x)
- propertyIsEnumerable:
o.propertyIsEnumerable(x)
,hasOwnProperty的增强版,自身属性且可枚举 - o.x!==undefined x的值为undefined则需要使用in
属性的特性
- 值 value
- 可写性 writable
- 可枚举性 enumerable
- 可配置性 configurable
获取自身属性的特性
Object.getOwnPropertyDescriptor({x:1},'x') // 返回{value:1,writable:true,enumerable:true,configurable:true}
设置属性的特性
// 单个 Object.definePeoperty(o,'x',{ value:1, // 值 writable:true, // 可读 enumerable:true, // 可遍历 configurable:true // 可改变配置 }) // 批量 Object.definePeoperties(o,{ x:{ value:1, writable:true, enumerable:true, configurable:true }, y:{ value:1, writable:true, enumerable:true, configurable:true } })
对象的三个属性
原型
- 查询原型
Object.getPrototypeOf()
- 检测是否包含某个原型
p.isPrototypeOf(o)
,p是否是o的原型
类属性
可以通过toString获取对象的类属性
function classof(o){ if(o===null) return 'Null'; if(o===undefined) return 'Undefined'; return Object.prototype.toString.call(o).slice(8,-1); } classof({}) // => 'Object'
可扩展性
- 查询可扩展性
Object.isExtensible(o)
- 转换成不可扩展
Object.preventExtensions(o)
- 封闭:转换为不可扩展且所有属性不可配置
Object.seal()
,可使用Object.isSealed()
来检测是否封闭 - 冻结:转换为不可扩展且所有属性不可配置、所有属性只读
Object.freeze()
,可以使用Object.isFrozen()
来检测是否冻结
序列化
JSON.stringify(),JSON.parse()具可接受第二个参数,标识需要序列化或还原的属性列表
对象方法
- toJSON()
- valueOf()将对象转换成原始值
数组
创建数组
- 数组直接量
[]
,该语法有可选的结尾逗号,故[,,]
只有两个元素而非三个 new Array()
new Array() // 创建一个空数组 new Array(10) // 创建一个长度为10的数组 new Array(5,4,3) // 创建一个已包含数组元素数组
稀疏数组
稀疏数组并不是项的值为undefined,而是不存在
// 三种方式创建 // 1 new Array(5) // 2 a=[] a[1000]=0 // 3 delete
数组方法
*标识为变异方法
- join
- reverse *
- sort *
- concat
- slice
- splice *,返回删除元素组成的数组
- push/unshift *,返回数组新的长度
- pop/shift * 返回删除元素的值
- toString/toLocalString 无方括号,逗号分隔
- forEach
- map
- filter,可以使用来压缩稀疏数组
- every 所有元素调用判定函数,均返回true才返回true
- some 所有元素调用判定函数,有一个返回true就返回true
- reduce/reduceRight
// 求和、第二个参数为temp的初始值,不传默认使用数组中的第一个元素 arr.reduce((temp,value,index,arr)=>temp+value, 0)
- indexOf/lastIndexOf
数组类型
判断使用Array.isArray(arr)
判断是否是数组
函数
构造函数:用于初始化一个新创建的对象的函数
函数定义
两种定义方式及区别:
- 函数声明语句,可以在定义前使用(函数声明前置);不能出现在循环、条件、try/catch/finally、with中
- 函数定义表达式,不能在定义前使用(变量声明前置);可以出现在任何地方
- 函数构造器
函数调用
四种调用方式:
- 作为函数:this在非严格模式为全局对象,严格模式为undefined
- 作为方法:this为方法所属对象
- 作为构造函数:this为新构造的对象
- call()或者apply()间接调用:this为指定的对象
函数的实参与形参
不定实参函数:可以接收任意个数的实参,通过arguments(类数组对象)接收参数
作为命名空间的函数
立即调用函数
(function(){ }())
闭包
词法作用域,函数内变量作用域是在函数定义时创建的,而不是在调用时创建,且在函数执行时,定义时的作用域链依然有效。
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存咋子函数作用域内,这种特性叫闭包。
函数属性、方法和构造函数
length 形参个数
arguments 参数对象(类数组对象)
prototype 指向原型对象
apply、bind、call
- apply 将函数作为指定对象thisObj的方法来调用,传递给它的是指定的参数数组args
function.apply(thisObj,args)
- call 将函数作为指定对象thisObj的方法来调用,传递给它的是指定的参数,如果thisObj为null,则为全局对象
function.call(thisObj,arg1,arg2,...)
- bing 返回一个新函数,通过可选的指定参数,作为指定对象obj的方法调用该方法,传递给该函数的参数由两部分组成,一部分是传递给bind()的args数组指定的参数,剩下的是传给这个新函数的所有值。
传入bind()的实参都是放在传入原始函数的实参列表开始的位置。
function.call(obj,arg1,arg2,...) // 示例: let g=f.bind(obj,1,2) g(3) // 等价于 f.call(obj,1,2,3)
toString
Function()构造函数,最后一个实参为函数体
函数式编程
高阶函数
操作函数的函数,接收一个或者多个函数作为参数,并返回一个函数。
不完全函数
传入bind()的实参都是放在传入原始函数的实参列表开始的位置。
作用域
分类
- 全局
- 函数,块级(ES6+)
- eval
作用域链
变量对象
用于存储执行上下文中的:
- 变量
- 函数声明
- 函数参数
1.变量初始化阶段
2.代码执行阶段
类和模块
特性:封装、继承、多态、抽象
类名使用大驼峰命名。ES6直接使用class,下面是ES6之前的。
构造函数和类的标识
原型对象是类的唯一标识:当切仅当两个对象继承自同一个原型对象时,它们才属于同一个类的实例。
construsctor
构造函数是类的公共标识,construsctor属性为对象提供了类。
let 0= new F() 0.construsctor===F // => true
javascript中的java式的类继承
创建一个类分为三步:
- 定义一个构造函数,并设置初始化新对象的实例属性
- 给构造函数的prototype对象定义实例方法
- 给构造函数定义类字段和类属性
类的扩充
javascript中基于原型的继承机制是动态的,如果创建对象之后原型的属性发生变化,也会影响到继承这个原型的所有实例,即我们可以通过给原型对象添加方法来扩充Javascript类。
类和类型
instanceof:
obj instanceof c obj.isPrototypeOf(f)
constructor:
x.constructor===Number
构造函数的名称
Object.prototype.toString.call(o)
注意:
- 前两种方法在多个执行上下文无效
- 这三种方法都有一个问题,就是不是所有对象都有constructor属性
鸭式辩型
Javascript中的面向对象技术
标准转换方法
- toString()
- toLocaleString()
- valueOf()
- toJSON()
方法借用
Object.prototype.xxx=xxx
子类
- 方法链
- 构造函数链
正则表达式的匹配模式
定义
- 直接量
- 构造器
内容
直接量字符
字符类
重复
非贪婪重复在匹配字符后加一个?即可。
选择、分组、引用
指定匹配位置
修饰符
String方法
- search
- replace
- match
- split
RegExp对象
属性:
- source
- global
- ignoreCase
- multiline
- lastIndex
方法:
- exec
- test
javascript的子集和扩展
迭代
迭代器
Iterator(),返回迭代器
for(let [k,v] in Iterator({a:1,b:2})) console.log(k+"="+v) // a=1,b=2
特性:
- 只针对自有属性进行遍历,忽略继承属性
- 第二个参数传true,则只遍历属性名。忽略值
数组推导*
[expression for (varuable in object) if(conditon)]
函数简写
表达式闭包:如果函数只计算一个表达式并返回它的值,关键字return和花括号可以省略
let succ=function(x)x+1
多catch从句
E4X
jsx语法