系统化学习前端之JavaScript(ECMAScript)
前言
JavaScript 由三部分组成:ECMAScript,DOM,BOM。
ECMAScript:JavaScript 核心语法,本篇主要介绍 ECMAScript,即 JavaScript 核心语法。
DOM:文件对象模型,主要作用是通过 JavaScript 与 HTML 和 CSS 交互,可参考 DOM 相关。
BOM:浏览器对象模型,主要作用是通过 JavaScript 与浏览器交互,可参考 BOM 相关。
JavaScript 基础语法
JavaScript 基础语法主要包括数据类型,变量定义,运算符以及流程控制等。
JavaScript 的宿主环境
-
node
服务端 JavaScript,又称为 Node.js。
-
浏览器 JavaScript 引擎
客户端 JavaScript,前端领域的 JavaScript,包含 ECMAScript,DOM,BOM,本篇及系列均为客户端 JavaScript。
JavaScript 的引入方式
-
内联式
<script> // js </script>
-
外链式
<script src="./xxx.js"></script>
JavaScript 注释
-
单行注释
// 单行注释
-
多行注释
/** * 这是一行注释 * 这是另一行注释 */
JavaScript 变量和常量
-
变量
JavaScript 定义变量的关键字有两个:
var
和let
。-
声明变量
var a; // 变量名必须由数字、字母、下划线以及$组成,数字不能开头 let b;
注意:声明变量是必须的,调用未声明的变量会报错
ReferenceError
。 -
初始化变量
a = 1; b = '1';
注意:初始化变量也叫做变量赋值,调用为初始化的变量会返回
undefined
。 -
定义变量
var a = 1; let b = '1';
注意:定义变量是声明变量和初始化变量的合集,定义变量时省略
var
或let
关键字,在非严格模式下,变量会成为全局变量。 -
调用变量
console.log('变量', a, b)
-
-
常量
JavaScript 定义常量的关键字为
const
。-
定义常量
const ISTURE = true; // 常量名尽量使用全大写
注意:声明常量时必须赋值,因此不存在声明常量和初始化常量的行为,常量定义后无法修改,修改会报错
TypeError
。 -
调用常量
console.log('常量', ISTURE)
常量赋值为引用类型时,引用类型数据存于堆空间,引用类型地址(常量名)存于栈空间,堆中数据改变,地址未变,因此,常量可变。
const OBJ = { name: 'Json', age: 18 } console.log('修改前', OBJ) OBJ.name = 'Alen' console.log('修改后', OBJ)
-
JavaScript 数据类型
-
Number
JavaScript 没有严格意义上的整型和浮点型的划分,整型和浮点型都是数字类型。
var a = 1; // 十进制 var b = 017; // 八进制 var c = 0x1F; // 十六进制 var d = 12.34; // 小数形式 var f = 1.2E2; // 指数形式 console.log(a, b, c, d, f) // 1 15 31 12.34 120
注意:根据输出结果,八进制和十六进制均自动转化为十进制输出了。
-
String
字符串类型可以使用 '' 或者 “” 包裹。
var a = '1'; var b = "js";
-
Boolean
布尔类型只有两个值:true 和 false。
var a = true; var b = false;
-
null
“主动”空类型,可以为变量主动赋空,能够释放变量占用的内存空间。
-
undefined
“被动”空类型,声明变量未赋值、调用无返回函数的返回值、访问对象不存在的属性均为 undefined。
-
symbol
symbol 类型的值表示独一无二的值。
var a = Symbol('1') // '1' 为标识字符串,可不传,Symbol() 生成的每个值都是唯一的 console.log(a) // Symbol(1) var b = Symbol('1') console.log(a === b) // false
-
array
数组类型只能使用 [] 包裹,多个数组元素使用 , 隔开。
var a = [1, '2', true] // 数组元素可以为任意数据类型
-
function
JavaScript 中是存在一种数据类型为函数类型的,因为在 JavaScript 中可以通过 函数表达式 来定义函数。
const sum = function (x, y) { return x + y } console.log(sum)
-
object
对象类型只能使用 {} 包裹,其属性以键值对的形式存在。
var a = { name: 'Json', // 属性值可以为任意类型数据,属性名比较特殊,后续详解。 age: 16 }
-
基本类型
JavaScript 基本类型数据有 6 种:Number,String,Boolean,null,undefined,symbol。
基本类型数据传递是按值传递的。
使用
typeof
可以识别基本类型数据。typeof 1 // 'number' typeof '1' // 'string' typeof true // 'boolean' typeof undefined // 'undefined' typeof null // 'object' typeof Symbol() // 'symbol'
注意:
typeof null
返回 object,可以区分typeof undefined
。 -
引用类型
JavaScript 引用类型数据有 3 种:array,function,object。
引用类型数据传递是按引用传递的。
使用
typeof
识别引用类型无法区分 Array 和 Object 类型。typeof [1,2,3] // 'object' typeof function() { console.log('123') } // 'function' typeof { name: 'Json', age: 19 } // 'object'
使用
instanceof
和constructor
识别引用类型数据,该方法需要预测引用类型数据。[1,2,3] instanceof Array // true (function a() { console.log('123') }) instanceof Function // true ({ name: 'Json', age: 19 }) instanceof Object // true [1,2,3].constructor === Array // true (function a() { console.log('123') }).constructor === Function // true ({ name: 'Json', age: 19 }).constructor === Object // true
-
封装识别数据类型的方法
上述的几种识别数据类型的方法都具有局限性,我们可以借助
Object.prototype.toString.call()
封住一个适用所有类型的方法,而且不需要预测。function type(arg) { return Object.prototype.toString.call(arg).slice(8, -1).toLowerCase() }
JavaScript 数据类型转换
JavaScript 基本数据类型可以通过操作符隐式转换,也可以通过转换函数显示转换。引用类型数据转换需要通过其原型方法来进行,后续对象中会讲解。
-
隐式转换
所谓隐式转换是指在运算过程中,操作数数据类型自动发生转换。
1 + 2 // 3 1 + true // 2 1 + false // 1 1 + '2' // 12 '2' - 1 // 1 1 + null // 1 1 + undeined // NaN,NaN 是一种返回值,表示结果不是一个数字。
注意:隐式转换主要发生在算术运算的过程中,Number 类型只有与 String 类型进行
+
运算会进行字符串拼接,与其他类型进行任何运算都将转换 Number 类型进行运算。另:关系运算和逻辑运算也会发生隐式转换,关系运算中操作数均转换为 Number 类型进行对比。逻辑运算中操作转换 Boolean 对比。 -
显式转换
所谓显示转换是指通过转换函数进行数据类型的转换。
-
Number()
Number() 函数接收一个参数,输出转换后的 Number 类型,如参数存在不可转字符,则输出 NaN。
Number('1') // 1 Number('1a') // NaN Number(true) // 1 Number(false) // 0 Number(null) // 0 Number(undefined) // NaN
注意:通常数字的转化可以使用
+参数
的形式转换,算术运算表达式中的隐式转换均通过 Number() 转换的。 -
parseInt()
parseInt() 函数接收一个参数,输出转换后的整型数字,如参数存在不可转字符,可作截取转换,但只能从头开始。
parseInt(2) // 2 parseInt(2.1) // 2 parseInt('1a') // 1 parseInt('1.1a') // 1 parseInt('a1.1') // NaN parseInt(true) // NaN parseInt(false) // NaN parseInt(null) // NaN parseInt(undefined) // NaN
注意:parseInt() 传入第二个参数表示转化进制数,如
parseInt('1010', 2)
返回10
,表示二进制1010
转化 十进制10
。 -
parseFloat()
parseFloat() 函数接收一个参数,输出转换后的 Number ,如参数存在不可转字符,可作截取转换,但只能从头开始。
parseFloat(2) // 2 parseFloat(2.1) // 2.1 parseFloat('1a') // 1 parseFloat('1.1a') // 1.1 parseFloat('a1.1') // NaN parseFloat(true) // NaN parseFloat(false) // NaN parseFloat(null) // NaN parseFloat(undefined) // NaN
-
String()
String() 函数接收一个参数,输出转换后的 Strong 类型,输出结果形式为 '参数'。
String(1) // '1' String(1.1) // '1.1' String(true) // 'true' String(false) // 'false' String(null) // 'null' String(undefined) // 'undefined'
注意:通常字符串转化可以使用
'' + 参数
的形式转化。 -
Boolean()
Boolean() 函数接收一个参数,输出转换后的 Boolean 类型。只有当参数为
0
,''
,undefined
,null
,NaN
时,输出值为false
,其余均为true
。Boolean(0) // false Boolean('') // false Boolean(null) // false Boolean(undefined) // false Boolean(NaN) // false
-
JavaScript 运算符和表达式
表达式由操作数和运算符组成,操作数可以是任意类型数据,运算符主要包含:算术运算符、关系运算符、逻辑运算符等。
-
算术运算符
运算符:
+
,-
,*
,/
,%
,++
,--
。%
模运算,运算结果为取余,如5 % 2
结果为 1。++
,--
自增自减运算,++num
表示自加 1 再取值运算;num++
表示先取值运算再自加 1。var a = 5 var b = 0 b = ++a + 2 console.log(b) // 8 var c = 5 var d = 0 d = c++ + 2 console.log(d) // 7
-
关系运算符
运算符:
>
,<
,>=
,<=
,==
,!=
,===
,!==
。==
不完全相等,只判断操作数通过 Number() 函数转换后 Numer 类型的值是否相等。===
完全相等,同时判断操作数转换后的值和操作数本身的数据类型是否相等。'1' == true // true '1' === true // false undefined == null // true 0 == undefined // false 0 == null //false NaN === NaN // false 0 === -0 // true Infinity === -Infinity // false
-
逻辑运算符
运算符:
&&
,||
,!
。&&
与运算,如a && b
,a 为真,则为 b,a 为假,则为 a。类比电路中的串联电路。||
或运算,如a || b
,a 为真,则为 a,a 为假,则为 b。类比电路中的并联电路 -
位运算符
运算符:
&
,|
,^
。&
按位与,任何数字与1按位与操作,结果为1,即为奇数,反之为偶。|
按位或,任何小数与 0 按位或操作,小数转换为整数(直接取整,无四舍五入)。^
按位异或,整数数字可以使用按位异或操作进行快速交换。var a = 10 var b = 13 a = a ^ b b = b ^ a a = a ^ b console.log(a, b) // 13, 10
-
赋值运算符
运算符:
=
,+=
,-=
,*=
,/=
,%=
。=
简单赋值,运算符右操作数赋值给左操作数。+=
,-=
,*=
,/=
,%=
运算赋值,左右操作数先进行相应的运算,运算结果再赋值给左操作数。 -
三元运算符
格式:
a ? b : c
。若 a 为真,则输出结果为 b,若 a 为假,则输出结果为 c。 -
运算优先级
算术 > 比较 > 逻辑(与 > 或 > 非),
()
解决一切优先级问题。
JavaScript 流程控制
-
顺序结构
JavaScript 默认代码执行顺序由上向下顺序执行。
-
分支结构
JavaScript 分支结构有
if...else if...else
结构和switch...case...default
结构。var score = 89 if(score >=0 && score < 60) { console.log('不及格') } else if(score >= 60 && score < 80) { console.log('及格') } else if(score >= 60 && score < 90) { console.log('良好') } else if(score >= 90 && score <= 100) { console.log('优秀') } else { console.log('谎报军情') } // 良好 var comments = "良好" switch(comments) { case '不及格': console.log('分数在 0 - 60 之间') case '及格': console.log('分数在 60 - 80 之间') case '良好': console.log('分数在 80 - 90 之间') // break case '优秀': console.log('分数在 90 - 100 之间') case '谎报军情': console.log('分数不在 0 - 100 之间') default: console.log('努力,少年。') } /** * 分数在 80 - 90 之间 * 分数在 90 - 100 之间 * 分数不在 0 - 100 之间 * 努力,少年。 */
if...else if...else
结构分支条件局限小,可以使用范围,结果只输出满足条件的分支。switch...case...default
结构分支条件只能为常量值,结果输出不唯一,默认输出满足条件及以下的分支,可以使用break
跳过其他分支。 -
循环结构
JavaScript 主要的循环结构有
while() {...}
结构和for() {...}
结构。// 循环打印 1 - 10 var n = 1; while(n <= 10) { console.log(n) n++ } for(var i = 1; i <= 10; i++) { console.log(i) }
一个循环必须的几个要素:循环初始值:1,循环终止值:10,循环过渡方法:num++,循环体:console操作。
控制循环进度的关键字
break
和continue
,break
表示执行到此,跳出循环;continue
表示执行到此,继续下一步循环。var n = 1; while(n <= 10) { if(n == 5) { n++ continue // 执行到 5,加 1 后不输出了,继续从 6 开始循环 } console.log(n) n++ } for(var i = 1; i <= 10; i++) { console.log(i) if(i == 5) { break // 执行到 5,跳出循环,不再输出 5 - 10 之间的值了 } }
JavaScript 对象
JavaScript 语言中”万物皆对象“。JavaScript 内置了 11 个对象,其中包括 2 个单体对象(Global,Math),2 个辅助对象(RegExp,Date),1 个错误对象(Error),3 个包装对象(Number,String,Boolean),3 个复杂对象(Array,Function,Object)。
Global
Global 为全局作用域对象,在浏览器由 window 代替,Nodejs 中使用 global。浏览器中定义的全局变量均挂载在 window 对象下。ES2020 中引入 globalThis对象作为顶层对象,任何环境都可以取到,也指向全局环境下的 this
关键字。
var a = 'global'
console.log(a) // global
console.log(window.a) // global
注意:定义全局变量实际调用方式应为 window.variable 的形式,window可以省略。
Math
Math 是保存数学计算的常量和提供数学计算常用 API 的对象。
-
常量
-
Math.E
自然常数 e。
-
Math.LN2
自然常数 ln2,以 e 为底 2 的对数。
-
Math.LN10
自然常数 ln10,以 e 为底 10 的对数。
-
Math.LOG2E
自然常数 log2(e),以 2 为底 e 的对数。
-
Math.LOG10E
自然常数 log10(e),以 10 为底 e 的对数。
-
Math.PI
自然常数 π。
-
Math.SQRT1_2
根号下1/2。
-
Math.SQRT2
根号下2。
-
-
数学 API
-
Math.abs()
绝对值函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则取绝对值返回,反之返回 NaN。
-
Math.cbrt()
立方根函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则取立方根返回,反之返回 NaN。
-
Math.sqrt()
平方根函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则取平方根返回,反之返回 NaN。
-
Math.ceil()
天花板函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则向上取整返回,反之返回 NaN。
-
Math.floor()
地板函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则向下取整返回,反之返回 NaN。
-
Math.exp()
幂指函数,函数接收一个参数 n,参数经过 Number() 函数转换后,若结果为 Number 类型则取 e 的 n 次幂返回,反之返回 NaN。
-
Math.pow()
乘方函数,函数接收两个参数 x,y,参数经过 Number() 函数转换后,若结果均为 Number 类型则取 x 的 y 次方 返回,反之返回 NaN。
-
Math.random()
随机函数,随机返回一个 0 - 1 之间的小数,可以取到 0 ,但取不到 1。
-
Math.round()
四舍五入函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则四舍五入取整,反之返回 NaN。
-
Math.trunc()
取整函数,函数接收一个参数,参数经过 Number() 函数转换后,若结果为 Number 类型则去除小数部分,返回整型,反之返回 NaN。
-
Math.hupot()
函数接收多个参数,所有参数经过 Number() 函数转换后,若存在非 Number 类型,直接返回 NaN,若均为 Number 类型,则返回多个参数的平方和的平方根。
-
RegExp
RegExp 是定义正则表达式和提供正则表达式常用 API 的对象。
-
正则语法
-
字符集
一位字符位上的备选字符集合。
[0-9]
表示匹配 0 -9 之间的一位数字。[a-z]
表示匹配 a - z 之间的一位字母。[A-Z]
表示匹配 A - Z 之间的一位字母。{a-zA-Z}
表示匹配 a - z 或 A - Z 之间的一位字母。[0-9A-Za-z]
表示匹配 0 - 9 或者 a - z 或者 A - Z 之间的一位字母或数字。[\u400-\u9fa5]
表示匹配一位汉字。注意:
-
表示范围。[^1]
表示匹配 1 以外的任何一位字符。注意:
^
范围太大,不推荐使用。\d
表示匹配一位数字。\w
表示匹配一位数字、字母或 _ 。\s
表示匹配一位空白字符(空格、TAB)。注意:
\D
,\W
,\S
表示取反,如[\d\D]
表示数字或非数字,即任意一位字符。.
表示匹配任意一位字符。 -
量词
规定一位字符集出现的次数,跟在字符集之后。
{n,m}
表示匹配至少 n 个,至多 m 个。{n,}
表示匹配至少 n 个。{n}
表示匹配只有 n 个。?
表示匹配 0 - 1 个。+
表示匹配 1 - ∞ 个。*
表示匹配 0 - ∞ 个。 -
选择与分组
a|b
表示匹配 a 或 b 。(a|b)
表示匹配 a 或 b 的分组。注意:
-
一个正则表达式中有 n 个
()
,就有 n 个分组。 -
分组引用:
\数字
形式可以作为分组的引用,如/(\d{1,2})(a|b)([A-Z])/
有 3 个分组,\1
表示分组(\d{1,2})
,\3
表示分组([A-Z])
。 -
分组捕获:默认分组引用是分组捕获而来的,
?:
可以取消分组捕获,如/(?:\d{1,2})(a|b)([A-Z])/
,第一个分组取消捕获,则\1
表示分组(a|b)
,\2
表示分组([A-Z])
。
-
-
特殊位置
^
表示匹配开头,如/^[a-z]/
表示匹配以 a - z 之间的字母开头。$
表示匹配结尾,如/[a-z]/
表示匹配以 a - z 之间的字母结尾。注意:
^
和$
合用时,表示完整匹配,如/^(x|y)$/
表示匹配 x 或 y,/^x|y$/
表示匹配 x 开头 或 y 结尾。\b
表示匹配边界,边界指:开头,空格,标点,结尾。如\b([a-zA-Z]+\b)
匹配字符串'this is,a.regExp'
的结果为:this
,is
,a
,regExp
。'this is,a.regExp'.match(/\b([a-zA-Z])+\b/g) // ['this', 'is', 'a', 'regExp']
-
-
正则表达式
JavaScript 中定义正则表达式的方法有两种:字面量法 和 构造函数法。
-
字面量法
var reg = /^a[a-z]+z$/ig
-
构造函数法
var reg = new RegExp('^a[a-z]z$', 'ig') var regBound = new RegExp('\\b([a-zA-Z])+\\b', 'ig') // 注意:字符串输出 `\` 需要转义,此类字符为转义字符。
注意:
i
表示匹配过程中忽略大小写,g
表示开启贪婪匹配,正则默认是非贪婪匹配的,一旦匹配则返回,不再继续匹配。 -
-
正则表达式 API
-
reg.test()
函数接收一个参数,用正则 reg 验证参数是否匹配,如果匹配,返回 true,反之返回 false。
-
reg.exec()
函数接收一个参数,在参数中查找匹配 reg 的关键词,如果找到,返回关键词信息数组,反之返回 null。
注意:其他与正则相关的 API 在 String 对象中会有体现,之所以没有放在这里,是因为调用方式不同。
-
Date
Date 是定义日期时间和提供操作日期时间 API 的对象。
-
日期时间
定义日期时间的方法只有 构造函数法。
var date = new Date() // 当前时间 var date1 = new Date('2023/3/15 8:10:30') // < 10 可不前缀 0 var date2 = new Date('2023-03-15 08:10:30') var date3 = new Date('2023/03/15') // Wed Mar 15 2023 08:00:00 GMT+0800 var date4 = new Date('08:10:30')
注意:
a.
new Date()
不传参生成当前日期时间对象,传参须按照上述格式,生成对应日期时间对象。b.
new Date()
不传参,取当前时间,取的实际是当前系统的时间。没错,就是电脑或手机的时间,你不准,它也不准。c.
new Date()
如果只传入日期,则取格林尼治时间 00:00:00,对应系统当前所在时区计算实际时间,北京,东 8 区,加 8 小时,为 08:00:00。d.
new Date()
如果只传入时间,则无法生成日期对象,返回Invalid Date
。 -
日期时间 API
-
date.getFullYear()
读取 date 对象中的年。
-
date.setFullYear()
接收一个参数,参数为字符串会用 Number() 转换,设置 date 对象中的年。
-
date.getMonth()
读取 date 对象中的月,获取的月份是在 0 - 11 之间,因此计算实际月份需要 +1。
-
date.setMonth()
接收一个参数,参数为字符串会用 Number() 转换,设置 date 对象中的月。
注意:设置月份需要结合年和日,设置月超过 12,则年 + 1,设置月 - 12 为实际月。如果设置月,不存在当前日,则设置不生效,如当前
31
日,设置2
或4
月均不生效。 -
date.getDate()
读取 date 对象中的日。
-
date.setDate()
接收一个参数,参数为字符串会用 Number() 转换,设置 date 对象中的日。
注意:设置日需要结合当前月确定,超出当前月的最大日,则月份 + 1,设置日 - 最大日为实际日。
-
date.getHours()
读取 date 对象中的时。
-
date.setHours()
接收一个参数,参数为字符串会用 Number() 转换,设置 date 对象中的时。
注意:设置时超过 24 ,则日期会自动 +1,设置时 -24 为实际时。
-
date.getMinutes()
读取 date 对象中的分。
-
date.setMinutes()
接收一个参数,参数为字符串会用 Number() 转换,设置 date 对象中的分。
注意:设置分超过 60,则时会自动 + 1,设置分 -60 为实际分。
-
date.getSeconds()
读取 date 对象中的秒。
-
date.setSeconds()
接收一个参数,参数为字符串会用 Number() 转换,设置 date 对象中的秒。
注意:设置分超过 60,则时会自动 + 1,设置分 -60 为实际秒。
-
date.toJSON()
当前系统时间转换为格林尼治时间输出,即不计算时区的时差。
-
date.getTime()
当前系统时间转换为时间戳(13位,毫秒级)输出,时间戳:1970-01-01 00:00:00 至今的毫秒数,时间运算底层是通过时间戳计算的。
-
date.toLocaleString()
格式化输出系统日期时间,如
yyyy/mm/dd 上午hh:mm:ss
,12小时制。注意:
a. toLocaleString() 在浏览器环境输出格式为
yyyy/mm/dd hh:mm:ss
,24小时制;在 node 环境输出格式为yyyy/mm/dd 上午hh:mm:ss
,12小时制。b. 统一设置 24 小时制的方式:
date.toLocaleString('chinese',{hour12: false})
-
data.toLocaleDateString
格式化输出系统日期,如
yyyy/mm/dd
。 -
data.toLocaleTimeString
格式化输出系统时间,如
下午hh:mm:ss
。
-
Error
Error 是定义错误和封装错误类型的对象。
-
错误类型及定义错误
-
Error
泛错误,不区分错误类型。定义错误:
new Error()
接受一个参数,参数为错误描述。 -
SyntaxError
语法错误。定义错误:
new SyntaxError()
接收一个参数,参数为错误描述。 -
ReferenceError
引用错误,一般指变量或对象属性未找到。定义错误:
new ReferenceError()
接收一个参数,参数为错误描述。 -
TypeError
类型错误,一般指错误使用
()
,[]
或 错误使用对象方法。定义错误:new TypeError()
接收一个参数,参数为错误描述。 -
RangeError
范围错误,一般指超出预设范围,如
toFixed()
参数范围为 0 - 20,超出则报错。定义错误:new RangeError()
接收一个参数,参数为错误描述。 -
URIError
URI 错误,一般指 URI 拼接错误。定义错误:
new URIError()
接收一个参数,参数为错误描述。 -
EvalError
Eval 错误,指 eval() 函数错误,一般不被抛出。
注意:一般定义抛出异常,使用
new Error()
即可。 -
-
抛出异常
throw new Error('自定义异常描述。')
注意:自定义抛出异常多用于表单校验。
-
捕获异常
try { // 可能存在异常代码 } catch(err) { // err 为错误对象,可以抛出异常 } finally { // 是否抛出异常都会执行的代码,多用于资源回收 }
Number
Number 是基本数据类型 Number 的包装对象。
Number 是定义 Number 类型数据和提供操纵 Number 类型数据 API 的对象。
-
Number 类型数据
Number 类型数据定义有两种方式:字面量法 和 构造函数法
-
字面量法
var num = 12.3
-
构造函数法
var num = new Number(12.3)
-
-
Number 类型 API
-
num.toExponential()
函数返回科学计数法的数字输出,接收一个参数,指定小数位数,参数范围为 0 - 20,四舍五入方式保留小数位数。
-
num.toFixed()
函数返回四舍五入后的数字输出,接收一个参数,参数指定小数位数,参数范围为 0 - 20,四舍五入方式保留小数位数。
-
num.toLocaleString()
函数返回数字的格式化字符串输出,默认返回千分位格式,如
1,000
。注意:
a.
num.toLocaleString('fi-FI')
输出空格格式千分位,如1 000
。b.
num.toLocaleString("zh-CN", {style:"currency", currency:"CNY"})
输出人民币千分位格式,如¥1,000
。c.
num.toLocaleString("en-US", {style:"currency", currency:"USD"})
输出美元千分位格式,如$1,000
。 -
num.toPrecision()
函数返回指定长度的数字输出,接收一个参数,参数指定数字长度,参数范围 0 - 100,四舍五入方式保留数字位数。
-
num.toString()
函数返回数字的字符串输出。
-
String
String 是基本数据类型 String 的包装对象。
String 是定义 String 类型数据,封装 String 类型数据属性和 String 类型数据 API 的对象。
-
String 类型
String 类型数据定义有两种方式:字面量法 和 构造函数法
-
字面量法
var str = 'javascript'
-
构造函数法
var str = new String('javascript')
-
-
String 类型数据属性
-
str.length
返回当前字符串长度。
-
-
String 类型数据 API
字符串是不可变的,任何方式处理字符串以后,原字符串不发生改变,只可能返回一个新的字符串。
-
str.charAt()
返回指定位置的字符。
不传参返回第一个字符;
传入位置参数则返回当前位置的字符,位置参数范围为
0 - (str.length - 1)
,如果参数在范围之外,则返回''
空字符串。 -
str.at()
返回指定位置的字符。
不传参返回第一个字符;
传入位置参数则返回当前位置的字符,位置参数范围为
(-str.lengh) - (str.length - 1)
,如果参数在范围之外,则返回undefined
。参数为负,实际返回(负值 + str.length)
位置的字符。注意:
str[i]
也可以返回指定位置的字符,i 的范围为0 - (str.length - 1)
,范围之外返回undefined
。 -
str.toLowerCase()
返回一个转换为全大写字母的字符串。
-
str.toUpperCase()
返回一个转换为全小写字母的字符串。
-
str.charCodeAt()
返回指定位置字符的 Unicode 编码。
不传参返回第一个字符的 Unicode 编码;
传入位置参数则返回当前位置字符的 Unicode 编码,位置参数范围为
0 - (str.length - 1)
,如果参数在范围之外,则返回NaN
。 -
str.codePointAt()
返回指定位置字符的 Unicode 编码。不传参返回第一个字符的 Unicode 编码,传入位置参数则返回当前位置字符的 Unicode 编码,位置参数范围为
0 - (str.length - 1)
,如果参数在范围之外,则返回undefined
。注意:静态方法
String.fromCharCode()
接收一个 Unicode 编码作为参数,返回 Unicode 编码对应的字符。 -
str.concat()
传入参数,参数会通过 String() 函数处理成字符串参数,后返回一个当前字符串与传入字符串参数拼接后的字符串,可传入多个参数,如
'hello'.concat('javascript', 3)
返回'hellojavascript3'
。注意:字符串拼接可以直接使用
str1 + str2
的方式。 -
str.repeat()
不传参返回
''
空字符串;传入参数 n 则返回一个当前字符串重复 n 次后的字符串,n 范围为 0 - ∞。如
'hello'.repeat(2)
返回'hellohello'
。 -
str.padStart()
可以接收两个参数 maxlength 和 b。
传入一个参数 maxlength,maxlength 表示最大长度,如果 maxlength 小于等于当前字符串长度,则返回当前字符串,大于则返回一个长度为 maxlength,以空格开头补全的字符串,如
'hello.padStart(6)'
返回' hello'
;传入两个参数,参数 b 通过 String() 函数转换字符串,再以 b 字符串开头补全字符串,根据 maxlength 参数最大长度, b 字符串会发生截取或重复,如
'hello'.padStart(10, 'ab')
返回ababahello
。 -
str.padEnd()
同上,结尾补全。如
'hello'.padEnd(10, 'ab')
返回helloababa
-
str.trim()
去除当前字符串左右两边的空格。如
' hel ll o '.trim()
返回'hel ll o'
。注意:无法去除字符串内部的空格。
-
str.trimStart()
,str.trimLeft()
去除当前字符串左边(即开头)的空格,如
' hel ll o '.trimStart()
等同于' hel ll o '.trimLeft()
返回'hel ll o '
。 -
str.trimEnd()
,str.trimRight()
去除当前字符串右边(即结尾)的空格,如
' hel ll o '.trimEnd()
等同于' hel ll o '.trimRight()
返回' hel ll o'
。 -
str.slice()
可以接收两个参数 start 和 end。
传入一个参数 start,start 表示开始位置,返回一个当前字符串从开始位置至结尾的所有字符组成的子字符串。start 取值范围为 -∞ - +∞,当 start 取值在 (-∞, -str.length] 时,返回当前字符串,如
'hello'.slice(-5)
返回'hello'
;当 start 取值在 (-str.length, -1] 时,返回 str.length + start 至结尾的子字符串,如'hello'.slice(-3)
返回'llo'
;当 start 取值在 [0,str.length) 时,返回 start 至结尾的子字符串,如'hello'.slice(2)
返回'llo'
;当 start 取值在 [str.length, +∞) 时,返回 '' 空字符串,如'hello'.slice(5)
返回''
。传入两个参数,end 表示结束位置,返回一个当前字符串从开始位置至结束位置的所有字符(不含结束位置字符)组成的子字符串。其中,end > start。
-
str.substring()
同上,但不支持负数位置,即当 start 取值在 (-∞, 0) 时,返回当前字符串。
-
str.substr()
可以接受两个参数 start 和 n。
传入一个参数时同
str.slice()
一样。传入两个参数,n 表示返回子字符串长度,即 从 start 位置开始向后截取 n 个字符组成子字符串返回。
-
str.startsWith()
接收一个参数,参数会通过 String() 函数转换字符串,判断当前字符串是否以参数字符串开头,是则返回 true,反之返回 false。如
'hello'.startsWith('he')
返回 true。 -
str.endsWith()
接收一个参数,参数会通过 String() 函数转换字符串,判断当前字符串是否以参数字符串结尾,是则返回 true,反之返回 false。如
'hellotrue'.endsWith(true)
返回 true。 -
str.indexOf()
接收一个参数,参数会通过 String() 函数转换字符串,先判断当前字符串是否包含参数字符串,不包含则返回 -1,包含的话,则会从当前字符串开头位置查找参数字符串第一个字符所在位置,一旦找到就返回其所在位置,不再继续查找。如
'hello'.indexOf('l')
返回2
。注意:字符在字符串所在位置被称为字符串索引,字符串索引由 0 开始,自左向右递增;当字符串索引为负时,由 -1 开始,自右向左递增。
-
str.lastIndexOf()
同上,只是查找顺序是从当前字符串结尾查找。如
'hello'.lastIndexOf('l')
返回3
。 -
str.includes()
接收一个参数,参数会通过 String() 函数转换字符串,判断当前字符串是否包含参数字符串,包含则返回 true,反之为 false。
-
str.search()
接收一个参数,参数可以为基础数据类型,也可以为正则表达式。
参数为基础数据类型时,等同于
str.indexOf()
。参数为正则表达式,从当前字符串中查找满足正则表达式的子字符串,找到则返回子字符串第一个字符所在位置,反之返回 -1 。
-
str.match()
接收一个参数,参数可以为基础数据类型,也可以为正则表达式。
参数为基础数据类型时,参数会通过 String() 函数转换字符串,判断当前字符串是否包含参数字符串,不包含返回 null,包含则返回由参数字符串,参数字符串第一个字符所在位置,以及其他信息组成的类数组。
参数为正则表达式,从当前字符串中查找满足正则表达式的子字符串,找到则返回由参数字符串,参数字符串第一个字符所在位置,以及其他信息组成的类数组,反之返回 null 。
-
str.replace()
接收两个参数 target 和 replace。target 表示需要被替换的内容,replace 表示替换的内容,replace 会通过 String() 函数转换字符串。
target 参数可以为基础数据类型或正则表达式。
target 为基础数据类型时,会通过 String() 函数转换字符串,在当前字符串中查找 target 子字符串,并替换为 replace 字符串,返回一个替换后的字符串,如
'hello'.replace('l',1)
返回'he1lo'
;target 参数为正则表达式时,在当前字符串中查找满足正则表达式的子字符串,并替换为 replace 子字符串,返回一个替换后的字符串,如
'hello'.replace(/ll/, 'r')
返回'hero'
。注意:replace 查找 target 子字符串属于非贪婪匹配,例子中
hello
替换l
只替换了第一个。 -
str.replaceAll()
str.replace()
的贪婪匹配模式,如'hello'.replaceAll('l', 1)
返回'he11o'
。注意:
str.replace()
可以通过第二参数传入函数的方式实现str.replaceAll()
。str.replace(/reg/, function (match) { // match 为 reg 匹配的每一个结果 return R // R 为替换内容 })
-
str.split()
接收一个参数。
不传参返回当前字符串组成的数组,如
'hello'.split()
返回['hello']
。传一个参数,参数会通过 String() 函数转换字符串,在当前字符串中查找参数字符串(贪婪匹配),未找到则返回当前字符串组成的数组,如果找到了,则以参数字符串分割当前字符串为多个子字符串,多个子字符串组成数组返回,如
'hello'.split('l')
返回['he', '', 'o']
。注意:
'hello'.split('')
返回['h', 'e', 'l', 'l', 'o']
。
-
Boolean
Boolean 是定义 Boolean 数据类型和提供操作 Boolean 类型数据 API 的对象。
-
Boolean 类型数据
Boolean 类型数据定义有两种方式:字面量法 和 构造函数法。
-
字面量法
var bool = true
-
构造函数法
var bool = new Boolean(true)
-
Array
Array 是定义 Array 数据类型,封装 Array 类型属性以及 Array 类型 API 的对象。
-
Array 类型
Array 类型数据定义有两种方式:字面量法 和 构造函数法。
-
字面量法
var arr = [1, true, '', null, undefined, [1,2]]
-
构造函数法
var arr = new Array(1,'', false, null, undefined, [1,2]) var arr1 = new Array(3) // [undefined, undefined,undefined]
注意:
a. 数组内的项可以称之为元素,数组的元素可以是任意类型数据。
b.
new Array()
如果传入参数为 1 个且为 Numer 类型,则返回一个长度等于参数,元素均为 undefined 的数组。c. 可以通过
Array.isArray()
判断是否是 Array 类型。 -
-
Array 类型数据属性
-
arr.length
返回当前数组的长度。
-
-
Array 类型数据 API
数组是可变的,数组经过 API 处理以后,原数组可能发生改变。
-
arr.at()
返回指定位置的元素。
不传参返回第一个元素;
传入位置参数则返回当前位置的元素,位置参数范围为
(-str.lengh) - (str.length - 1)
,如果参数在范围之外,则返回undefined
。参数为负,实际返回(负值 + arr.length)
位置的元素。注意:
arr[i]
也可以返回指定位置的元素,i 为数组索引,i 的范围为0 - (str.length - 1)
,范围之外返回undefined
。 -
arr.pop()
不接受参数,原数组从结尾处推出一个元素,并返回
推出的元素
,原数组改变。 -
arr.push()
接收多个参数,原数组从结尾处按照参数原有的顺序推入多个参数,并返回
推入后数组的长度
,原数组改变。如[3,4,5].push(6,7)
返回5
,原数字变为[3,4,5,6,7]
。 -
arr.shift()
不接受参数,原数组从开头处推出一个元素,并返回
推出的元素
,原数组改变。 -
arr.unshift()
接收多个参数,原数组开头处按照参数原有的顺序推入多个参数,并返回
推入后数组的长度
,原数组改变。如[3,4,5].push(1,2)
返回5
,原数字变为[1,2,3,4,5]
。 -
arr.fill()
var arr = [2, 3, '', false, null, undefined, 1, 4, 5] arr.fill() // [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined] arr.fill('h') // ['h', 'h', 'h', 'h', 'h', 'h', 'h', 'h', 'h'] arr.fill('l', 4) // ['h', 'h', 'h', 'h', 'l', 'l', 'l', 'l', 'l'] arr.fill('o', 4, 6) // ['h', 'h', 'h', 'h', 'o', 'o', 'l', 'l', 'l']
接收三个参数,第一个参数为替换元素,第二、三为起始和终止位置。
不传参数,原数组中元素均被替换为 undefined;
传入一个参数,原数组中元素均被替换为参数元素;
传入两个参数,原数组中起始位置开始至结束的元素被替换为参数元素;
参数三个元素,原数组中起始元素至终止位置的元素被替换为参数元素,不含终止位置的元素。
返回
替换后的数组
,原数组改变。 -
arr.copyWithin()
var arr = [1,2,3,4,5,6] arr.copyWithin() // [1, 2, 3, 4, 5, 6] arr.copyWithin(4) // [1, 2, 3, 4, 1, 2] arr.copyWithin(4, 2) // [1, 2, 3, 4, 3, 4] arr.copyWithin(2, 0, 1) // [1, 2, 1, 4, 3, 4]
接收三个参数,第一个参数为被替换元素位置,第二、三个参数为复制元素的起始位置和终止位置。
传入一个参数,该参数既为复制元素的终止位置,又为被替换元素的起始位置。从开始至参数位置复制元素,不包含参数位置的元素,在参数位置处替换当前位置及以后与复制元素同个数的元素,若后续元素个数小于复制元素的个数,则切割复制元素,使数组长度保持不变。
传入两个参数,从第二个参数位置处复制元素至结尾,在第一个参数位置开始替换元素,包括其后续元素,保持数组长度不变。
传入三个参数,从第二参数位置出复制至第三个参数处结束,不包含第三个参数位置处的元素,在第一个参数位置开始替换元素,包括其后续元素,保持数组长度不变。
返回
替换后的数组
,原数组改变。 -
arr.concat()
接收多个参数,参数按照传参顺序拼接在数组的结尾,返回
拼接后的数组
,原数组改变。 -
arr.includes()
接收一个参数,判断参数是否在数组中,是则返回
true
,反之返回false
,原数组不改变。 -
arr.indexOf()
接收一个参数,从数组开头处查找并判断参数是否在数组中,不在则返回
-1
,在则返回查找到的第一个元素的索引
,原数组不改变 -
arr.lastIndexOf()
接收一个参数,从数组结尾处查找并判断参数是否在数组中,不在则返回
-1
,在则返回查找到的第一个元素的索引
,原数组不改变 -
arr.slice()
接收两个参数 start 和 end,用法同:
str.slice()
。返回切割后的数组
,原数组不改变。 -
arr.splice()
接收多个参数,第一个参数表示起始位置,第二参数表示删除元素个数,其余参数表示新增的元素。如
arr.splice(2, 3, 'a', 'b')
,表示从数组 arr 索引 2 位置开始,删除 3 个元素,后从索引 2 位置开始插入 a 和 b 两个元素。接收一个参数,数组从参数位置删除至数组结尾。
接收两个参数,数组从第一个参数位置开始删除元素,删除与第二个参数相同个数的元素。
接收两个以上参数,数组从第一个参数位置开始删除元素,删除与第二个参数相同个数的元素,并在第一个参数位置开始添加第三个及以后的参数。
返回
删除元素组成的数组
,原数组改变。注意:
arr.toSpliced()
同上用法,但 toSpliced() 方法使用过数组的副本进行修改的,不改变原数组。 -
arr.with()
接收两个参数,第一个参数表示数组索引位置,第二个参数表示替换元素。
接收一个参数,数组中当前参数位置的元素会被替换为 undefined。
接受两个参数,数组中第一个参数位置出的元素会被替换为第二参数。
返回
替换后的数组
,原数组不改变。注意:修改数组中元素也可以使用
arr[i] = x
的方式修改,但是这种修改方式会改变原数组。 -
arr.reverse()
数组倒序排列,返回
倒序排列后的数组
,原数组改变。注意:
arr.toReversed()
同上用法,但 toReversed() 方法使用过数组的副本进行修改的,不改变原数组。 -
arr.sort()
数组排序,按照数组元素的 Unicode 编码排序,默认升序排列,如
[1,10,2,20].sort()
返回[1, 10, 2, 20]
,返回排序后的数组
,原数组改变。注意:
a. 数字数组正常大小排序需要传入排序函数:arr.sort((a,b) => a - b)
表示升序排列,arr.sort((a,b) => b - a)
表示降序排列。arr.toSorted()
同上用法,但 toSorted() 方法使用过数组的副本进行修改的,不改变原数组。 -
arr.join()
不传入返回当前数组元素的字符串格式,等同于
arr.toString()
。如[1,2,3,4].join()
返回'1,2,3,4'
。传入参数,以参数为分隔符分隔多个数组元素,如
[1,2,3,4].join(2)
返回'1222324'
。返回
数组元素的字符串形式
,原数组不改变。注意:
[1,2,3,4].split('')
返回'1234'
。 -
arr.toString()
返回
数组的字符串格式
,原数组不改变。如[1,2,3,4].toString()
返回'1,2,3,4'
,其实是将[]
替换为''
。 -
arr.find()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
find() 接收一个断言函数,该函数返回一个断言,按顺序从头开始查找数组元素是否满足断言,一旦满足则返回
当前元素
,不再继续查找,反之返回undefined
,原数组不改变。如
[1,2,3,4,5].find((item, index, arr) => item > 3)
返回4
。 -
arr.findLast()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
findLast() 接收一个断言函数,该函数返回一个断言,按顺序从结尾开始查找数组元素是否满足断言,一旦满足则返回
当前元素
,不再继续查找,反之返回undefined
,原数组不改变。如
[1,2,3,4,5].find((item, index, arr) => item > 3)
返回5
。 -
arr.findIndex()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
findIndex() 接收一个断言函数,该函数返回一个断言,按顺序从头开始查找数组元素是否满足断言,一旦满足则返回
当前元素的索引
,不再继续查找,反之返回-1
,原数组不改变。如
[1,2,3,4,5].findIndex((item, index, arr) => item > 3)
返回3
。 -
arr.findLastIndex()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
findLastIndex() 接收一个断言函数,该函数返回一个断言,按顺序从结尾开始查找数组元素是否满足断言,一旦满足则返回
当前元素的索引
,不再继续查找,反之返回-1
,原数组不改变。如
[1,2,3,4,5].findLastIndex((item, index, arr) => item > 3)
返回4
。 -
arr.every()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
every() 接受一个断言函数,该函数返回一个断言,按顺序从头开始查找数组元素是否满足断言,都满足则返回
true
,反之返回false
,原数组不改变。如
[1,2,3,4,5].every((item, index, arr) => item > 0)
返回true
。 -
arr.some()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
some() 接受一个断言函数,该函数返回一个断言,按顺序从头开始查找数组元素是否满足断言,一旦有一个元素满足则返回
true
,反之返回false
,原数组不改变。如
[1,2,3,4,5].some((item, index, arr) => item > 2)
返回true
。 -
arr.filter()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
filter() 接收一个断言函数,该函数返回一个断言,按顺序从头开始查找数组元素是否满足断言,返回
满足断言的元素组成的数组
,均不满足则返回[]
,原数组不改变。如
[1,2,3,4,5].filter((item, index, arr) => item > 3)
返回[4,5]
。 -
arr.forEach()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
forEach() 接收一个常规函数,该函数无返回值,可以通过
arr[i]
修改元素,无返回,原数组改变。var arr = [1,2,3,4,5] arr.forEach((item, index, arr) => { if(item > 4) { arr[index] = 4 } }) // arr = [1, 2, 3, 4, 4]
-
arr.map()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
map() 接收一个常规函数,该函数有返回值,返回值会修改当前索引位置的元素,返回
修改后的数组
,原数组不改变。var arr = [1,2,3,4,5] var temp = arr.map((item, index, arr) => { if(item > 4) { return 4 } else { retrun item } }) // temp = [1, 2, 3, 4, 4] // arr = [1, 2, 3, 4, 5]
-
arr.reduce()
高阶方法,接收一个回调参数,回调参数的参数分别为汇总值,当前数组的元素,当前数组的索引以及当前数组。
reduce() 接收一个常规函数,该函数有返回值,返回
数组元素从左至右累计计算结果
,原数组不改变。注意:reduce() 可以接受第二参数作为汇总值的初始值。
如
[1,2,3,4,5].reduce((pre, item, index, arr) => pre + item, 10)
返回25
。 -
arr.reduceRight()
用法同上,返回
数组元素自右向左累计计算结果
,原数组不改变。 -
arr.flat()
二维数组:数组元素为数组时,则该数组被称为二维数组,如
[1,2,3,[4,5]]
。flat() 扁平化二维数组为一维数组,返回
扁平化后的一位数组
,原数组不改变。如
[1,2,3,[4,5]].flat()
返回[1,2,3,4,5]
。注意:
arr.flat()
只能用于扁平化二维数组,三维及以上无法继续扁平化。 -
arr.flatMap()
高阶方法,接收一个回调参数,回调参数的参数分别为当前数组的元素,当前数组的索引以及当前数组。
flatMap() 是 flat() 和 map() 的合体,接收一个常规函数,该函数有返回值,返回值为数组,用于修改当前索引位置的元素,如此会返回一个二维数组,在通过 flat() 扁平化为一维数组返回,原数组不改变。
如
[1,2,3].flatMap((item, index, arr) => [ item * 2 ])
返回[2,4,6]
。
-
Function
Function 是定义 Function 数据类型,封装 Function 类型属性的对象。
-
Function 数据类型
Function 函数定义的方式有:函数声明法,函数表达式法,构造函数法。
-
函数声明法
function fun(arg) { // 函数体 return arg }
-
函数表达式法
var fun = function(arg) { // 函数体 return arg }
注意:函数表达式和函数声明因 JavaScript 的声明提升特性,有着本质的区别,可参考 V8中的函数表达式。
-
构造函数法
var fun = new Function('arg', '// 函数体 \n return arg')
注意:函数的三要素:函数名,参数,返回值。在 JavaScript 中,函数名相当于变量名,函数也可以作为参数和返回值,因此,函数是"一等公民"。
-
-
作用域链
作用域:变量的可用范围,实质是一个保存变量的对象,各函数的作用域相互独立,互相之间不受干扰,闭包除外。
根据变量定义位置不同,可以区分为全局变量和局部变量。定义函数之外称为全局变量,全局变量挂载的对象,称之为全局作用域;定义函数内称之为局部变量,局部变量挂载的对象称之为局部作用域。
-
全局作用域
全局作用域是“天生”存在的,即代码执行前,JavaScript 引擎会初始化一段内存,作为全局作用域对象,在浏览器环境是 window 对象,node 环境是 global 对象。
全局作用域的特点是:公共。所有函数均可使用其内部保存的全局变量。
-
局部作用域
局部作用域是临时的,它的存在仅限于函数的生命周期范围内,即函数创建时,初始化一段内存,作为函数的局部作用域对象,称之为活动对象(AO);函数调用完成,内存释放,局部作用域 AO 对象被销毁。
局部作用域的特点是:私有,临时。函数不能使用其他函数局部作用域保存的变量,且函数调用结束,局部作用域内的变量销毁。
函数调用执行变量查找方式为:局部作用域 --> 全局作用域,这便形成了作用域链。
注意:函数内可以访问函数外的变量,即局部作用域 --> 全局作用域。函数外不可访问函数内部的变量,即全局作用域 -X-> 局部作用域
-
-
函数的调用流程
var a = 1 function fun() { var b = 2 console.log(a, b) } fun() console.log(a)
-
闭包
闭包是指有权在函数外访问函数内部变量的函数。
var a = 1 function outer() { var b = 1; function inner() { var c = 1; b++; console.log(a, b, c) } return inner } var get = outer() get() // 1 2 1 get() // 1 3 1
函数对象通过 scope 隐藏属性指向其作用域,函数作用域通过 parent 隐藏属性指向其父作用域,这里能更清晰的理解作用域链。
上述过程中,
outer
函数执行完成以后,outer AO
本该销毁,但是outer
的返回函数inner
引用了outer AO
,因此outer AO
会常住内存。闭包成立条件:
-
外部函数必须返回内部函数,即
outer
必须返回inner
。 -
内部函数必须引用外部函数的变量,即
inner
必须引用变量b
。
闭包的特点:
-
变量
b
是闭包维护的私有变量,外部无法篡改,不会轻易释放,可以被重用。 -
改变了变量的访问形式,由变量名的访问形式变成了函数调用的访问形式。
释放闭包:
get = null
。 -
-
匿名函数和IIFE
-
匿名函数
匿名函数是未命名的函数,没有函数名,相当于没有变量,匿名函数就如同一个特殊数据类型的值,可以作为参数,返回值等。
arr.sort(function (a, b) { return a - b }) // 匿名函数作为参数,即回调函数
-
IIFE
IIFE 即立即执行函数,简单来说就是匿名函数自调,定义的同时调用。
var res = (function (x, y) { return x + y })(1,2) console.log(res) // 3
注意:IIFE 可以避免内存泄露以及过多占用内存的情况,开发过程中,使用 IIFE 的作用域特性也可以隔离变量,防止命名冲突。
-
Object
Object 是定义 Object 数据类型,封装 Object 类型属性的对象。
-
Object 数据类型
Object 类型定义的方式有:字面量法,构造函数法。
-
字面量法
var obj = { name: 'Jason', age: 19, getName: function () { return this.name } }
-
构造函数法
var obj = new Object() obj.name = 'Jason' obj.age = 19 obj.getName = function () { return this.name }
-
-
对象的属性和方法
对象的本质是将一组变量和函数封装在同一命名空间下。对象封装的变量称为对象的属性,对象封装的函数称为对象的方法。
-
属性
对象的属性通过键值对表示的,键称之为属性名,值称为属性值。
属性名可以使用无
''
字符串或数字表示,属性值可以使用任意数据类型。var obj = { 1: 1, name: 'jason', true: true, null: null, undefined: 'undefined', arr: [1,2,3], }
属性的调用可以是使用
.
或[]
的方式调用,如obj.name
和obj['name']
。但是,对于数字类型或者变量的属性名,必须使用[]
调用,如obj[1]
。var a = 'name' var obj = { name: 'jason' } console.log(obj[a]) // jason
注意:属性名定义时使用无
''
字符串,但是使用[]
调用时,需要添加''
,如果是变量则无需添加。Object.getOwnPropertyNames(obj)
可以返回对象属性名组成的数组。 -
方法
可以预见,属性值为 function 类型时,则属性就称之方法。
var obj = { name: 'jason' run: function () { console.log('runing man') }, getName: function() { console.log(this.name) } }
方法的调用类似函数的调用,如
obj.run()
。注意:对象内部可以定义方法,方法中可以通过
this
关键字引用对象的属性,此时的this
指向当前对象,可以 替换this.name
为obj.name
。
-
-
原型链和面向对象
JavaScript 不是面向对象的语言,没有 class,extends 等类相关的关键字。面向对象语言的三大特性:封装、继承、多态,JavaScript只能通过原型链的方式去模拟。
-
原型链
原型链是由多级父对象逐级继承形成的链式结构,规定对象属性查找的范围,类似作用域链,属性查找:当前对象 --> 父对象 --> 祖父对象。具体可参考 v8中JavaScript对象。
对象的属性可以是自身属性也可以继承属性,如何区分自有属性和共有属性呢?
obj.hasOwnProperty('attr')
返回 true 表示attr
为obj
的自有属性,返回 false,结合obj.attr !== undefined
可以判断attr
为obj
的共有属性。 -
面向对象
JavaScript利用构造函数和原型模拟类,通过构造函数和原型的组合模拟继承。
构造函数创建的同时,原型对象就会被自动创建,构造函数通过
prototype
指向原型对象,原型对象通过constructor
指向构造函数。var fun = new Fun() function Fun() { // 构造函数 } Fun.prototype.run = function () { console.log('running man') } fun.run() // running man console.log(Fun.prototype) // {run: ƒ, constructor: ƒ} console.log( Fun.prototype.constructor) // Fun
原型对象添加方法时,只能通过
prototype.xx
的方式逐个添加,不能使用prototype = { xx: ..., xxx: ... }
的方式添加。其原因是后面方式添加,原型对象赋值等于重写了原型对象,原型对象的constructor
会指向 Object。此外,也丧失原型的动态性(动态性:实例在原型之前创建,修改原型,实例能立即提现)。var fun = new Fun() function Fun() { // 构造函数 } Fun.prototype = { constructor: Fun, run: function () { console.log('running man') } } console.log(Fun.prototype) // {constructor: 'Fun', run: ƒ} console.log( Fun.prototype.constructor) // Fun fun.run() // TypeError: fun.run is not a function
-
继承
原型式继承
function Father(argus) { this.argus = argus } Father.prototype.method = function () { ... } function Son(params) { this.params = params } Son.prototype = new Father() Son.prototype.constructor = Son Son.prototype.methond = function () { ... } // 可以重写父方法,多态特性
原型式继承存在的问题:
-
子类实例化过程中,无法给父类传参。
-
父类属性为引用类型时,子类继承属性,子类继承的属性修改时,会关联父类实例化的其他子类。
构造函数式继承
function Father(argus) { this.argus = argus } Father.prototype.method = function () { ... } function Son(argus, params) { Father.call(this, argus) this.params = params } Son.prototype.method = function () { ... }
构造函数式继承存在的问题:
-
子类只继承父类构造函数,父类的原型方法对子类不可见。
-
子类实例化会执行一次父类构造函数。
组合式继承
function Father(argus) { this.argus = argus } Father.prototype.method = function () { ... } function Son(argus, params) { Father.call(this, argus) this.params = params } Son.prototype = new Father() Son.prototype.constructor = Son Son.prototype.method = function () { ... }
组合式继承存在的问题:
- 子类实例化执行两次
寄生式继承
function inherit(child, parent) { function Fun() { this.constructor = child } Fun.prototype = parent.prototype child.prototype = new Fun() return child } function Father(argus) { this.argus = argus } Father.prototype.method = function () { ... } function Son(params) { this.params = params } inherit(Son, Father) Son.prototype.method = function () { ... }
寄生式继承存在的问题:
- 子类实例化无法继承父类构造函数属性
寄生组合式继承
function inherit(child, parent) { function Fun() { this.constructor = child } Fun.prototype = parent.prototype child.prototype = new Fun() return child } function Father(argus) { this.argus = argus } Father.prototype.method = function () { ... } function Son(argus, params) { Father.call(this, argus) this.params = params } inherit(Son, Father) Son.prototype.method = function () { ... }
寄生组合式继承是目前模拟继承较为妥当的方式。
-
-
后记
JavaScript 的核心语法知识包括:基本语法和内置对象,新增的 JavaScript 相关语法主要划分为两个版本:ES5 和 ES6。鉴于篇幅问题,ES5 和 ES6 梳理为碎片化学习即可,ES5 主要是规范性问题的研究,ES6 主要是简化开发问题的研究,可作为 JavaScript 内容学习的补充,简而言之,没有 ES5 和 ES6 你照样能工作!
本文来自博客园,作者:深巷酒,转载请注明原文链接:https://www.cnblogs.com/huangminghua/p/17188601.html