1.词法结构
标志符:必须以
字母、\_或者$开头,后续可以是字母、数字、_或者$
2.类型、值和变量
2.1 Javascript类型:
原始类型:字符串、布尔值、数值、undefined、null、symbol(符号类型)
对象类型:对象、数组、函数
2.2 数值
2.2.1 整数字面量
十六进制:0x或0X开头
八进制:0o或0O开头
二进制:0b或0B开头
2.2.2 浮点字面量
- 十进制数形式,例如:
123.45 - 科学计数法形式,例如:
1.23e2或1.23e-2
2.2.3 Javascript中的算术
主要包括Math 函数
Math.开头的函数
如Math.ceil(向上舍入)、Math.floor(向下舍入)、Math.pow(2,6)(求2的6次方)、Math.round(四舍五入)、Math.max(求最大值)、Math.min(求最小值)
Javascript中的一些运算准则:
上溢出、下溢出和被零除不会报错
正上溢出(Infinity)、负上溢出(-Infinity)、正下溢出(比最小可表示数值更接近0,则为0)、负下溢出(-0)
正零和负零相等,即使在严格模式下:
被0除返回无穷或者负无穷
0除以 0结果为NaN
NaN与任何数值和非数值都不相等(两个NaN不相等)
所以不能用x===NaN来判断x的值是非数值,只能用(x!=x和Number.isNaN(x)来判断)
2.2.4 BigInt表示任意精度整数
表示方法:一串数字后面+小写字母n
BigInt()函数用于把数值或者字符串转换为BigInt值
BigInt(string)结果10n**100n
0==0n true
0===0n false
Math对象的人和函数不接受BigInt操作数
2.3 文本
2.3.1 字符串字面量
反斜杠的特殊用法
'two\nlines' 写在一行但表示两行
‘one\
long\
line’ 写在多行但只有一行
2.3.2 字符串API
S.length
S.substring(1,4) 第二到四个字符
S.slice(1,4) s.slice(-3) 最后三个字符
S.indexOf(“a”) 搜索第一个a 字符出现的位置,没找到返回-1
S.lastIndexOf(“a”) 搜索最后一个a出现的位置
ES6之后新增
s.startWith(“hello”) s.endWith(“hello”) 字符串是不是以这些字符开头或结尾的
S.includes(“hello”) s是不是包含该子串
S.replace(“a”,”b”) 用b替换a
S.toLowerCase()、S.toUpperCase() 全部转换为大写和小写字母
S.charAt(0) S[0] 获取字符串中指定位置的字符
“x”.padStart(3,””)、”x”.padEnd(3,””) 在左侧或者右侧填充字符使其长度变为3,如第二个参数省略,则用空格填充
“test”.trim()、”test”.trimStart()、”test”.trimEnd() 删除左右侧、左侧、右侧的空格
s.concat(“!”) s+”!” 连接两个字符串
“2”.repeat(5) 重复5次
JS中的字符串不可修改,以上操作都返回一个新字符串
2.4 布尔值
假性值:false、undefined、null、’’、NaN、0、-0 在需要布尔值的地方返回false,其他返回true
但是undefined == false返回 false
2.5 null和undefined
typeof(null) 返回object
null==undefined null!==undefined
2.6 全局对象
全局函数:isNaN()、parseInt()、eval()(?)
全局常量:undefined、Infinity、NaN
构造函数:Date()、RegExp()、String()、Object()、Array()
全局对象:Math、json
Node全局对象:global
浏览器:window
ES2020:Node和浏览器:globalThis
2.7 是否可修改
JS中undefined、null、布尔值、数值、字符串都不可修改
原字符串不可修改 ,对字符串操作之后会返回一个新字符串
对象和数组可以修改,而且是引用 把对象赋值给一个变量,是在赋值引用,不会创建对象的副本,复制数组可以使用Array.from创建副本而不是引用
2.8 类型转换
| 值 | 转为字符串 | 转为数值 | 转为布尔值 |
|---|---|---|---|
| Undefined | “undefined” | NaN | false |
| Null | “null” | 0 | false |
| true | “true” | 1 | |
| false | “false” | 0 | |
| “”(空字符串) | 0 | false | |
| “1.2”(非空字符串,数值) | 1.2 | true | |
| “one”(非空字符串,非数值) | NaN | true | |
| 0 | “0” | false | |
| -0 | “0” | false | |
| 1(有限,非零) | “1” | true | |
| Infinity | “Infinity” | true | |
| -Infinity | “-Infinity” | true | |
| NaN | “NaN” | false | |
| {}对象 | true | ||
| [](空数组) | 0 | true | |
| [9](一个数值元素) | “9” | true | |
| [‘a’]任何其他数组 | Join方法 | NaN | true |
| Function(){}(任何函数) | NaN | true |
2.8.1 转换与相等
Undefined==null true
Undefined==false false
0==false true(布尔值转换为数值0)
“0”==false true(两个操作数都转换为0)
==操作符不会将其他操作数转换为布尔值
2.8.2 显示转换
显示转换为数值、字符串或者布尔值的三个函数Number()、String()、Boolean()
常用的隐式转换:
X+”” 转为字符串
+x 转为数值
X-0 转为数值
!!x 转为布尔值
数值向字符串的特殊转换,四舍五入
let n = 123456.789;
n.toFixed(m) m指定的小数位数
n.toExponential(m) 转为科学计数法,小数点前1位,小数点后m位
n.toPrecision(m) 指定有效位数个数m
parseInt()、parseFloat()将字符串转为整数或者浮点数会忽略开头的空格和后面的无关字符,如果第一个字符是非空格非数值,则返回NaN
parse可以解析0x或0X开头的字符串
Number只能处理基数为10的,且不能处理后面为无关字符的
parseInt可以接受第二个参数,指定要解析的基数
如:parse(“077”,10) 77
parse(“077”,8) 63
2.9 对象到原始值转换
对象到布尔值的转换:所有对象都转为true
对象转为字符串:
2.10 变量声明与赋值
2.10.1 var、const和let
通过var实现的全局变量被实现为全局对象的属性,可以通过globalThis来引用(如 var x =10;等价于globalThis.x =10);通过const和let声明的变量不是全局对象的属性。
2.10.2 解构赋值
3.表达式与操作符
3.1 属性访问表达式
ES2020新增属性访问表达式
Expression?.identifier
Expression?.[expression]
?左侧为undefined或者null时会返回Undefined,不会继续往下求值
a?.b 如果a不存在或a.b不存在返回undefined
可选链式求值,如果遇到?左侧为undefined或者null立即返回不会抛出错误
Let a = {b:null};
a.b?.c.d undefined
(a.b?.c).d TypeError
Let a = {b:{}}中a.b不是null也不是undefined所以继续求值,a.b?.c?.d返回undefined
3.2 调用表达式
使用?.()来调用函数,如果?左侧是undefined或null则返回undefined,但是不能检查?左侧是不是函数定义
比如调用log函数,log?.(x)
3.3 对象创建表达式
如:new Object()或者new Object
不传参括号可以省略
3.4 算术表达式
**幂运算具有右结合性
223相当于 2**8
JS中所有数值都是浮点数,所以5/2=2.5不是2
%操作结果的符号与第一个操作数符号相同,-5%2=-1
%操作可以用于浮点数,6.5%2.1=0.2
3.4.1 +操作符
只要有一个操作数是字符串或者可以转为字符串的对象,都执行字符串拼接
以下为字符串拼接:
“1”+2 “12”
1+{} 1[object object]
以下转为数值后再进行运算:
True+true 2
1+null 1
2+undefined NaN
3.4.2 一元操作符
++自增操作符始终将操作数先转为数值后再进行自增运算
3.4.3 位操作符
& | ~ ^
<<左移(新的第一位(右)用0填充)
有符号右移(左边的位用符号位填充)
零填充右移(左边的位用0填充)
3.5 关系表达式
3.5.1 不相等和不严格相等
!=和!==分别是==和===的对立面
对象和数组的==和===比较的是地址值
字符串与数值的==,把字符串转为数值后在进行比较
对象与数值或字符串的==,将对象转为原始值再比较 怎么转为原始值?
首先尝试用valeof(),再尝试toString();Date是个例外,执行toString()
3.5.2 比较操作符
+与<的区别
+偏向字符串,只要有一个操作符是字符串就执行拼接操作;<偏向数值,只要有一个操作符是数值就执行比较操作
“11”<3 false “11”转为数值
3.5.3 in
in操作符左侧操作数是字符串、符号或者可以转换为字符串的值,右侧操作数是对象,如果左侧是右侧的属性名则返回true。
let data = [7,8,9]; 数组包括索引
“0” in data true
1 in data true 数值1转为字符串
3.5.4 instanceof操作符
Instanceof:需要理解原型链
左侧操作数是对象,右侧操作数是对象的类
let d = new Date();
d instanceof Date; true
d instanceof Object true
左侧操作数不是对象返回false;右侧操作数不是对象的类,返回TypeError。
3.6 逻辑表达式
(a===b)&&stop()
3.7 求值表达式
eval
3.8 其他操作符
3.8.1 先定义??
??的作用类似||,||左侧为false才对右侧求值,??在左侧求值为null或者undefined时才求值右侧。
如果混用??和||或者&&中的某一个,必须加圆括号指明先执行什么,否则会报错。
3.8.2 typeof操作符
typeof操作符的返回值
| x | Typeof x |
|---|---|
| undefined | “undefined” |
| null | “object” |
| 任意值或NaN | “number” |
| 任意函数 | “function” |
| 任意字符串 | “string” |
| 任意符号 | “symbol” |
| 任意非函数对象 | “object” |
| true或者false | “boolean” |
| 任意BigInt | “bigint” |
3.8.3 delete操作符
delete是一元操作符,删除操作数指定的对象属性或数组元素
不可配置属性无法删除
let a = [1,2,3];
delete a[2];
a.length 3
删除数组某个值,该值不存在了但是数组长度没有变化
4. 语句
4.1 for/of
for of用于可迭代对象,可迭代对象一般包括数组、字符串、集合和映射
对象:
对象默认不可迭代,如果想迭代对象的属性可以使用for/in或者for of Object.keys(o);迭代对象的值for of Object.values(o);如果想迭代属性和值对,使用for [key,value] of Object.entries(o)(返回一个数组的数组,[key,value]用于解构。for/of不会迭代继承的属性。
set 和map:
for of可以迭代set和map,迭代map时,
let m = new map([[1,”one”]]);
for(let [key,value] of m){
}迭代出key和value
4.2 for/in
for/in可以用于任意对象迭代。For/in不迭代枚举名字为符号的属性,对于名字为字符串的属性,只迭代可遍历的属性;继承的属性也可以被迭代。
5. 对象
对象有名字和值还有writable(可写)、enumerable(可枚举)、configurable(可配置)属性。
可写:是否可以设置属性的值;
可枚举:是否可以用for/in循环返回属性名;
可配置:是否可以删除属性以及是否可以修改其特性。
属性名可以是任意字符串包括空字符串。
5.1 创建对象
5.1.1 对象字面量
属性名包含空格和连字符要使用字符串字面量。
let o = {x:1,y:2};
5.1.2 使用new创建对象
new+构造函数()
5.1.3 Object.create创建对象
Object.create 的使用:
(1)let o = Object.create({x:1;y:2});
(2)let o = Object.create(Object.prototype);
创建一个空对象
(3)let o = Object.create(null);
创建没有原型的对象,通过这种方式创建的新对象不会继承任何东西,包括toString方法等
5.2 查询和设置属性
属性名需要使用[]操作符不能使用.操作符的情况:
不能写死的属性要用[]而不能用.
比如:动态设置属性名
function(portfolio,stockname,shares){
portfolio[stockname] = shares;
}
5.2.1 属性访问错误
查询已存在对象的不存在的属性是undefined不是错误;但是查询不存在对象的属性是错误TypeError。在null或者undefined上设置属性也会导致TypeError。
防止出现错误的访问方式:
surname = book&&book.author&&book.author.surname; 返回null或者undefined或者surname。
ES2020新语法:
Surname = book?.author?.surname;
5.3 删除属性
采用delete删除属性,只能删除自有属性不能删除继承属性。Delete不可删除不可配置属性。
在严格模式下删除全局属性必须使用delete globalThis.x,非严格模式下可以使用delete x。
Delete使用起来需要小心,操作成功或者没有影响都返回true(删除已删除的属性、删除toString等、删除不存在的属性也返回true。),删除失败才返回false。
5.4 测试属性
测试对象是否含有某个属性。可以使用****in 、hasOwnProperty() 、propertyIsEnumerable() 或者直接查询相应属性,均可用于字符串或者符号作为属性名。
in: 可以测试自有属性和继承属性。 “x” in o
hasOwnProperty :测试自有属性不包含继承属性。O.hasOwnProperty(‘x’)
propertyIsEnumerable: 测试自有且可枚举属性。(常规创建的属性都是可枚举的,除非设置为不可枚举)
in操作符可以区分存在但设为Undefined的属性
let o = {x:1};
“x” in o true
delete o.x
“x” in o false
5.5 枚举属性
for in 循环可以遍历枚举属性和继承属性
获取属性名数组的方法:
Object.keys():只返回可枚举属性,不包含不可枚举、继承、符号属性;
Object.getOwnPropertyNames:自有字符串属性,可枚举和不可枚举;
Object.getOwnPropertySymbols:符号自有属性,可枚举和不可枚举;
Reflect.ownKeys:返回所有属性名,可枚举、不可枚举,字符串、符号属性皆可。
5.6 扩展对象
Object.assign()接收多个参数,第一个参数是目标对象,会被修改;后面的是来源对象,将来源对象的可枚举自有属性复制到目标对象。
第一个来源对象会覆盖目标对象的同名属性,第二个来源对象会覆盖第一个来源对象的同名属性。
5.7 序列化和恢复对象
JSON.stringify()序列化对象
JSON.parse()恢复对象
5.8 对象方法
对象Object.property上的方法,以下方法在具体的类中都需要重写。
5.8.1 toString()
在对象上使用toString()得到[object object]。
5.8.2 toLocalString
object的toLocalString没有实现本地化,只是简单的调用toString然后返回该值。
5.8.3 valueOf方法
用于把对象转换为数值。
5.8.4 一些例子
一、JS Array
例子:
| 1234 | Var array = new Array("niu","li","na");console.log(array.valueOf());console.log(array.toString());console.log(array.toLocaleString()); |
|---|
结果:
valueOf:返回数组本身
toString():把数组转换为字符串,并返回结果,每一项以逗号分割。
toLocalString():把数组转换为本地数组,并返回结果。
二、JS Boolean
例子:
| 123 | var boolean = new Boolean();console.log(boolean.valueOf());console.log(boolean.toString()); |
|---|
结果:
valueOf:返回 Boolean 对象的原始值。
toString():根据原始布尔值或者 booleanObject 对象的值返回字符串 "true" 或 "false"。默认为"false"。
toLocalString() :Boolean 对象没有toLocalString() 方法。但是在Boolean 对象上使用这个方法也不会报错。
三、JS Date
例子:
| 1234 | var date = new Date();**console.log(date.valueOf());**console.log(date.toString());console.log(date.toLocaleString()); |
|---|
结果:
valueOf:返回 Date 对象的原始值,以毫秒表示。
toString():把 Date 对象转换为字符串,并返回结果。使用本地时间表示。
toLocalString():可根据本地时间把 Date 对象转换为字符串,并返回结果,返回的字符串根据本地规则格式化。
四、JS Math
例子:
| 1 | console.log(Math.PI.valueOf()); |
|---|
结果:
valueOf:返回 Math 对象的原始值。
五、JS Number
例子:
| 1234 | var num = new Number(1337);console.log(num.valueOf());console.log(num.toString());console.log(num.toLocaleString()); |
|---|
结果:
valueOf:返回一个 Number 对象的基本数字值。
toString():把数字转换为字符串,使用指定的基数。
toLocalString():把数字转换为字符串,使用本地数字格式顺序。
六、JS String
例子:
| 123 | var string = new String("abc");console.log(string.valueOf());console.log(string.toString()); |
|---|
结果:
valueOf:返回某个字符串对象的原始值。
toString():返回字符串。
七、toString()方法与toLocalString()方法区别
toLocalString()是调用每个数组元素的 toLocaleString() 方法,然后使用地区特定的分隔符把生成的字符串连接起来,形成一个字符串。
toString()方法获取的是String(传统字符串),而toLocaleString()方法获取的是LocaleString(本地环境字符串)。
如果你开发的脚本在世界范围都有人使用,那么将对象转换成字符串时请使用toString()方法来完成。
LocaleString() 会根据你机器的本地环境来返回字符串,它和toString() 返回的值在不同的本地环境下使用的符号会有微妙的变化。
所以使用toString()是保险的,返回唯一值的方法,它不会因为本地环境的改变而发生变化。如果是为了返回时间类型的数据,推荐使用LocaleString()。若是在后台处理字符串,请务必使用****toString() 。
5.9 对象字面量扩展语法
5.9.1扩展属性
...用于把a和b的属性展开放入c中。
let a = {x:1;z:2};
let b = {y:2};
let c = {...a,...b};
扩展操作符只扩展自有属性,不扩展继承属性。
5.9.2 简写方法
ES6之前:
let square = {
area:function(){
this.side*this.side;
}
}
ES6之后,function可以省略。
let square = {
area(){
this.side*this.side;
}
}
以上简写语法写方法时,属性名可以是对象字面量允许的任何形式。
5.9.3 属性的get和set
6. 数组
JS数组中的不同元素可以是不同的类型;也可以是对象或者数组;数组可以是稀疏的,即不一定具有连续的索引。
数组从Array.prototype继承属性。
ES6新增定型数组。
6.1 创建数组
6.1.1 数组字面量
let count = [1,,3]; 索引1没有数值,按索引访问时返回undefined
let undefs = [,,]; 该数组没有元素,长度为2,数组字面量语法允许末尾出现逗号
6.1.2 扩展操作符
let a = [1,2,3];
let b = [0,...a,4]; b的值为0,1,2,3,4
数组去重的方法:将数组转为set去重后再转回来
let letters = [...”hello world”];
[...new Set(letters)] 结果为[‘h’,’e’,’l’,’o’,’’,’w’,’r’,’d’]
6.1.3 Array()构造函数
let a = Array(10); 创建一个长度为10的数组,数组没有存储任何值,数组索引“0”、“1”等没有意义。
传入Array()构造函数的参数个数大于1时,参数代表数组元素。
所以使用Array()构造函数无法创建一个只含有一个特定值的数组。
6.1.4 Array.of()函数
使用Array.of()可以创建一个含有特定值的元素。
Array.of(10) 创建一个只含有元素10的数组
Array.of(1,2,3) 创建一个含有元素1,2,3的数组
6.1.5 Array.from()函数
Array.from()用于创建数组的副本。
Array.from()函数接受一个或者两个参数,如果有第二个参数,第二个参数是一个函数,第一个参数数组中的每一个值都会经过该函数的处理再返回。
6.2 可读写数组元素
数组是对象,索引可以使用字符串等,但是只有所以为数值0-2^32-2之间才会为数组添加长度length属性。
查询对象中不存在的属性会返回undefined。
6.3 稀疏数组
稀疏数组的判定:length值大于元素个数
方法一: let a = Array(5) 创建了一个长度为5但是没有元素的数组
方法二:
方法三:
let a = [,];
方法四:
let b = [undefined,undefined];
6.4 数组长度
如果将数组长度强行设置为小于数组元素个数的值,数组中length之后的值会被删除
6.5 添加或删除数组元素
尾部添加:a.push
首部添加:a.unshift
删除:delete a[2] 删除元素相当于把数组该索引处值设为undefined,不会改变数组长度,会使数组变为稀疏数组
6.6 数组迭代
for/of:不能感知稀疏数组 如果想得到数组索引,使用for(let[index,item] of a.entries())
forEach:可以感知稀疏数组
6.7 多维数组
访问元素a[x][y]
6.8 数组方法
6.8.1数组迭代器方法
这类方法对于稀疏数组,不会对不存在的数组元素调用传入的这个参数。
一、forEach
参数是一个函数,可以接收三个参数,第一个参数是元素值,第二个参数是元素索引,第三个参数是数组对象,第二、三个参数可以省略。forEach没有提前终止的方式,类似break这种。
二、Map
Map()方法用于对每个元素进行处理并将处理结果返回给该元素。Map()返回一个新数组,并不修改调用它的数组。forEach 可以修改调用它的数组。
三、Filter
可以接受三个参数
过滤稀疏数组,只保留有值的元素
A.filter(()=>true)
清空空隙删除Undefined和null
A. filter(x=>x!=null&&x!=undefined)
四、every和some
均返回true或者false
a.every(x=>x>0) 在第一次返回false时就停止返回false
a.some(x=>x>0) 在第一次返回true时就停止返回true
对于空数组,every返回true;some返回false
五、find和findIndex
均接收一个函数
find返回第一个匹配的元素,没找到返回undefined;findIndex返回第一个匹配的元素的索引,没找到返回-1
六、reduce和reduceRight
reduce:归并,接收两个参数,第二个参数表示传给归并函数的初始值。
let a = [1,2,3,4,5];
a.reduce((x,y)=>x+y,0) //15
a.reduce((x,y)=>x*y,1) //120
a.reduce((x,y)=>x>y?x,y) //5
reduceRight从高索引开始归并
6.8.2 打平数组
flat():打平数组
不传参调用,打平一级嵌套
let a = [1,[2,3],[[4]],[[[5]]]];
a.flat(4) [1,2,3,4,5]
flatMap() 与map 方法相似,只不过返回的结果会被自动打平
let phrases = [“hello world”,”the definitive guide”];
let words = phrases.flatMap(phrase=>phrase.split(“”));
words [“hello”,”world”,”the”,”definitive”,”guide”]
flatMap 允许把输入元素映射为空数组
[-1,-2,1,4].flatMap(x=>x<0?[]:Math.sqrt(x)) [1,2]
6.8.3 使用concat()
会返回一个新数组
concat接收多个参数,如果参数中含有数组,会将数组打平,但是不会嵌套打平
let a = [1,2];
a.concat(3,[4,[5,6]]) //[1,2,3,4,[5,6]]
6.8.4 push、pop、shift和unshift
这四个方法会修改原数组
在数组末尾的操作: push、pop
push返回新数组长度;pop返回删除数组的值
在数组开头的操作: shift和unshift
unshift返回新数组长度;shift返回删除元素的值
使用unshift时需要注意:
let a = [1];
a.unshift(2); //a:[2,1]
a.unshift(3); //a:[3,2,1]
对比:
let a = [1];
a.unshift(2,3); //a:[2,3,1]
6.8.5 slice()、splice()、fill()和copyWithin()
一、slice()
接收两个参数,代表切片的起点和终点,如果任何一个参数是负值则表示相对于数组长度的指定数组元素,比如-1代表最后一个元素,-2代表倒数第二个元素。
二、splice
会修改数组,可以对数组进行插入和删除。如果只有两个参数,表示对数组进行删除,并将返回值创给数组。
splice的第一个参数指定插入或删除操作的起点位置,第二个参数指定从数组中删除的元素个数。第三个参数及之后的参数表示要插入数组中的元素。如果插入的是数组,splice不会打平插入的数组,concat会打平一层嵌套数组。
let a = [1,2,3,4,5];
a.splice(2,2) //从位置2开始删除,删除长度为2,结果为a ==[1,2,5]
let a = [1,2,3,4,5];
a.splice(2,2,[6,7],3) //a==[1,2,[6,7],3]
三、fill
可以接收三个参数,第一个参数表示要填充的值,第二个可选参数表示填充的起始索引,第三个可选参数表示填充的终止索引。
不能如下:
可以如下:
四、copyWithin
把数组切片后复制到数组中指定位置,会就地修改数组,但不会改变数组长度。可以接收一个、两个或三个参数。第一个参数表示要复制到的位置,第二个参数表示复制的起始索引,第三个参数表示要复制的终止索引。
如下:要复制的元素过长,会进行截取,数组长度不会改变。
6.8.6 数组索引与排序方法
一、indexOf()和lastIndexOf()
indexOf()和lastIndexOf()从数组中搜索指定的值并返回第一个找到的元素的索引;
indexOf从低索引开始找,lastIndexOf从高索引开始找;
均接收第二个可选参数,表示开始寻找的起始索引;如果没有找到返回-1。
用法indexOf(1,2),寻找1,从索引2开始找
indexOf和findIndex都可以从数组查找元素,indexOf接受一个具体值,findIndex接受一个函数。
二、includes()
includes()参数接受一个参数,如果数组包含该值返回true,否则返回false。
let a = [1,NaN];
a.includes(NaN); //true
includesOf()可以检测NaN,indexOf()无法检测NaN
三、sort()
sort()在不传参时按照字母顺序排序。接收参数是函数。
所以在对数值排序时,需要传参
四、reverse()
反转数组顺序,就地修改数组。
6.8.7 数组到字符串的转换
join()或者toString()
toString()与不传参的join()实现效果一样。
6.8.8 静态数组函数
用于确定一个未知值是不是数组。
Array.isArray([]) //true
Array.isArray({}) //false
7.函数
7.1 函数定义
7.1.1 函数声明
函数声明可以出现在块语句中,不过块语句中的函数只能在该块中有定义,对块的外部不可见。
7.1.2 函数表达式
使用函数声明形式,在作用域内,可以在函数定义之前调用函数,因为存在变量提升;使用函数表达式形式,在定义函数之前不能调用函数,在定义之前函数是undefined。
7.1.3 箭头函数
箭头函数如果想要返回对象需要写成如下形式:
const functionX = x=>{{value:x}}; // 必须使用两个括号
7.2 函数调用
7.2.1 函数的this
非严格模式下,函数的this指window;严格模式下,this是undefined。
7.2.2 作为方法
将函数添加为某个对象的方法,o.m = f;作为方法的函数的this指向该对象而不是window。
在函数中可以嵌套定义函数,在方法中嵌套定义的函数的this 是window 而不是该对象。
let o = {
m:function(){
let self = this;
this === o ; //true
f();
function f(){
this === o; //false
self === o; //true;
}
}
}
在ES6 中嵌套函数写成箭头函数也可以解决这个问题。函数f 重写为
const f = ()=>{
this === o; //true
}
或者:
const f = (function(){
this === o;
}).bind(this);
7.2.3 构造函数调用
构造函数如果没有参数可以省略圆括号;
o = new Object(); 等价于 o = new object;
构造函数正常情况下不会使用return关键字;而是初始化新对象并在到达函数体末尾时隐式返回这个对象。
如果显示的使用return返回某个对象,该对象就会成为调用表达式的值;如果使用了return但没有返回值或者返回的是一个原始值,这个返回值就会被忽略,仍以先创建的对象作为调用表达式的值。
7.2.4 间接调用
JS **函数也是对象,它也有两个方法call() ****和apply() **,可以用来间接调用函数。
这两个方法允许指定调用时this 的值,意味着可以将任意函数作为任意对象的方法来调用,即使这个函数并不是该对象的方法。
7.3 函数实参与形参
7.3.1 可选形参与默认值
如果实参个数小于形参则额外的形参会获得undefined值。
function f(a,b){
b||[] //如果形参b没有值就用[]代替
}
默认值的使用:
function f(a,b=[]){
}
可以使用前面的形参给后面的形参传值:
function f(a,b=2*a){
}
7.3.2 剩余形参与可变长度实参列表
如果实参个数大于形参个数,使用剩余形参。
f(a,b,c,d)
function f (a,b,...c){
}
7.3.3 Arguments对象
arguments是一个类数组(元素类型可以不同),是实参列表数组
例如:
function max(x){
let maxValue = -Infinity;
for(let i = 0;i<arguments.length;i++)
{
if(arguments[i]>maxValue)
maxValue = arguments[i];
}
return maxValue;
}
7.3.4 在函数调用中使用扩展操作符
let numbers = [1,2,3,4,5];
Math.min(...numbers); //1
7.3.5 函数实参解构为形参
function a({x:x1,y:y1,z=0},{x:x2,y:y2})
{
}
a({x:1,y:2},{x:3,y:4})
解构对象和解构数组都可以使用剩余形参
function a({x:x1,y:y1,...rest},{x:x2,y:y2})
{
}
a({x:1,y:2,z:5,o:33},{x:3,y:4})
7.3.6 参数类型
如果实参与期待的参数类型不匹配,JS会进行自动类型转换。
7.4 定义函数属性
函数也是对象,可以为函数设置属性。
counter 是函数uniqueInteger 函数的一个属性
factorial() 函数使用自身的属性来缓存之前的计算结果(函数将自身作为一个数组)
用函数名来保存变量
7.5 函数作为命名空间
在函数内声明的变量在函数外部不可见。可以把函数作为临时的命名空间,这样可以保证在其中定义的变量不会污染全局命名空间。
其中一个应用:立即调用函数表达式IIFE
(function(){}());
另一个应用:闭包
7.6 闭包
P188
例一:
以下利用闭包把counter 放在私有作用域内;
uniqueInteger 在全局作用域内,所以多次调用时counter 会在上次的值上进行累加,作用域没有销毁 ???
let uniqueInteger = (function() { // Define and invoke
let counter = 0; // Private state of function below
return function() { return counter++; };
}());
uniqueInteger() // => 0
uniqueInteger() // => 1
例二:
function counter() {
let n = 0;
return {
count: function() { return n++; },
reset: function() { n = 0; }
};
}
let c = counter(), d = counter(); // Create two counters
c.count() // => 0
d.count() // => 0: they count independently
c.reset(); // reset() and count() methods share state
c.count() // => 0: because we reset c
d.count() // => 1: d was not reset
通过let c = counter(), d = counter();c和d都在全局作用域中,所以每次调用d.count都会在原值基础上自增。c 和d 互不影响。
例三:私有状态传入
没有声明局部变量,只使用自己的参数n 保存供属性访问器共享的私有状态。这样可以让counter() 调用者指定私有变量初始值。
function counter(n) { // Function argument n is the private variable
return {
// Property getter method returns and increments private counter var.
get count() { return n++; },
// Property setter doesn't allow the value of n to decrease
set count(m) {
if (m > n) n = m;
else throw Error("count can only be set to a larger value");
}
};
}
let c = counter(1000);
c.count // => 1000
c.count // => 1001
c.count = 2000;
c.count // => 2000
c.count = 2000; // !Error: count can only be set to a larger value
例四:以下两份代码对比 重要
以下funcs5的结果为5
funcs5的结果为10
7.7 函数属性、方法和构造函数
7.7.1 length属性
length代表形参个数
7.7.2 name属性
表示定义函数时使用的名字
7.7.3 prototype属性
除了箭头函数所有函数都有prototype属性。
数值字面量的分隔符
数值字面量可以使用分隔符分隔为更为清楚的字段:
如:1_000_000,千位分隔符;0x89_ab_cd_ef,字节分隔符;0b0001_1101,半字节分隔符;0.123_456_789,小数点后分隔符