负责页面中的的行为,运行在浏览器端的脚本语言。
输出语句
- alert(): 该语句会在浏览器窗口中弹出一个警告框
1
alert("要输出的内容");
- document.write(): 该内容将会被写到 body 标签中,并在页面中显示
1
document.write("要输出的内容");
- console.log(): 该内容会被写到开发者工具的控制台中
1
console.log("要输出的内容");
基本语法
- 单行注释 // …
- 多行注释 /_ … _/
- JS 中严格区分大小写
- JS 中每一条语句以分号(;)结尾,只有单行内需要分割语句,这个分号才是必须的
- JS 中会忽略多个空格和换行
- JS 语句是按照自上向下的顺序一条一条执行的
- 在 JS 中可以使用{}来为语句进行分组,同一个{}中的语句我们称为是一组语句,这一组语句我们也称为一个代码块
标识符
- 在 JS 中所有的可以由我们自主命名的都可以称为是标识符
- 例如:变量名、函数名、属性名都属于标识符
- JS 底层保存标识符时实际上是采用的 Unicode 编码,所以理论上讲,所有的 utf-8 中含有的内容都可以作为标识符
- 命名规则:
- 标识符中『可以』含有字母、数字、_、$
- 标识符『不能』以数字开头
- 标识符『不能』是 ES 中的关键字或保留字
- 标识符一般都采用『驼峰命名法aBbbCcc』
JS 的编写位置
- 编写到外部 js 文件中,然后通过 script 标签引入(推荐)
- 好处:可以复用多个页面
- 坏处:不能在编写修改,但是可以使用内部 script 标签
1
<script type="text/javascript" src="文件路径"></script>
- 编写到 script 标签内 — 写在页面底部
1
<script type="text/javascript"></script>
- 写在超链接的 href 属性中,这样当点击超链接时,会执行 js 代码
1
<a href="javascript:alert('点击弹窗');">弹窗</a>
- 编写到标签的 onclick 属性中,当我们点击按钮时,js 代码才会执行(不推荐)
1
<button onclick="alert("点击弹窗")"></button>
数据 | 内存 | 变量
数据
- 存储于内存中代表特定信息的, 本质就是 0101 二进制
- 具有可读和可传递的基本特性
- 万物(一切)皆数据, 函数也是数据
内存
- 内存条通电后产生的存储空间(临时的)
- 产生和死亡: 内存条(集成电路板)==>通电==>产生一定容量的存储空间==>存储各种数据==>断电==>内存全部消失
- 内存的空间是临时的, 而硬盘的空间是持久的
- 一块内存包含 2 个数据
- 内部存储的数据(一般数据/地址数据)
- 内存地址值数据
- 内存分类
- 栈: 全局变量, 局部变量 (空间较小)
- 堆: 对象 (空间较大)
变量
- 值可以变化的量, 由变量名与变量值组成
- 一个变量对应一块小内存, 变量名用来查找到内存, 变量值就是内存中保存的内容
- 声明变量
- 使用 var、const、 let 关键字声明
- var a;
- 为变量赋值: a = xxx;
- 声明和赋值同时进行:var a = xx;
三者之间的关系
- 内存是一个容器, 用来存储程序运行需要操作的数据
- 变量是内存的标识, 我们通过变量找到对应的内存, 进而操作(读/写)内存中的数据
问题: JS 引擎如何管理内存?
- 内存生命周期
- 分配需要的内存
- 使用分配到的内存
- 不需要时将其释放/归还
- 释放内存
- 为执行函数分配的栈空间内存: 函数执行完自动释放
- 存储对象的堆空间内存: 当内存没有引用指向时, 对象成为垃圾对象, 垃圾回收器后面就会回收释放此内存
var & let & const
es6 之前创建变量只有 var,let/const 是 es6 后出来的
- var 定义的变量,没有块的概念,可以跨块访问,不能跨函数
- let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问
- const 用来定义常量,使用时必须初始化(即赋值),只能块作用域里访问,且不能修改
- var 定义的变量可以先使用,后声明,存在变量提升,let 必须先声明后使用
- var 允许在相同作用域内重复声明同一个变量,let 和 const 不允许
- 在全局上下文中,基于 var 声明的变量会和全局对象(window)有映射,而 let 不会
- 会产生暂时性死区:检测一个未被声明的变量类型时,不会报错,会返回 undefined
- let、const 会把当前所在的大括号作为一个全新的块级上下文
数据存储
可以用来保存字面量,而且变量的值是可以任意改变的
- JS 中的变量都是保存到栈内存中的,
- 『基本数据类型的值』直接在『栈内存』中存储,值与值之间是『独立』存在,修改一个变量『不会影响』其他的变量
- 『对象』是保存到『堆内存』中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,
- 而『变量保存』的是『对象的内存地址』(对象的引用),如果两个变量『保存』的是『同一个』对象引用,当一个通过一个变量修改属性时,另一个也『会受到影响』
- 『比较』两个『基本数据类型的值』时,『就是比较值』。
- 『比较』两个『引用数据类型』时,它是『比较对象的内存地址』,如果两个对象是一样,但是地址不同,它也会返回 false
关于引用变量赋值问题
- 2 个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
- 2 个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象
问题: 在 js 调用函数时传递变量参数时, 是值传递还是引用传递
- 只有值传递, 没有引用传递, 传递的都是变量的值, 只是这个值可能是基本数据, 也可能是地址(引用)数据
- 如果后一种看成是引用传递, 那就值传递和引用传递都可以有
转义字符
1 | \":表示 " |
js 的数据类型
- 七种基本类型,一种引用类型
- Number、Boolean、String、undefined、null、Bigint(es2020 新增,范围比 Number 的大)、Symbol(ES6 新增)
- Object对象(包括普通Object、Function、Array、Date、RegExp、Math)
Number 数值
- 特殊的数字
Infinity 正无穷
-Infinity 负无穷
NaN 非法数字(Not A Number) - 进制:
- 如果需要表示『16进制』的数字,则需要『以0x开头』
- 如果需要表示『8进制』的数字,则需要『以0开头』
- 如果要要表示『2进制』的数字,则需要『以0b开头』,但是`不是所有的浏览器都支持』
- 示例:像”070”这种字符串,有些浏览器会当成 8 进制解析,有些会当成 10 进制解析
- 可以在parseInt()中传递一个『第二个参数』,来『指定数字的进制』
- 示例:
1
a = parseInt(a,10);
- 示例:
null 和 undefined
- undefined 代表没有赋值
- null 代表赋值了, 只是值为 null
- 由于 undefined 衍生自 null,所以 null == undefined 会返回 true。
- 但是 null === undefined 会返回 false。
- null 的使用
1
2var a = null // a 将指向一个对象, 但对象此时还没有确定
a = null // 让 a 指向不再使用的对象成为垃圾对象(垃圾回收机制 GC)
NaN
- 『与任何值都不相等,包括自身』
- 可以通过 isNaN()函数来判断一个值是否是 NaN
1
2
3function isNaN(x){
return x! = x
} - 机制:会先判断参数是不是 Number 类型,不是尝试转为 Number,之后再去判断是不是 NaN
Unicode 编码
- 『\u四位编码』:在字符串中使用的 Unicode 编码
- 『&#编码』:
- 在网页中使用的 Unicode 编码,这里的编码需要的是 10 进制
- 示例:
1
<h1 style="font-size: 200px;">☠</h1>
typeof — 类型检测方案
区分数据类型,并返回 number、 string、 boolean、undefined、object、function、symbol
- 对于『基本数据』类型,『除null都可以正确判断』
- 对于『引用类型,除function』外,都会『返回object』
- typeof 返回值是 string,即『双层typeof』返回的类型是 string
- typeof『未定义的变量』,返回”undefined”
- typeof(null) —> “object”
- typeof(NaN) —> “number”
- typeof(Infinity) —> “number”
instanceof — 类型检测方案
判断对象的原型链上是否存在构造函数的原型。只能判断引用类型。检查一个对象是否是一个类的实例
- 语法:A instanceof B,常用来判断 A 是否为 B 的实例,如果是,则返回 true,否则返回 false,
- 如果 B 函数的显式原型对象在 A 对象的原型链上, 返回 true, 否则返回 false
- arr instanceof Array ,可以『判断数组类型,但不推荐』,因为如果网页存在多个 iframe,便会存在多个 Array 构造函数,此时判断会存在问题
- 所有的对象都是 Object 的后代,所以任何对象和 Object 左 instanceof 检查时都会返回 true
- Number、Boolean、String 基本类型不能判断
Object.prototype.toString.call([value]) — 类型检测方案
可以精准判断数据类型,与 Array.isArray 结合使用可以判断数组和对象,此方式兼容性最好
1 | if (!Array.isArray) { |
类型转换
在 js 中,只有三种类型转换
转换为 Number 类型:Number()/parseFloat()/parseInt()
- 显式/强制:直接使用 Number()/parseFloat()/parseInt()
- 隐式:比较操作(<,>,<=,>=)、按位操作(|,&,^,~)、算术操作(+,-,*,/,%)、一元+-操作
- Number 类型使用 toString(整数)可以将数字转为指定的进制,默认 10 进制
- parseFloat(): 把一个字符串转换为一个浮点数
- parseInt(): 把一个字符串转换为一个整数,将一个字符串中的有效的整数内容取出来
1
2
3
4['1','2','3'].map(parseInt)相当于执行了以下三次过程:
parseInt('1', 0, ['1','2','3']): radix 为 0 时,默认取 10,最后返回 1
parseInt('2', 1, ['1','2','3']): radix 取值为 2~36,返回 NaN
parseInt('3', 2, ['1','2','3']): radix 取值为 2,二进制只包括 0,1,返回 NaN - 『对非 String使用 parseInt()或 parseFloat(),会先将其转换为 String 然后在操作』
- 字符串 –> 数字
类型 结果 undefined NaN null +0 Boolean true:1,false:+0 Number 返回对应的值 String 空字符串返回 0,出现任何一个非有效数字返回 NaN,其他的直接转换为对应的值 Object 先执行 ToPrimitive 方法,再执行 Number 类型转换
转换为 String 类型:String()/toString()
- 显式/强制:直接使用 String()/toString()
- 隐式:+ 运算符有一侧操作数为 string 类型
- null 或 undefined 调用 String()方法,返回字符串格式是它们本身
- 『null 或 undefined 使用 toString()会报错』
转化为 Boolean 类型:Boolean()
- 显式/强制:直接使用 Boolean()
- 隐式:通过 !! 或 !
- 『除 0,-0,null,NaN,undefined,或空字符串(“”)为 false 外』,其余全为 true
==隐式转换规则
- 类型相同的比较
- 类型是 undefined 或 null, 返回 true
1
2null == null // true
undefined == undefined // true - 如果一个是+0,另一个是-0,返回 true
1
+0 == -0 //true
- 如果类型是对象,二者引用同一个对象,返回 true,否则返回 false
1
{} == {} // false
- 类型是 undefined 或 null, 返回 true
- null 和 undefined 比较,仅当它们之间比较返回 true
1
null == undefined // true
- NaN 比较,NaN 与任何之后比较都返回 false,包括自己
1
NaN == NaN // false
- 字符串与数字比较,先将字符串转换为数字在比较
- 布尔值与非布尔值比较:先将布尔值转换为数字在比较
- 对象与原始类型比较,将对象通过ToPrimitive转换为原始类型对应的类型在比较
1
{} == 1 // false
- ! 的优先级高,如遇到[] == ![], 先进行非运算,
- 又因为任何对象转换成布尔值都得到true,先转布尔在非运算
- 即 ![] == false, 根据==隐式转换规则,对象与原始类型转换,两边先转为数字在比较,
- 即 ![] ==> false ==> 0
- [] 经过 ToPrimitive 方法 转换为’ ‘,
- 所以[] == ![] 变成 ‘ ‘ == 0
- 然后根据字符串与数字比较,先将字符串转为数字在比较,’’ 的布尔值为 0
- 即 0 == 0 ,为 true
- 其他的
1
2
3
4
5
6
7
8{} == !{} // !{}->false->0 {}->"[object Object]"->NaN
{} == true
[] == [] // false 比较引用地址
{} == [] // 报错
{} + {} // "[object Object][object Object]"
{} + [] // 0 ,{}被当做一个块,并非{}转NaN,相当于执行 ({},+[]),返回值为小括号最后面的表达式的返回值
{q:1} + [] // 0
var a = {q:1};a + [] // "[object Object]"
toString 和 valueOf 方法有什么区别
都属于 Object 对象,是为了解决 JavaScript 值运算与显示的问题。
- js 对象的键必须是字符串
- toString(): 返回当前对象的字符串形式
- valueOf(): 返回该对象的原始值
类型 valueOf toString Array[1,2,3] 数组本身[1,2,3] “1,2,3” Object 对象本身 [object Object] Boolean 类型 Boolean 值 “true” 或”false” Function 函数本身 function fnName(){code} Number 数值 数值的字符串形式 Date 毫米格式时间戳 GMT 格式时间字符串 - 隐式转换会自动调用 toString 和 valueOf
- 强制转化为字符串类型时,优先调用 toString
- 强制转化为数值类型时,优先调用 valueOf
- 使用运算符操作符情况下,valueOf 优先级高于 toString
- 对象类型转换为原始值回调用内置的[ToPrimitive]函数
- ToPrimitive 方法接受两个参数,输入的值 value,期望转换的类型 Type
- 如果未传入 PreferredType 参数,让 hint 等于’default’,后面会将 hint 修改为”number”
- 如果 value 是 基本类型,直接返回对应的类型的值
- 否则,调用 valueOf 方法,如果得到原始值,则返回
- 否则,调用 toString 方法,如果得到原始值,则返回
- 否则,报错
- OrdinaryToPrimitive(input,hint)
- 如果 hint 是 ‘string’,那么就将 methodNames 设置为 toString、valueOf
- 如果 hint 是 ‘number’,那么就将 methodNames 设置为 valueOf、toString
- ToPrimitive 方法接受两个参数,输入的值 value,期望转换的类型 Type
- 设变量a={x:100},obj={},然后设obj[a] = 100 ,则此时obj[a]为多少?
- 解:由于js对象的键必须为字符串,所以先将变量a转换为字符串,即a = “{x:100}”
- 然后因为强制转化为字符串类型时,优先调用toString,通过上方表格可知,obj[a]为[object Object]
1
2- {} + 1 // js引擎回解析成1个代码块和1个1,最终结果为1
- 1 + {}// 先将{}调用ToPrimitive转为'[object Object]',然后跟1拼串,最终结果1[object Object]
运算符
逻辑运算符
- !!:将变量『转为布尔值』
- !:取反,对非 boolean 类型的元素,则会将其『转boolean』类型『再取反』
- &&:只有两个值都为 true 时,才会返回 true,如果第一个值为 false,则不会看第二个值
- 对于非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,并且返回原值
- 如果『第一个值为true』,则必然『返回第二个值』
- 如果『第一个值为false』,则直接『返回第一个值』
- 如果两个值『都为true』,则『返回后边的』
- ||:两个值中只要有一个 true,就返回 true,如果第一个值为 true,则不会检查第二个值
- 对于非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,并且返回原值
- 如果『第一个值为true』,则直接『返回第一个值』
- 如果『第一个值为false』,则『返回第二个值』
- 如果『两个值中有false』,则『返回靠前的false』
赋值运算符
- 『=』:赋值
- 『+=』:a += 5 相当于 a = a+5
- 『-=』:a -= 5 相当于 a = a-5
- 『*=』:a = 5 相当于 a = a5
- 『/=』:a /= 5 相当于 a = a/5
- 『%=』:a %= 5 相当于 a = a%5
- 『
n++,n--
』:先自增,自减,并且赋值时,把自增、自减前的值的赋值,即得到的结果是『变量的原值』 - 『
++n,--n
』:先自增,自减,并且把变化后的值赋值给变量,即得到的结果是『变量的新值』 - 示例:
1
2
3
4const n1 = 10
const n2 = 10
var n = n1++ //n1 = 11 n1++ = 10 n = 10
var m = ++n2 //n2 = 11 ++n1 = 11 n = 11
相等运算符
- 『==』:等于,值相等返回 true,如果值的类型不同,则会自动进行类型转换,将其转换为相同的类型,然后在比较
- 『!=』:不等于,两个值是否不相等,不相等返回 true,也会对变量进行自动的类型转换,然后在比较
- 『===』:绝对等于,类型也相等,不会做自动的类型转换
- 『!==』:不全等,不会做自动的类型转换,如果两个值的类型不同,直接返回 true
算数运算符
- 『
+、-、*、/
』:加减乘除,任何值做-、/、*运算时都会自动转换为 Number - 『%』:算数运算符,取模运算(取余数)
关系运算符
- 『>、>=、<、<=』
- 对于『非数值』进行比较时,会将其『转为数字在比较』
- 如果符号两侧的值『都是字符串』时,会分别『一位一位比较』字符串中字符的『Unicode编码』
- 注:在比较两个字符串型的数字时,一定要转型
三元运算符(条件运算符)
- 语法:
1
条件表达式 ? 语句1 : 语句2
- 执行的流程:
- 条件运算符在执行时,首先对条件表达式进行求值,
- 为 true,则执行语句 1,并返回执行结果,为 false,则执行语句 2,并返回执行结果
- 如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值然后在运算
一元运算符
- 『+、-』:『正负号,只需要一个操作数』
- 对于非 Number 类型的值,会先转换为 Number,然后在运算
- 『+ 变量转为Number』类型
- 『任何值和字符串相加都会转换为字符串』,叫做拼串操作
流程控制语句
条件判断语句
- 语句一:
1
2
3if (条件表达式) {
为 true 语句...
} - 语句二
1
2
3
4
5if (条件表达式) {
为 true 语句...
} else {
为 false 语句...
} - 语句三
1
2
3
4
5
6
7if (条件表达式) {
语句...
} else if (条件表达式) {
语句...
} else {
语句...
}
条件分支语句 switch
- 语法:
1
2
3
4
5
6
7switch(条件表达式){
case 表达式:
语句...
break;
default:
break;
} - 执行流程
- 依次将 case 后的表达式的值和 switch 后的条件表达式的值进行全等比较,如果为 true,则从当前 case 处开始执行代码。
- 为 false,则继续向下比较,知道所有结果都为 false,只执行 default 后的语句
循环语句
for循环
- 语法:
1
2
3for(① 初始化表达式;② 条件表达式;④ 更新表达式){
语句...
} - 执行流程:
- 执行初始化表达式,初始化变量(初始化表达式只会执行一次)
- 执行条件表达式,判断是否执行循环。为 true,则执行循环,为 false,终止循环
- 执行更新表达式,更新表达式执行完毕继续重复上一步
- 注:for 循环中的三个部分都可以省略,也可以写在外部,for(;;){}死循环
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24打印2-1000质数
for(var i=2 ; i<=1000; i++){
var flag = true;
for(var j=2 ; j<=Math.sqrt(i) ; j++){
if(i%j == 0){
//如果进入判断则证明i不是质数,修改flag值为false
flag = false;
break;
}
}
//如果是质数,则打印i的值
if(flag){
document.write(i+' ')
}
}
打印九九乘法表
//创建外层循环,用来控制乘法表的高度
for(var i=1 ; i<=9 ; i++ ){
// 创建一个内层循环来控制图形的宽度
for(var j=1 ; j<=i ; j++){
document.write("<span>"+j+"*"+i+"="+i*j+"</span>");
}
document.write("<br />"); //输出一个换行
}
- 语法:
while循环
- 语法:
1
2
3while(条件表达式){
语句...
} - 执行流程
- 先对条件表达式进行求值判断,
- 如果值为 true,则执行循环体,
- 循环体执行完毕以后,继续对表达式进行判断,以此类推
- 如果值为 false,则终止循环
- 语法:
do…while循环
- 语法:
1
2
3do{
语句...
} while(条件表达式) - 执行流程:
- 先执行循环体,
- 循环体执行完毕以后,在对 while 后的条件表达式进行判断,
- 如果结果为 true,则继续执行循环体,执行完毕继续判断以此类推, 如果结果为 false,则终止循环
- 语法:
do…while循环和while循环区别:
- while 是先判断后执行,
- 而 do…while 会先执行后判断,
- do…while 可以保证循环体至少执行一次,而 while 不能
将条件表达式写死为 true 的循环,叫做死循环,可以使用 break,来终止循环
函数 function
函数也是一个对象,函数中可以封装一些功能,在需要时可以执行这些功能
创建一个函数对象
- 方法一:使用构造函数(少用)
- 语法:
1
const fun = new Function(语句...);
- 语法:
- 方法二:使用函数声明
- 语法:
1
2
3function 函数名([形参 1,形参 2...形参 N]){
语句...
}
- 语法:
- 方法三:使用函数表达式
- 语法:
1
2
3const 函数名 = function([形参 1,形参 2...形参 N]){
语句...
}
- 语法:
调用方法:
- 函数对象([实参 1,实参 2])
- new 函数对象()
- 对象.函数对象()
- 函数对象.call/apply(obj)
形参与实参:
- 实参:
- 在调用函数时,可以在()中指定,实参将会赋值给函数中对应的形参
- 调用函数时解析器不会检查实参的类型和数量,可以传递任意数据类型的值
- 多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是 undefined
- 形参:
- 定义函数时,可以在()中定义一个或多个形参,形参之间使用,隔开
- 定义形参就相当于在函数内声明了对应的变量但是并不赋值,形参会在调用时才赋值。
构造函数:
- 创建一个构造函数:与普通函数一样,但首字母要大写
- 调用方式:使用 new 关键字来调用
- 构造函数是专门用来创建对象的函数
- 一个构造函数我们也可以称为一个类
- 通过一个构造函数创建的对象,我们称该对象时这个构造函数的实例
- 通过同一个构造函数创建的对象,我们称为一类对象
- 构造函数就是一个普通的函数,只是他的调用方式不同,
- 如果直接调用,它就是一个普通函数
- 如果使用 new 来调用,则它就是一个构造函数
- 执行流程:
- 立刻创建一个新的对象
- 将新建的对象设置为函数中 this,在构造函数中可以使用 this 来引用新建的对象
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
作用域
- 定义:变量与函数的可访问范围,由当前环境与上层环境的一系列变量对象组成。
全局作用域:
- 直接编写在 script 标签中的 JS 代码,都在全局作用域
- 全局作用域在页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象 window,
- 创建的变量都会作为 window 对象的属性保存
- 创建的函数都会作为 window 对象的方法保存
- 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
- 在函数作用域中也可以访问到全局作用域的变量。
- 尽量不要在全局中创建变量
函数作用域:函数执行时创建的作用域
- 函数作用域在函数执行时创建,在函数执行结束时销毁。
- 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
- 在函数作用域中可以访问到全局作用域的变量,反过来不行
- 在函数中要访问全局变量可以使用 window 对象
- 作用:隔离变量,不同作用域同名变量不会有冲突。
作用域链
- 定义:调用某个函数或属性时,先在当前作用域寻找,如果找不到的情况下去父级寻找,如果父级找不到继续向上级寻找,直到找到全局作用域为止,如果全局作用域中依然没有找到,则会报错 ReferenceError,这就是链式查找的过程
声明提前
变量的声明提前
- 使用 var 关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值)
- 但是如果声明变量时不使用 var 关键字,则变量不会被声明提前
- 在函数作用域中,也具有以上特性
- 在函数作用域中,不使用关键字声明的变量都会成为全局变量
函数的声明提前
- 在全局作用域中, 使用『函数声明形式』创建的函数 function 函数(){},会在所有的代码执行之前就被创建,所以『可以在函数声明前来调用函数』
- 在全局作用域中,使用『函数表达式』创建的函数 var fun = function(){},『不会』被声明提前,所以『不能』在声明前调用
- 在函数作用域中,使用函数声明创建的函数,会在所有的函数中的代码执行之前就被创建好了。
函数的方法
- call() 和 apply()
- 这两个方法都是函数对象的方法,需要通过函数对象来调用
- 通过两个方法可以直接调用函数,并且可以通过『第一个实参(对象)来指定函数执行中 this』
- call()方法可以将实参在对象之后『依次传递』
- apply()方法需要将实参『封装』到一个『数组』中『统一传递』
- arguments(封装实参的对象)
- arguments 和 this 类似,都是函数中的隐含的参数
- arguments 是一个类数组元素,它可以通过索引来操作数据,获取长度
- 在调用函数时,我们所传递的实参都会在 arguments 中保存
- arguments.length 可以用来获取实参的长度
- arguments 中有一个属性 callee 表示当前执行的函数对象
1
2
3function fun(a,b) {
console.log(arguments.callee == fun) // true
} - 即使不定义形参,也可以通过 arguments 来使用实参
- arguments[0] 表示第一个实参
- arguments[1] 表示第二个实参
this(函数的上下文对象)
- 解析器在每次调用函数都会向函数内部传递进一个隐含的参数,这个隐含的参数就是 this
- this 指向的是一个对象,这个对象我们称为函数执行的上下文对象
- 使用 this 来引用上下文对象,根据函数的调用形式不同,this 的值也不同
- 当『以函数的形式调用时,this是window』
1
xxx() // this 指向 window
- 当『以方法的形式调用时,谁调用方法this就是谁』
1
obj.xxx() // this 指向 obj
- 当『以构造函数的形式调用时,this就是新创建的那个对象』
- 通过 call/apply 指定谁调用: xxx.call(obj),指向 obj
- 在全局作用域中 this 代表 window
- 示例
1
2
3
4
5
6
7
8
9
10function Person(name, age, gender) {
// 定义了形参,创建构造函数
this.name = name
this.age = age
this.gender = gender
this.sayName = function () {
alert(this.name)
}
}
var per = new Person('孙悟空', 18, '男') // this执行per,并传了实参
(IIFE)立即执行函数:
- 全称: Immediately-Invoked Function Expression 立即调用函数表达式
- 别名: 匿名函数自调用
- 定义:函数定义完,立即被调用,往往只会执行一次
- 语法:
1
(function(){ alert("我是一个匿名函数~~~"); })();
- 作用
- 隐藏内部实现
- 不污染外部命名空间
回调函数
- 定义:定义了该函数,没有直接执行,需要按下或者在一定时候才会执行的函数
- 常见的回调函数
- DOM 事件函数
- 定时器函数
- ajax 回调函数
- 生命周期回调函数
1 | // DOM事件函数 |
对象
对象的分类:
- 内建对象: 由 ES 标准中定义的对象,在任何的 ES 的实现中都可以使用
- 比如:Math String Number Boolean Function Object….
- 宿主对象: 由 JS 的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
- 比如 BOM DOM
- 自定义对象: 由开发人员自己创建的对象
in 运算符
- 定义:检查一个对象中是否含有指定的属性,有则返回 true,没有则返回 false
- 语法:”属性名” in 对象
使用 new 关键字创建一个对象
- 语法:使用 new 关键字调用的函数,是构造函数 constructor,
1
var obj = new Object();
- 向对象中『添加』属性:
1
对象.属性名 = 属性值(可以是任意类型,也可以是个函数)
- 特殊属性名的添加方式:
1
对象["属性名"] = 属性值
- 特殊属性名的添加方式:
- 『读取』对象中的属性:
1
对象.属性名
- 如果读取对象中没有的属性,返回 undefined
- 特殊属性名的读取方式:
1
对象["属性名"]
- 『修改』对象的属性值:
1
对象.属性名 = 新值
- 『删除』对象的属性:
1
delete 对象.属性名
- 调用方法:
1
对象.函数对象(); // 如果一个函数作为一个对象的属性值保存,那么我们称这个函数是这个对象的方法
使用对象字面量来创建一个对象
- 语法:
1
2const 对象 = {属性名:属性值,属性名:属性值....};
// 可以在创建对象时,直接指定对象中的属性,也可以为空 - 属性名可以加引号也可以不加,如果要使用一些特殊的名字,则必须加引号
- 属性名和属性值是一组一组的名值对结构,名和值之间使用:连接,多个名值对之间使用,隔开
- 示例
1
2
3
4
5
6const obj = {
name: '孙悟空',
age: 18,
gender: '男',
address: '花果山',
}
使用工厂方法传建对象
- 优点:可以批量创建
- 缺点:创建的对象都是 Object 这个类型,无法区分出多种不同类型的对象
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function createPerson(name, age, gender) {
//创建一个新的对象
var obj = new Object()
//向对象中添加属性
obj.name = name
obj.age = age
obj.gender = gender
obj.sayName = function () {
alert(this.name)
}
//将新的对象返回
return obj
}
var obj2 = createPerson('猪八戒', 28, '男')
var obj3 = createPerson('蜘蛛精', 18, '女')
枚举对象中的属性使用 for … in 语句
- 语法:
1
for(var 变量 in 对象){}
- for…in 语句 对象中有几个属性,循环体就会执行几次
- 每次执行时,会将对象中的一个属性的名字赋值给变量
- 示例
1
2
3
4for (var n in obj) {
console.log('属性名:' + n)
console.log('属性值:' + obj[n])
}
break | continue | return
break 关键字 — 会立即终止离他最近的那个循环语句
- 可以用来退出 switch 或循环语句,
- 不能在 if 语句中使用 break 和 continue
label — 标识当前的循环
- 语法:
1
label:循环语句
- 使用 break 语句时,可以在 break 后跟着一个 label,这样 break 将会结束指定的循环
- 示例
1
2
3
4
5
6
7
8// 给循环标识了一个label
outer: for (var i = 0; i < 5; i++) {
console.log('@外层循环' + i)
for (var j = 0; j < 5; j++) {
break outer // 退出时,结束outer这个循环
console.log('内层循环:' + j)
}
}
continue 关键字 — 用来跳过当次循环
- 同样 continue 也是默认只会对离他最近的循环循环起作用
return — 可以结束整个函数
- 语法:
1
return 值(任意类型的值,不写相当于 undefined)
- 可以使用 return 来设置函数的返回值,值将会作为函数的执行结果返回
- 在 return 后的语句都不会执行