一.javascript的组成
1.ECMAScript:定义了javascript的语法规范,描述了语言的基本语法和数据类型
2.BOM(Brower Object Model):浏览器对象模型,有一套成熟的可以操作浏览器的方法,通过BOM可以操作浏览器,比如:弹出对话框,跳转页面,获取分辨率等
3.DOM(Document Object Model):文档对象模型,有一套成熟的可以操作页面元素的方法,通过DOM可以操作页面中的元素,比如:增加一个div,减少一个div,给div添加样式,换个位置等。
4.总结:javascript就是通过固定的语法,去操作浏览器和标签结构来实现网页上的各种效果。
二.javascript的书写位置
1.行内:写在标签里面的js代码,需要依赖用户的行为来触发这个代码的(不推荐)。
2.内嵌:写在html的script标签中,会在页面打开的直接执行代码。在页面任意位置添加一对scirpt标签,js代码就写在这对标签里面,一个页面可以有多个scirpt标签。
3.外链:只要新建一个.js后缀的文件,在里面就可以写js代码,把写好的js代码通过html文件的script标签的src引入就可以。在页面打开的时候直接触发(推荐)
三.javascript的注释
1.单行://
一般是用来描述下一行代码的作用的。
windows快捷键(ctrl+/)
mac的快捷键:command+/)
2.多行: / ** /
一般用来写一大段话,获取注释一段代码.
可以直接写 / ** /,然后在两个*之间写注释
window的快捷键: shift+alt+a(默认)
mac的快捷键:shift+option+a
四.变量
1.变量就是在程序中保存数据的一个容器,也可以说是程序内存中申请的一块用来存放数据的空间。
2.声明一个变量并赋值,我们称之为变量的初始化。
3.在js中不声明,可以直接使用变量。 eg:qq=100;(不提倡)
4.name在某些浏览器有特殊含义,建议不要用其作为变量名。
定义变量: var 变量名(变量名和var之间必须有空格)
给变量赋值: 变量名 = 数据
定义变量并赋值:var 变量名 = 数据
二.变量名的命名规则
1.只能是字母,数字,英文下划线和美元符号
2.严格区分大小写
3.不能以数字开头,不能使用中文汉字命名
4.不能有空格
5.不能是关键字或者保留字(eg:var)
6.变量名的命名规范
7.尽量语义化(变量名尽量有意义)
8.尽量驼峰命名(由多个单词组成的时候,从第二个单词开始首字母大写 eg:myFriendName)
注意:1.一个变量里面只能存储一个值
2 .当再次给一个变量赋值的时候,前面一次的就没有了
3 .变量名区分大小写(js区分大小写)
4 .js代码每一句用分号结尾或者换行结束
五.数据类型:指的是我们存储在内存中的数据的类型。分为两大类:基本数据类型和复杂数据类型
一.基本数据类型
1.数值类型(number)
一切数字都是数值类型
一个特殊的数字,非数字:NaN
js中数值的最大值:Number.Max_value
js中数值的最小值:Number.Min_value
infinity 无穷大
-infinity 无穷小
2.字符串类型(string)
被引号包裹的数据(可以是单引号也可以是双引号,要成对就可以)
字符串引号嵌套(外双内单,内双外单)
3.布尔类型(boolean)
只有两个:true 或 false
4.null类型(null)
有一个,就是null,表示空的意思
5.undefined类型(undefined)
只有一个,就是undefined,表示没值的意思
二.复杂数据类型 - 下周讲
1.数组
2.对象
3.....
六.判断数据类型:使用typeof 关键字来进行判断
语法:typeof(变量)
语法:typeof 变量
结果:是一个表示数据类型的字符串('number'/'string'/'boolean'/'undefined'/'null')
注意:只是定义了,没有赋值,数据类型为undefined。(eg:var a;)
七.其他数据类型转数值类型
一.比如:数值类型转字符串,字符串转布尔值,布尔值转数字等,其他数据类型转数值类型 - 我们自己调用方法显式转换。
1.Number(数据)
可以把一个变量强制转换成数值类型,返回数据转换成的具体的数字
可以转换小数
可以转换布尔值
可以转换null
遇到不可转换的都返回NaN
当==两边进行比大小时,==两边默认转化成Number进行比较。
2.parseInt(数据) - 取整(可以去除单位)
可以把一个变量强制转换成数值类型,返回数据转换成的具体的数字
从第一位开始检查,是数字就转换,直到一个不是数字的内容
开头不是数字,那么直接返回NaN
不认识小数点,只能保留整数
3.parseFloat(数据)(可以去除单位)
可以把一个变量强制转换成数值类型,返回数据转换成的具体的数字
从第一位开始检查,是数字就转换,直到一个不是数字的内容
开头不是数字,那么直接返回NaN
认识一次小数点
二.其他数据类型转数值类型 - 隐蔽的浏览器自动遇到一些特殊情况给我们转换
4.除了加法以外的数学运算符
加法以外的数学运算要求两边都是数字
浏览器就自动给我们吧运算符两边的转数字,再参与运算
浏览器内部会调用Number帮我们偷偷的转
但加法没有这个能力
八.其他数据类型转字符串类型
一.其他数据类型转字符串类型 - 我自己调用方法来获取转换的结果
1.变量.toString()
就可以把变量转换成字符串类型
有一些数据类型不能使用这个方法,比如 undefined和null,会报错(因为他们后面不能接 .的)
2.String(变量)
所有数据类型都可以转换成字符串
二.其他数据类型转字符串类型 - 浏览器根据一些特殊情况转换字符串
3.使用加法运算
在js里面,+ 有两个含义
如果 + 两边都是数值类型就是表示做加法运算
如果 + 两边有任意一边是字符串,就会把两边都自动转换字符串,然后进行字符串拼接
九.其他数据类型转布尔值
Boolean(变量)
除了0,null,"",undefined,NaN转换结果是false,其他的都是true
拓展:Boolean(Nan==Nan) 结果为false。
var $myName = true(正确),数据类型为boolean
十.运算符:在代码库里面进行运算的时候使用的符号,不光只是数学运算符,我们的js里面的运算符有很多种
一.数学运算符:+,-,*,/,%
1.+
只有符号两边都是数值的时候才会进行加法运算
只要符号任意一边是字符串类型,就是进行字符串拼接
2.-
会执行减法运算
js会自动把符号两边转数值类型做减法
3.*
会执行乘法运算
js会自动把符号两边转数值类型做乘法
4./
会执行除法运算
js会自动把符号两边转数值类型做除法
5.%
会执行求余数运算
js会自动把符号两边转数值类型求余数
二.赋值运算符
1.=
就是把 = 右边的数据赋值给等号左边的变量名
var num = 100;就是把100赋值给num变量,那么num变量里面的值就是100
2.+=
比如: a += 10 等价于: a = a+10
3.-=
比如: a -= 10 等价于: a = a-10
4.*=
比如: a *= 10 等价于: a = a*10
5./=
比如: a /= 10 等价于: a = a/10
6.%=
比如: a %= 10 等价于: a = a%10
三.比较运算符
1.==
比较符号两边的值“是否“相等,不管数据类型,只比较值
eg:1=='1' 两个的值是一样的,所以得到true
2.===
比较符号两边的值和数据类型”是否“都相等
eg:1==='1' 两个的值虽然一样,但是数据类型不同,所以得到false
3.!=
比较符号两边的值是否不等
eg:1!='1'因为两边的值是相等的,所以比较他们不相等的时候得到false
4.!==
比较符号两边的值和类型是否不等
eg:1!=='1' 因为两边的数据类似确实不一样,所以得到true
5.>=
比较左边的值是否大于或等于右边的值
eg:1>=1 true; eg:1>=0 true; eg:1>=2 false
6.<=
比较左边的值是否小于等于右边的值
eg:1<=1 true; eg:1<=0 false; eg:1<=2 true
7.>
比较左边的值是否大于右边的值
eg:1>1 false; eg: 1>0 true; eg:1>2 false
8.<
比较左边的值是否小于右边的值
eg:1<1 false; eg:1<0 false; eg: 1<2 true
四.逻辑运算符
1.&&
进行且运算
符号左边必须位true,并且右边必须也是true,结果才是true
只要有一边不是true,那么就会返回false
2.||
进行或运算
符号的左边为true或者右边为true,都会返回true
只有两边都是false的时候才返回false
3.!
进行取反运算
本身是true的,返回false
本身是false的,返回true
五.自增和自减运算符
1.自增运算符(一元运算符:运算符一边有数据)
语法:++
表示自增运算
分成两种,++前置(++在数据前面),++后置(++在数据后面)
++前置,会把数据先+1,然后再返回
++后置,会先返回数据,再+1
2.自减运算符(一元运算符:运算符一边有数据)
语法:--
表示自减运算
分成两种,--前置(--在数据前面),--后置(--在数据后面)
--前置,会把数据先-1,然后再返回
--后置,会先返回数据,再-1
六.前置和后置的区别
区别只影响当前行
后置就是先返回num的值,再加1,给num赋值
前置就是先+1,赋值给num,再返回num的值
在下一行就不受影响了
注意:
1. js并不擅长做小数运算,建议做整数运算。
2. .toFixed(n)n为保留几位小数,可以实现四舍五入。
拓展:页面上获取到的所有值都是字符串。
prompt()为输入框 —浏览器弹出输入框,用户可输入;
“prompt 输入的,都是字符串型的数据。要变成数字型,需要自己转换”
eg:var name = prompt(’请输入你的名字‘) 即把输入框的值保存起来。
我们的js代码都是顺序执行的(从上到下),逻辑分支就是根据我们设定好的条件来决定要不要执行某些代码
一.if分支语句:通过一个if语句来决定代码执行与否
if(条件){
要执行的代码// 条件为true执行添加后面{}里面的代码
}
二.if … else 语句:通过if条件来判断,执行哪一个{}里面的代码
特点:两个{}里面的代码一定有一个会执行,如果{}里面只有一行代码可以省略{}
if(条件){
要执行的代码// 条件为true执行条件后面{}里面的代码
}else{
要执行的代码// 条件为false执行else后面{}里面的代码
}
三.if…else if …语句:可以通过if和else if来设置多个条件进行判断
特点:会从头开始依次判断条件,如果条件1为true,就不进行后面的判断了,只有前面的条件不为true,才进行后面的条件判断。多个{},只会有一个被执行,一旦有一个条件为true了,后面的就不再判断了
if(条件1){
// 条件1为true执行条件1后面{}里面的代码
}else if(条件2){
// 条件2为true执行条件2后面{}里面的代码
}
四.if…else if …else 语句
特点: 和之前的if else if … 基本一致,只不过是在所有条件都不满足的时候,执行最后else后面的{}里面的代码。如果前面有一个条件满足了,那么后面的就都不会执行了。
如果条件1为true,就不进行后面的判断了
只有前面的条件不为true,才进行后面的条件判断
if(条件1){
// 条件1为true执行条件1后面{}里面的代码
}else if(条件2){
// 条件2为true执行条件2后面{}里面的代码
}else{
// 前面所有条件都不满足的时候执行else后面{}里的代码
}
五.switch分支语句:也是条件判断语句的一种,主要用于判断一个变量
特点:判断某一个变量全等于某一个值的时候使用(break是一个关键字)
注意:case后面的值必须是确定的,不能有范围,但是可以在:下写if条件判断。
switch(变量){
case 情况1:
// 变量全等于情况1的时候执行此次代码
break; // 后面的代码就不继续执行了
case 情况2:
// 变量全等于情况2的时候执行此次代码
break; // 后面的代码就不继续执行了
default:
// 前面的情况都不符合就执行此
}
注意:当不写break时,叫做switch的穿透,即无法跳出当前case,而后面的代码就不用进行条件判断了,依次执行,一直执行到下一个break结束或者执行到最后一行,运行完代码。
六.三元运算符:就是用两个符号组成一个运算符(类似if else语句)
语法:条件?条件true返回的值:条件false返回的值
七.扩展:逻辑运算符
1.&& 且运算
进行且运算
只有左边和右边都是true的时候结果才是true
运算的时候,只有左边有true才会进行右边的运算
运算的时候,如果左边的是false,就不进行右边的运算
且运算的返回值是最后运算的那个
注意:只有0,'',null,undefined,NaN会把看成false,其他可以看成true。
2.|| 或运算
进行或运算
只要左边和右边有一个是true结果就是true
运算的时候,如果左边是true,就不看右边了
运算的时候,如果左边是false,就看右边了
且运算的返回值是最后运算的那个
3.! 取反操作
进行取反操作
结果一定是布尔值
如果是true取反就是false
如果是false取反就是true
一.介绍
1.顺序结构:代码从上到下依次执行
2.分支结构:代码根据给定的条件选择执行其中的代码
3.循环结构: 就是根据某些给出的条件,重复执行同一段代码
4.循环必须有固定的内容组成:1.初始条件2.条件的判断3.自身的改变4.要执行的代码
二.while循环结构
1.中文是当的意思,就是当条件满足时就执行代码,一旦不满足了,就不执行了
2.语法: while(条件){条件为true要执行的代码}
3.当条件满足的时候,就执行{}里面的代码,执行完继续进行条件判断,条件满足就执行{}里面的代码,直到条件为false的时候,while循环就完了
4.如果没有自身的改变,那么就会一直循环下去,叫做死循环
eg:1.初始化条件 var num = 1;
2.条件判断 while(num<11){}
3.自身的改变 num++;
4.要执行的代码 console.log('这是你工作的第'+num+'年,给你加薪5%')。
三.do …while循环
1.类似while循环
2.while循环先进行条件判断,条件为true才执行{}里面的代码,为false就不执行{}里面的代码
3.但是do while循环是,先不管条件,先执行一次,然后再进行条件判断
4.语法: do{要执行的代码,至少会执行一次}while(条件)
eg: 案例:你来公司的工资是10K,每工作1年,年底涨薪5%,问第10年年底,你的工资是多少?
1.思路
2.初始条件 year = 1
3.判断条件 year <= 10
4.自身的改变 year++
5.要执行的代码:
-需要在循环外定义一个变量记录初始工资: salary = 10K
-每次执行代码重新赋值 salary = salary*(1+0.05)
var salary = 10000;
/* 1 初始条件 */
var year = 1
do {
/* 2 要执行的代码 */
salary = salary *1.05;
/* 3 自身的改变 */
year++
}while(year<11)
五.for循环
1.for循环:和while和do while都不太一样的循环结构,道理是一样,都要那四个条件
2.语法: for(1初始条件;2判断条件;4自身的改变){3要执行的代码}
3.代码执行过程:
-先初始化条件
-然后进行循环的条件判断
-如果符合条件,执行{}里面的代码
-执行自身的改变的代码
-在进行循环条件判断
-如果符合条件,执行{}里面的代码
-执行自身的改变的代码
-在进行循环条件判断,以此类推,直到判断条件为false,for循环就结束了
-总结顺序: 1-234-234-234-...-2为false就结束了
4.注意:for循环中两个分号不能少。
六.拓展:逗号操作符
逗号操作符:对它的每个操作数求值(从左到右),并返回最后一个操作数的值。
在console.log()的括号中有逗号,表示输出多个。
七.break关键字
break 终止循环
1.在循环没有进行完毕的时候,因为我设置的条件为true,提前终止循环
2.比如:我要吃5个包子,吃到第三个的时候,吃不下了,我就停止了吃包子这个事情。
3.要终止循环,就可以使用break关键字
八.continue关键字
continue 结束本次循环(必须放在循环中)
1.在循环中,把循环的本次跳过去,继续执行后续的循环(即本次循环后面的代码都不执行,放在代码前面)
2.比如:我要吃5个包子,吃到第三个的时候,第三个掉地上了,跳过第三个不吃了,继续吃第四个和第五个
解释:我们代码里面所说的函数和我们上学的时候学习的什么三角函数、二次函数之类的不是一个东西
一.函数的概念
1.对于 js 来说,函数就是把任意一段代码放在一个盒子里面
2.在我想要让这段代码执行的时候,直接执行这个盒子里面的代码就行
3.先看一段代码
// 这个是我们以前写的一段代码
for (var i = 0; i < 10; i++) {
console.log(i)
}
// 函数,这个 {} 就是那个 “盒子”
function fn() {
// 这个函数我们以前写的代码
for (var i = 0; i < 10; i++) {
console.log(i)
}
}
二.函数的两个阶段(重点)
按照我们刚才的说法,两个阶段:
1.放在盒子里面
2.让盒子里面的代码执行
三.函数定义阶段(函数在定义阶段不会执行)
1.定义阶段就是我们把代码放在盒子里面
2.我们就要学习怎么放进去,也就是书写一个函数
3.我们有两种定义方式:
声明式和赋值式
四.声明式
使用 function
这个关键字来声明一个函数
语法:
function fn() {
// 一段代码
}
// function: 声明函数的关键字,表示接下来是一个函数了
// fn: 函数的名字,我们自己定义的(遵循变量名的命名规则和命名规范)
// (): 必须写,是用来放参数的位置(一会我们再聊)
// {}: 就是我们用来放一段代码的位置(也就是我们刚才说的 “盒子”)
五.赋值式
其实就是和我们使用 var
关键字是一个道理了
首先使用 var
定义一个变量,把一个函数当作值直接赋值给这个变量就可以了
语法:
var fn = function () {
// 一段代码
}
// 不需要在 function 后面书写函数的名字了,因为在前面已经有了
六.函数调用阶段
1.就是让盒子里面的代码执行一下
2.让函数执行
3.两种定义函数的方式不同,但是调用函数的方式都以一样的
七.调用一个函数
函数调用就是直接写 函数名()
就可以了
// 声明式函数
function fn() {
console.log('我是 fn 函数')
}
// 调用函数
fn()
// 赋值式函数
var fn2 = function () {
console.log('我是 fn2 函数')
}
// 调用函数
fn()
针对没有函数名的函数(匿名函数):就是直接在你要调用的函数后面加() eg:匿名函数的调用:(匿名函数)(),只能调用一次
// 定义一个匿名函数
// 定义函数就是为了调用,调用需要函数名
// 你定义函数没有函数,后期就无法调用
// 只能在定义以后立即调用
// 加在函数体上的(),表示这个函数是一个整体,是一个匿名函数,不是为了调用函数的
// 加在函数体后面的(),才是表示调用函数
(function (){
console.log('我是一个匿名函数')
})()
注意: 定义完一个函数以后,如果没有函数调用,那么写在 {} 里面的代码没有意义,只有调用以后才会执行
八.调用上的区别
虽然两种定义方式的调用都是一样的,但是还是有一些区别的
声明式函数: 调用可以在 定义之前或者定义之后
// 可以调用
fn()
// 声明式函数
function fn() {
console.log('我是 fn 函数')
}
// 可以调用
fn()
赋值式函数: 调用只能在 定义之后
// 会报错
fn()
// 赋值式函数
var fn = function () {
console.log('我是 fn 函数')
}
// 可以调用
fn()
九.函数的参数(重点)
我们在定义函数和调用函数的时候都出现过 ()
现在我们就来说一下这个 ()
的作用
就是用来放参数的位置
参数分为两种 行参 和 实参
// 声明式
function fn(行参写在这里) {
// 一段代码
}
fn(实参写在这里)
// 赋值式函数
var fn = function (行参写在这里) {
// 一段代码
}
fn(实参写在这里)
十.行参和实参的作用
行参
就是在函数内部可以使用的变量,在函数外部不能使用
每写一个单词,就相当于在函数内部定义了一个可以使用的变量(遵循变量名的命名规则和命名规范)
多个单词之间以 ,
分隔
// 书写一个参数
function fn(num) {
// 在函数内部就可以使用 num 这个变量
}
var fn1 = function (num) {
// 在函数内部就可以使用 num 这个变量
}
// 书写两个参数
function fun(num1, num2) {
// 在函数内部就可以使用 num1 和 num2 这两个变量
}
var fun1 = function (num1, num2) {
// 在函数内部就可以使用 num1 和 num2 这两个变量
}
如果只有行参的话,那么在函数内部使用的值个变量是没有值的,也就是 undefined
行参的值是在函数调用的时候由实参决定的
实参
在函数调用的时候给行参赋值的
也就是说,在调用的时候是给一个实际的内容的
function fn(num) {
// 函数内部可以使用 num
}
// 这个函数的本次调用,书写的实参是 100
// 那么本次调用的时候函数内部的 num 就是 100
fn(100)
// 这个函数的本次调用,书写的实参是 200
// 那么本次调用的时候函数内部的 num 就是 200
fn(200)
函数内部的行参的值,由函数调用的时候传递的实参决定
多个参数的时候,是按照顺序一一对应的
function fn(num1, num2) {
// 函数内部可以使用 num1 和 num2
}
// 函数本次调用的时候,书写的参数是 100 和 200
// 那么本次调用的时候,函数内部的 num1 就是 100,num2 就是 200
fn(100, 200)
3.函数里面有一个变量,记录了所有的你传入的实参,这个变量就是arguments,只有在函数中有这个预先定义好的变量
代码如下:
function fn(a,b,c){
console.log(a,b,c)
// 在函数里面有预先定义好的变量arguments
// 调用函数的时候,能接受到传入的所有实参
console.log(arguments)
}
// 实参在函数调用的时候给形参赋值
fn(11,22,33,'hello','world')
十一.参数个数的关系
行参比实参少
因为是按照顺序一一对应的
行参少就会拿不到实参给的值,所以在函数内部就没有办法用到这个值
function fn(num1, num2) {
// 函数内部可以使用 num1 和 num2
}
// 本次调用的时候,传递了两个实参,100 200 和 300
// 100 对应了 num1,200 对应了 num2,300 没有对应的变量
// 所以在函数内部就没有办法依靠变量来使用 300 这个值
fn(100, 200, 300)
行参比实参多
因为是按照顺序一一对应的
所以多出来的行参就是没有值的,就是 undefined
function fn(num1, num2, num3) {
// 函数内部可以使用 num1 num2 和 num3
}
// 本次调用的时候,传递了两个实参,100 和 200
// 就分别对应了 num1 和 num2
// 而 num3 没有实参和其对应,那么 num3 的值就是 undefined
fn(100, 200)
十二.函数的return(重点)
十三.终断函数
当我开始执行函数以后,函数内部的代码就会从上到下的依次执行
必须要等到函数内的代码执行完毕
而 return
关键字就是可以在函数中间的位置停掉,让后面的代码不在继续执行
function fn() {
console.log(1)
console.log(2)
console.log(3)
// 写了 return 以后,后面的 4 和 5 就不会继续执行了
return
console.log(4)
console.log(5)
}
// 函数调用
fn()
十四.返回值
函数调用本身也是一个表达式,表达式就应该有一个值出现
现在的函数执行完毕之后,是不会有结果出现的
// 比如 1 + 2 是一个表达式,那么 这个表达式的结果就是 3
console.log(1 + 2) // 3
function fn() {
// 执行代码
}
// fn() 也是一个表达式,这个表达式就没有结果出现
console.log(fn()) // undefined
return
关键字就是可以给函数执行完毕一个结果
function fn() {
// 执行代码
return 100
}
// 此时,fn() 这个表达式执行完毕之后就有结果出现了
console.log(fn()) // 100
return
关键把任何内容当作这个函数运行后的结果十五.函数的优点
十六.预解析(重点)(函数是一等公民,在函数名和变量名相同时,先执行函数)
十七.解释代码
因为是在所有代码执行之前进行解释,所以叫做 预解析(预解释)
需要解释的内容有两个
声明式函数
var
关键字
注意:函数预解析过后,执行过程中不再执行,即跳过。
看下面一段代码
fn()
console.log(num)
function fn() {
console.log('我是 fn 函数')
}
var num = 100
经过预解析之后可以变形为
function fn() {
console.log('我是 fn 函数')
}
var num
fn()
console.log(num)
num = 100
赋值是函数会按照 var
关键字的规则进行预解析
一.作用域
就是一个变量可以生效的范围
变量不是在所有的地方都可以使用的,这个变量的生效范围就是作用域
1.全局作用域
== 全局作用域是最大的作用域
== 在全局作用域定义的变量可以在任何地方使用
== 页面打开的时候,浏览器会自动给我们生成一个全局作用域window
== 这个作用域会一直存在,直到浏览器关闭就销毁了
2.局部作用域
== 局部作用域就是在全局作用域下面又开辟出来的一个相对小一些的作用域
== 在局部作用域中定义的变量只能在这个局部作用域起作用
== 在js中只有函数能生成一个局部作用域,别的都不行
== 每一个函数,都是一个局部作用域
3.作用域链
==包含了函数被创建的作用域中可使用的数据的集合,称为函数的作用域链。
==作用域链决定了哪些数据能被函数访问。
eg:举个例子
// 下面两个变量都是存在全局作用域下面的
var num = 100;
var num2 = 200;
function fn(){
// 下面的这个变量就是一个fn局部作用域里面的变量
// 只能在函数fn内部使用
var num3 = 300;
console.log(num3) ;// 可以打印
}
fn()
// console.log(num3) ;// 报错
二.变量的使用规则,访问规则以及赋值规则。
1.变量的使用规则
== 有了作用域,变量就有了使用范围,也就又了使用规则
== 变量使用规则分为两种:访问规则和赋值规则
2.访问规则
== 当我想获取一个变量的值的时候,我们管这个行为叫做访问
2.1获取变量的规则
== 首先:在自己的作用域内部查找,如果有,就直接拿来使用
== 如果没有,就去上一级作用域查找,如果有,就拿来使用
== 如果没有,就继续去上一级作用域查找,依次类推
== 如果一直到全局作用域都没有这个变量,那么就会报错,(xxx is not defined)
== 变量的访问规则业叫做变量作用域的查找机制
3.赋值规则
== 当你想给一个变量赋值的时候,那么就先要找到这个变量,再给他赋值
3.1变量赋值规则
== 现在自己作用域内部查找,有就直接赋值
== 没有就去上一级作用域查找,有就直接赋值
== 在没有就继续去上一级作用域查找,有就直接赋值
== 如果一直到全局作用域都没有,那么就把这个变量定义成全局变量,再给他赋值
4.注意:作用域的查找机制只能是向上级找,不能向下级找
注意:在函数里面定义变量的使用,不要忘记写var,否则会变成全局变量
三.递归函数
在编程世界里面,递归就是一个自己调用自己的手段。递归函数:一个函数内部,调用了自己,循环往复。
其实递归和循环很类似,需要有初始条件,自增,执行代码,条件判断的,不然就是一个没有尽头的递归函数,我们佳作死递归
eg1:// 简单实现一个递归
// 需求: 求1到5的累加和
// 书写一个函数sum,可以实现任意数字到5的累加和
function sum(n){
if(n==5){
return 5;
}else {
return n + sum(n+1)
}
}
alert(sum(5))
/*n=5 求5到5的累计和,就是5 ; sum(5)
n=4 求4 + 5累加和,就是 4+sum(5) ; sum(4)
n=3 求3 + 4的累计和,就是3+sum(4); 只要能确保sum(4)的结果是对的,那么sum(3)
n=2 求2 + 3的累计和,就是2+sum(3); 只要能确保sum(3)的结果是对的,那么sum(2)
n=1 求1 + 2的累计和,就是1+sum(2); 只要能确保sum(2)的结果是对的,那么sum(1)*/
eg2:// 案例:求斐波那契数列的第n项的值
// 斐波那契数列: 1、1、2、3、5、8、13、21、34、...
function feibo(n){
if(n===1){
return 1;
}else if(n===2){
return 1;
}else{
return feibo(n-1)+feibo(n-2)
}
}
alert(feibo(10))
// n=1 返回1
// n=2 返回1
// n=3 前两项的和 feibo(3) = feibo(2)+feibo(1)
// n=4 前两项的和 feibo(4) = feibo(3)+feibo(2)
// n=5 前两项的和 feibo(5) = feibo(4)+feibo(3)
四.数组
1.什么是数组:
== 字面意思就是数据的集合
== 也就i是我们把一些数据放在一个盒子里面,按照顺序排好
== [12,'hello','str',null,undefined,NaN]
== 这个东西就是一个数组,存储写一些数据的集合
2.创建一个数组
== 数组就是一个[]
== 在[]里面存储着各种各样的数据,按照顺序依次排好
==字面量创建一个数组:直接使用[]的方式创建一个数组 eg:var arr = [];var arr2 = [1,'hello',2,3]
==内置构造函数创建数组:使用js的内置构造函数Array创建一个数组 eg:var arr4=new Array();var arr4 = new Array(1,'hello',2,3);var arr4=new Array(10),创建一个数组长度为10的数组。(can'shu)
3.数组的length
== length:长度的意思
== length就是表示数组的长度,数组里面有多少个成员,length就是多少
4.数组的索引
== 索引,也叫做下标,是指一个成员在数组里面排在第一个位置
== 注意:在所有的编程语言中,索引都是从0开始
== 在js里面也是一样,索引从0开始
== 要获取数组的成员的值,需要通过索引来获取,语法: 数组[索引]
5.for循环遍历数组
== 因为数组的索引可以后去数组中的成员值
== 数组的索引又是按照0到length-1顺序排列
== 我们就可以使用for循环来循环数组,因为for循环我们可以设置成0到length-1递增
== 我们把这个行为叫做遍历
五.对象
1.对象是一个复杂数据类型
== 也是一个盒子,里面存储了一些数据
== {}就是一个盒子
== {name:'kk',age:18,height:180,weight:120,love:'rap',hate:'写代码'}
== 这里的{}和函数的{}不一样
== 函数里面的是写代码的,对象里面是写一些数据的
== 对象是一个无序的键值对的集合
== {} 里面的一个键值对我们可以叫做一个成员
== {} 一个成员里面的键,我们也可以叫做成员名
== {} 一个成员里面的值,我们可以叫做成员值
2.创建一个对象
字面量的方式创建一个对象
== 就是用{}来创建一个对象
== 每一个键值对之间用 逗号,分隔
== 键和值之间是 冒号:
内置构造函数创建对象
== js里面内置了对象的构造函数Object,用于创建一个对象
== 语法: var obj = new Object()
3.操作对象的方法
获取对象的成员值:
== 对象.成员名 这个成员名是一个字符串
== 对象[成员名] 这个成员名是一个变量名(一定要给成员名声明一个变量名)
增加/修改成员
== 对象.成员名 = 成员值
== 对象[成员名] = 成员值
删除成员
== delete 对象.成员名 eg:delete obj2.name
== delete 对象[成员名] eg:delete human[a]
eg:
字面量的方式创建一个对象
// 创建一个空对象
var obj = {}
// 像对象中添加成员
obj.name = 'Jack'
obj.age = 18
// 查询对象中的成员值
console.log(obj.name)
// 修改对象中的成员值
obj.name ="lucy"
// 删除对象中的成员
delete obj.name
内置构造函数的方式创建对象
// 创建一个空对象
var obj = new Object()
// 向对象中添加成员
obj.name = 'Rose'
obj.age = 20
// 查询对象中的成员值
console.log(obj.name)
// 修改对象中的成员值
obj.name ="lucy"
// 删除对象中的成员
delete obj.name
六.for…in循环遍历对象中的成员和成员值
// 创建一个空对象
var obj = new Object()
// 向对象中添加成员
obj.name = 'Rose'
obj.age = 20
for(var key in obj){
console.log(key)
console.log(obj[key])
}
key
是我们自己定义的一个变量七.数据类型之间存储的区别(重点)
八.基本数据类型在内存中的存储情况
var num = 100
,在内存中的存储情况九.复杂数据类型在内存中的存储情况
下面这个 对象 的存储
var obj = {
name: 'Jack',
age: 18,
gender: '男'
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CacU42YP-1661353959189)(H:\Typora\data\TyporaPages\image-20220805192049037.png)]
复杂数据类型的存储
这就是数据类型之间存储的区别
十.数据类型之间的比较
基本数据类型是 值 之间的比较
var num = 1
var str = '1'
console.log(num == str) // true
复杂数据类型是 地址 之间的比较
var obj = { name: 'Jack' }
var obj2 = { name: 'Jack' }
console.log(obj == obj2) // false
obj
和 obj2
两个变量的地址不一样false
先遍历数组,让挨着的两个进行比较,如果前一个比后一个大,那么就把两个换个位置
数组遍历一遍以后,那么最后一个数字就是最大的那个了
然后进行第二遍的遍历,还是按照之前的规则,第二大的数字就会跑到倒数第二的位置
以此类推,最后就会按照顺序把数组排好了
我们先来准备一个乱序的数组
var arr = [3, 1, 5, 6, 4, 9, 7, 2, 8]
先不着急循环,先来看数组里面内容换个位置
// 假定我现在要让数组中的第 0 项和第 1 项换个位置
// 需要借助第三个变量
var tmp = arr[0]
arr[0] = arr[1]
arr[1] = tmp
第一次遍历数组,把最大的放到最后面去
for (var i = 0; i < arr.length; i++) {
// 判断,如果数组中的当前一个比后一个大,那么两个交换一下位置
if (arr[i] > arr[i + 1]) {
var tmp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = tmp
}
}
// 遍历完毕以后,数组就会变成 [3, 1, 5, 6, 4, 7, 2, 8, 9]
按照数组的长度来遍历多少次
for (var j = 0; j < arr.length; j++) {
for (var i = 0; i < arr.length; i++) {
// 判断,如果数组中的当前一个比后一个大,那么两个交换一下位置
if (arr[i] > arr[i + 1]) {
var tmp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = tmp
}
}
}
// 结束以后,数组就排序好了
给一些优化
想象一个问题,假设数组长度是 9,第八次排完以后
后面八个数字已经按照顺序排列好了,剩下的那个最小的一定是在最前面
那么第九次就已经没有意义了,因为最小的已经在最前面了,不会再有任何换位置出现了
那么我们第九次遍历就不需要了,所以我们可以减少一次
for (var j = 0; j < arr.length - 1; j++) {
for (var i = 0; i < arr.length; i++) {
// 判断,如果数组中的当前一个比后一个大,那么两个交换一下位置
if (arr[i] > arr[i + 1]) {
var tmp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = tmp
}
}
}
第二个问题,第一次的时候,已经把最大的数字放在最后面了
那么第二次的时候,其实倒数第二个和最后一个就不用比了
因为我们就是要把倒数第二大的放在倒数第二的位置,即使比较了,也不会换位置
第三次就要倒数第三个数字就不用再和后两个比较了
以此类推,那么其实每次遍历的时候,就遍历当前次数 - 1 次
for (var j = 0; j < arr.length - 1; j++) {
for (var i = 0; i < arr.length - 1 - j; i++) {
// 判断,如果数组中的当前一个比后一个大,那么两个交换一下位置
if (arr[i] > arr[i + 1]) {
var tmp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = tmp
}
}
}
至此,一个冒泡排序就完成了
先假定数组中的第 0 个就是最小的数字的索引
然后遍历数组,只要有一个数字比我小,那么就替换之前记录的索引
知道数组遍历结束后,就能找到最小的那个索引,然后让最小的索引换到第 0 个的位置
再来第二趟遍历,假定第 1 个是最小的数字的索引
在遍历一次数组,找到比我小的那个数字的索引
遍历结束后换个位置
依次类推,也可以把数组排序好
准备一个数组
var arr = [3, 1, 5, 6, 4, 9, 7, 2, 8]
假定数组中的第 0 个是最小数字的索引
var minIndex = 0
遍历数组,判断,只要数字比我小,那么就替换掉原先记录的索引
var minIndex = 0
for (var i = 0; i < arr.length; i++) {
if (arr[i] < arr[minIndex]) {
minIndex = i
}
}
// 遍历结束后找到最小的索引
// 让第 minIndex 个和第 0 个交换
var tmp = arr[minIndex]
arr[minIndex] = arr[0]
arr[0] = tmp
按照数组的长度重复执行上面的代码
for (var j = 0; j < arr.length; j++) {
// 因为第一遍的时候假定第 0 个,第二遍的时候假定第 1 个
// 所以我们要假定第 j 个就行
var minIndex = j
// 因为之前已经把最小的放在最前面了,后面的循环就不需要判断前面的了
// 直接从 j + 1 开始
for (var i = j + 1; i < arr.length; i++) {
if (arr[i] < arr[minIndex]) {
minIndex = i
}
}
// 遍历结束后找到最小的索引
// 第一堂的时候是和第 0 个交换,第二趟的时候是和第 1 个交换
// 我们直接和第 j 个交换就行
var tmp = arr[minIndex]
arr[minIndex] = arr[j]
arr[j] = tmp
}
一些优化
和之前一样,倒数第二次排序完毕以后,就已经排好了,最后一次没有必要了
for (var j = 0; j < arr.length - 1; j++) {
var minIndex = j
for (var i = j + 1; i < arr.length; i++) {
if (arr[i] < arr[minIndex]) {
minIndex = i
}
}
var tmp = arr[minIndex]
arr[minIndex] = arr[j]
arr[j] = tmp
}
在交换变量之前,可以判断一下,如果我们遍历后得到的索引和当前的索引一直
那么就证明当前这个就是目前最小的,那么就没有必要交换
做一我们要判断,最小作引和当前作引不一样的时候,才交换
for (var j = 0; j < arr.length - 1; j++) {
var minIndex = j
for (var i = j + 1; i < arr.length; i++) {
if (arr[i] < arr[minIndex]) {
minIndex = i
}
}
if (minIndex !== j) {
var tmp = arr[minIndex]
arr[minIndex] = arr[j]
arr[j] = tmp
}
}
至此,选择排序完成
数组是一个复杂数据类型,我们在操作它的时候就不能再想基本数据类型一样操作了
比如我们想改变一个数组
// 创建一个数组
var arr = [1, 2, 3]
// 我们想把数组变成只有 1 和 2
arr = [1, 2]
push
是用来在数组的末尾追加一个元素
语法: 数组.push(追加的元素1,追加的元素2,…)
返回值: 增加完以后数组的长度
在原数组修改
var arr = [1, 2, 3]
// 使用 push 方法追加一个元素在末尾
arr.push(4)
console.log(arr) // [1, 2, 3, 4]
pop
是用来删除数组末尾的一个元素
语法: 数组.pop()
返回值:被删除的那个元素
在原数组修改
var arr = [1, 2, 3]
// 使用 pop 方法删除末尾的一个元素
arr.pop()
console.log(arr) // [1, 2]
unshift
是在数组的最前面添加一个元素
语法: 数组.unshift(追加的元素1,追加的元素2,…)
返回值: 增加完以后数组的长度
在原数组修改
var arr = [1, 2, 3]
// 使用 unshift 方法想数组的最前面添加一个元素
arr.unshift(4)
console.log(arr) // [4, 1, 2, 3]
shift
是删除数组最前面的一个元素
用来删除数组的最前面一个元素
语法: 数组.shift()
在原数组修改
var arr = [1, 2, 3]
// 使用 shift 方法删除数组最前面的一个元素
arr.shift()
console.log(arr) // [2, 3]
splice
是截取数组中的某些内容,按照数组的索引来截取
语法: splice(从哪一个索引位置开始,截取多少个,替换的新元素)
(第三个参数可以不写)
返回值:被删除的元素的集合,即使只有一个,也放在盒子里面
在原数组修改
注意:可以设置截取两个,替换的元素可以实现在索引值处增加。
var arr = [1, 2, 3, 4, 5]
// 使用 splice 方法截取数组
arr.splice(1, 2)
console.log(arr) // [1, 4, 5]
arr.splice(1, 2)
表示从索引 1 开始截取 2 个内容var arr = [1, 2, 3, 4, 5]
// 使用 splice 方法截取数组
arr.splice(1, 2, '我是新内容')
console.log(arr) // [1, '我是新内容', 4, 5]
arr.splice(1, 2, '我是新内容')
表示从索引 1 开始截取 2 个内容reverse
是用来反转数组使用的
语法: arr.reverse()
返回值: 修改好的原数组
var arr = [1, 2, 3]
// 使用 reverse 方法来反转数组
arr.reverse()
console.log(arr) // [3, 2, 1]
sort
是用来给数组排序的
语法: arr.sort(排序规则函数) 注意:如果不写排序规则函数,就是按照字符串的规则进行排序,而不是按数字大小排序。
数组排序,修改的是原数组
返回值:排好序的原数组。
var arr = [2, 3, 1]
// 使用 sort 方法给数组排序
arr.sort()
console.log(arr) // [1, 2, 3]
arr2.sort(function(a,b){
/*如果返回值是负数 ,那么 a 会被排列到b之前
如果返回值是0 , a 和 b 的相对位置不变
如果返回值是正数 , b 会被排列到 a 之前*/
if(a>b){
return -1; // return b-a
}else if(a<b){
return 1; // return b-a
}else{
return 0 // return b-a
}
// 上面的代码可以简写成 return b-a(从大到小),(从小到大是a-b)
})
concat
是把多个数组进行拼接
语法: arr1.concat(arr2,arr3,…)
和之前的方法有一些不一样的地方,就是 concat
不会改变原始数组,而是返回一个新的数组
var arr = [1, 2, 3]
// 使用 concat 方法拼接数组
var newArr = arr.concat([4, 5, 6])
console.log(arr) // [1, 2, 3]
console.log(newArr) // [1, 2, 3, 4, 5, 6]
join
是把数组里面的每一项内容链接起来,变成一个字符串
语法: arr.join(‘链接符’)
可以自己定义每一项之间链接的内容 join(要以什么内容链接)
不会改变原始数组,而是把链接好的字符串返回
没有写连接符,默认用逗号链接。
注意:如果想要实现将字符串中的split方法实现的单个字符串的数组拼接起来成为字符串,拼接的连接符为空字符串即可。
var arr = [1, 2, 3]
// 使用 join 链接数组
var str = arr.join('-')
console.log(arr) // [1, 2, 3]
console.log(str) // 1-2-3
indexOf
用来找到数组中某一项的索引
语法: indexOf(你要找的数组中的项)
var arr = [1, 2, 3, 4, 5]
// 使用 indexOf 超找数组中的某一项
var index = arr.indexOf(3)
console.log(index) // 2
如果你要找的内容在数组中没有,那么就会返回 -1
var arr = [1, 2, 3, 4, 5]
// 使用 indexOf 超找数组中的某一项
var index = arr.indexOf(10)
console.log(index) // -1
和 for 循环一个作用,就是用来遍历数组的
语法:arr.forEach(function (item, index, arr) {})
var arr = [1, 2, 3]
// 使用 forEach 遍历数组
arr.forEach(function (item, index, arr) {
// item 就是数组中的每一项
// index 就是数组的索引
// arr 就是原始数组
console.log('数组的第 ' + index + ' 项的值是 ' + item + ',原始数组是', arr)
})
和 forEach 类似,只不过可以对数组中的每一项进行操作,返回一个新的数组
语法: arr.map(function(item,index,arr){}
返回值:一个新的数组
var arr = [1, 2, 3]
// 使用 map 遍历数组
var newArr = arr.map(function (item, index, arr) {
// item 就是数组中的每一项
// index 就是数组的索引
// arr 就是原始数组
return item + 10
})
console.log(newArr) // [11, 12, 13]
和 map 的使用方式类似,按照我们的条件来筛选数组
把原始数组中满足条件的筛选出来,组成一个新的数组返回
var arr = [1, 2, 3]
// 使用 filter 过滤数组
var newArr = arr.filter(function (item, index, arr) {
// item 就是数组中的每一项
// index 就是数组的索引
// arr 就是原始数组
return item > 1
})
console.log(newArr) // [2, 3]
> 1
> 1
的项var arr = [1, 30, 39, 29, 10, 13];
console.log(arr.every(function(item){
return item>0
}));// true
var arr = [1, 30, 39, 29, 10, 13];
console.log(arr.some(function(item){
return item>10
}));// true
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
function(previousValue, currentValue){
return previousValue + currentValue
},
initialValue
);
console.log(sumWithInitial);// 10
之前我们知道了,基本数据类型和复杂数据类型在存储上是有区别的
那么他们在赋值之间也是有区别的
基本数据类型之间的赋值
var num = 10
var num2 = num
num2 = 200
console.log(num) // 100
console.log(num2) // 200
复杂数据类型之间的赋值
var obj = {
name: 'Jack'
}
var obj2 = obj
obj2.name = 'Rose'
console.log(obj.name) // Rose
console.log(obj2.name) // Rose
函数的参数也是赋值的之中,在函数调用的时候,实参给行参赋值
和之前变量赋值的规则是一样的
函数传递基本数据类型
function fn(n) {
n = 200
console.log(n) // 200
}
var num = 100
fn(num)
console.log(num) // 100
函数传递复杂数据类型
function fn(o) {
o.name = 'Rose'
console.log(o.name) // Rose
}
var obj = {
name: 'Jack'
}
fn(obj)
console.log(obj.name) // Rose
方法一:
function norepeat(arr){
// 定义一个新数组,存储没有重复的数组元素
var result = [];
// 遍历数组 arr
// 如果遍历到的元素在result里面不存在,就从末尾加入
// 如果遍历到的元素在result里面存在, 就什么都不做
arr.forEach(function(item){
if(result.indexOf(item)==-1){
result.push(item)
}
})
// 返回新数组
return result;
}
console.log(norepeat([12,321,4,213,2,3,132,32,32,4,32,4,324]))
方法二:(因为遍历对象的key值会不同,遇到相同的成员名,key值被替换,即实现了去重)
function norepeat(arr){
// 定义一个对象,记录每个数组元素的出现
var obj = {};
// 遍历数组arr
arr.forEach(function(item){
// 记录每个出现的元素
obj[item] = 1
})
// 把对象中的成员遍历,
var result = [];
for(var key in obj){
// 把遍历到的成员名放到新数组里面
result.push(key)
}
return result;
}
console.log(norepeat(['hello','world','lucy','kily','lucy','lilei','kily']))
想开启严格模式,直接在代码最开始的位置写上字符串 use strict
<script>
'use strict'
// 下面代码书写就要按照严格模式来书写
script>
声明变量必须有 var
关键字
'use strict'
var num = 100
num2 = 200 // 这个就会报错
函数的行参不可以重复
'use strtic'
function fn(p1, p1) {} // 直接就会报错
声明式函数调用的时候函数内部没有 this
'use strtic'
function fn() {
console.log(this) // undefined
}
fn()
我们创建字符串也分为两种方法 字面量 和 构造函数
字面量:
var str = 'hello'
构造函数创建
var str = new String('hello')
0101010
这样的二进制数字a ~ z
/ A ~ Z
/ ``/ @
/… 之类的内容也有由二进制数字组成的a ~ z
/ A ~ Z
/ ``/ @
/… 之类的内容都有一个自己的编号,然后在计算机存储的时候,是存储的这些编号,我们看的时候,也是通过这些编号在解析成我们要看到的内容给我们看到[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oUPLx9MJ-1661353959190)(http://8f5843e2874c48668cb250413f8fd4d6/?ynotemdtimestamp=1660007011466)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0MDjgPTf-1661353959191)(http://ef1c22e48df64aa2ae5614cb4757aeba/?ynotemdtimestamp=1660007011466)]
charAt(索引)
是找到字符串中指定索引位置的内容返回(str[索引]也可以实现)
语法:str.charAt(索引)
var str = 'Jack'
// 使用 charAt 找到字符串中的某一个内容
var index = str.charAt(2)
console.log(index) // c
如果没有对应的索引,那么就会返回 空字符串
var str = 'Jack'
// 使用 charAt 找到字符串中的某一个内容
var index = str.charAt(10)
console.log(index) // ''
''
charCodeAt(索引)
就是返回对应索引位置的 unicode 编码,如果没有找到返回NaN
语法:str.charCodeAt(索引)
var str = 'Jack'
// 使用 charAt 找到字符串中的某一个内容
var index = str.charCodeAt(0)
console.log(index) // 74
J
在 unicode 对照表里面存储的是 74,所以就会返回 74indexOf
就是按照字符找到对应的索引,如果没有找到返回-1。
语法:str.indexOf(字符)
var str = 'Jack'
// 使用 indexOf 找到对应的索引
var index = str.indexOf('J')
console.log(index) // 0
J
在字符串 Jack
中的索引位置是 0substring
是用来截取字符串使用的
语法: substring(从哪个索引开始,到哪个索引截止)
,包含开始索引,不包含结束索引(包前不包后),返回截取好的字符串。
只写开始索引的话,默认截取到末尾
截止索引大于字符串长度的话,也是截取到末尾。
var str = 'hello'
// 01234
// 使用 substring 截取字符串
var newStr = str.substring(1, 3)
console.log(newStr) // el
substr
也是用来截取字符串的
语法:substr(从哪个索引开始,截取多少个)
只写开始索引的话,默认截取到末尾
截取长度大于字符串长度的话,也是截取到末尾。
var str = 'hello'
// 01234
// 使用 substr 截取字符串
var newStr = str.substr(1, 3)
console.log(newStr) // ell
substring
不一样的是,第二个参数是截取多少个ell
这两个方法分别使用用来给字符串转成 小写字母 和 大写字母 的
var str = hello
// 使用 toUpperCase 转换成大写
var upper = str.toUpperCase()
console.log(upper) // HELLO
// 使用 toLowerCase 转换成小写
var lower = upper.toLowerCase()
console.log(lower) // hello
str.split('分隔符')
str.trim()
str.trimStart()
或者str.trimLeft()
str.trimEnd()
或者str.trimRight()
js里面有一些有特殊含义的符号
// 比如:"",''
// 我们可以在字符串中使用转移符,\
// 可以把有意思的字符转成没有特殊含义的
// 可以把没有特殊含义的转换有特殊含义的
常见转义符(字符串转义字符,都是用\开头,需要写在引号里。)
1.\n 换行
2.\\ 输出\
3.\' 输出单引号
4.\" 输出双引号
5.\t tab缩进
6.\b 空格(b是blank的意思)
Math.random()
这个方法是用来生成一个 0 ~ 1
之间的随机数
每次执行生成的数字都不一样,但是一定是 0 ~ 1
之间的
生成的数字包含 0 ,但是不包含 1
var num = Math.random()
console.log(num) // 得到一个随机数
Math.round()
是将一个小数 四舍五入 变成一个整数
var num = 10.1
console.log(Math.round(num)) // 10
var num2 = 10.6
console.log(Math.round(num2)) // 11
Math.abs()
是返回一个数字的绝对值
var num = -10
console.log(math.abs(num)) // 10
Math.ceil()
是将一个小数 向上取整 得到的整数
var num = 10.1
console.log(Math.ceil(num)) // 11
var num2 = 10.9
console.log(Math.ceil(num2)) // 11
Math.floor()
是将一个小数 向下取整 的到的整数
var num = 10.1
console.log(Math.floor(num)) // 10
var num2 = 10.9
console.log(Math.floor(num2)) // 10
Math.max()
得到的是你传入的几个数字之中最大的那个数字
console.log(Math.max(1, 2, 3, 4, 5)) // 5
Math.min()
得到的是你传入的几个数字之中最小的那个数字
console.log(Math.min(1, 2, 3, 4, 5)) // 1
Math.PI
得到的是 π
的值,也就是 3.1415936...
console.log(Math.PI) // 3.141592653589793
console.log(Math.pow(2,4);// 16
console.log(Math.sqrt(4))//2
八进制:0~7 程序里数字前面加0表示八进制
var num=010;
console.log(num);//num=8
十六进制:0~9 a~f 程序里数字前加0x表十六进制。
toString()
方法可以在数字转成字符串的时候给出一个进制数
语法: toString(你要转换的进制)
var num = 12
console.log(num.toString(16)) // 'C'
new Date()
在不传递参数的情况下是默认返回当前时间
var time = new Date()
console.log(time) // 当前时间 Fri Mar 01 2019 13:11:23 GMT+0800 (中国标准时间)
new Date()
在传入参数的时候,可以获取到一个你传递进去的时间
var time = new Date('2019-03-03 13:11:11')
console.log(time) // Sun Mar 03 2019 13:11:11 GMT+0800 (中国标准时间)
new Date()
传递的参数有多种情况
传递两个数字,第一个表示年,第二个表示月份
var time = new Date(2019, 00) // 月份从 0 开始计数,0 表示 1月,11 表示 12月
console.log(time) // Tue Jan 01 2019 00:00:00 GMT+0800 (中国标准时间)
传递三个数字,前两个不变,第三个表示该月份的第几天,从 1 到 31
var time = new Date(2019, 00, 05)
console.log(time) // Sat Jan 05 2019 00:00:00 GMT+0800 (中国标准时间)
传递四个数字,前三个不变,第四个表示当天的几点,从 0 到 23
var time = new Date(2019, 00, 05, 22)
console.log(time) // Sat Jan 05 2019 22:00:00 GMT+0800 (中国标准时间)
传递五个数字,前四个不变,第五个表示的是该小时的多少分钟,从 0 到 59
var time = new Date(2019, 00, 05, 22, 33)
console.log(time) // Sat Jan 05 2019 22:33:00 GMT+0800 (中国标准时间)
传递六个数字,前五个不变,第六个表示该分钟的多少秒,从 0 到 59
var time = new Date(2019, 00, 05, 22, 33, 55)
console.log(time) // Sat Jan 05 2019 22:33:55 GMT+0800 (中国标准时间)
传入字符串的形式
console.log(new Date('2019'))
// Tue Jan 01 2019 08:00:00 GMT+0800 (中国标准时间)
console.log(new Date('2019-02'))
// Fri Feb 01 2019 08:00:00 GMT+0800 (中国标准时间)
console.log(new Date('2019-02-03'))
// Sun Feb 03 2019 08:00:00 GMT+0800 (中国标准时间)
console.log(new Date('2019-02-03 13:'))
// Sun Feb 03 2019 13:00:00 GMT+0800 (中国标准时间)
console.log(new Date('2019-02-03 13:13:'))
// Sun Feb 03 2019 13:13:00 GMT+0800 (中国标准时间)
console.log(new Date('2019-02-03 13:13:13'))
// Sun Feb 03 2019 13:13:13 GMT+0800 (中国标准时间)
Sun Feb 03 2019 13:13:13 GMT+0800 (中国标准时间)
语法: toLocaleString()
把日期格式转换成字符串格式
var date = new Date()
console.log(date.toLocaleString());// 2022/8/10 10:59:46
getFullYear()
方式是得到指定字符串中的哪一年
var time = new Date(2019, 03, 03, 08, 00, 22)
console.log(time.getFullYear()) // 2019
getMonth()
方法是得到指定字符串中的哪一个月份
var time = new Date(2019, 03, 03, 08, 00, 22)
console.log(time.getMonth()) // 3
getDate()
方法是得到指定字符串中的哪一天
var time = new Date(2019, 03, 03, 08, 00, 22)
console.log(time.getDate()) // 3
getHours()
方法是得到指定字符串中的哪小时
var time = new Date(2019, 03, 03, 08, 00, 22)
console.log(time.getHours()) // 8
getMinutes()
方法是得到指定字符串中的哪分钟
var time = new Date(2019, 03, 03, 08, 00, 22)
console.log(time.getMinutes()) // 0
getSeconds()
方法是得到指定字符串中的哪秒钟
var time = new Date(2019, 03, 03, 08, 00, 22)
console.log(time.getSeconds()) // 22
getDay()
方法是得到指定字符串当前日期是一周中的第几天(周日是 0,周六是 6)
var time = new Date(2019, 03, 08, 08, 00, 22)
console.log(time.getDay()) // 1
getTime()
方法是得到执行时间到 格林威治时间
的毫秒数
var time = new Date(2019, 03, 08, 08, 00, 22)
console.log(time.getTime()) // 1554681622000
1970年01月01日00时00分00秒
格林威治时间
格林威治时间
的数字是 0格林威治时间
开始,每经过1毫秒,数字就会 + 1格林威治时间
的毫秒数2019-01-01 00:00:00
到 2019-01-03 04:55:34
的时间差先获取两个时间点到 格林威治时间
的毫秒数
var time1 = new Date('2019-01-01 00:00:00')
var time2 = new Date('2019-01-03 04:55:34')
time1 = time1.getTime()
time2 = time2.getTime()
console.log(time1) // 1546272000000
console.log(time2) // 1546462534000
两个时间相减,得到两个时间点之间相差的毫秒数
var differenceTime = time2 - time1
console.log(differenceTime) // 190534000
把我们计算的毫秒数换算成时间
先计算出有多少天
以为一天是 1000 * 60 * 60 * 24
毫秒
用总的毫秒数除以一天的毫秒数,就能得到多少天了
var time1 = new Date('2019-01-01 00:00:00')
var time2 = new Date('2019-01-03 04:55:34')
time1 = time1.getTime()
time2 = time2.getTime()
var differenceTime = time2 - time1
// 计算整的天数
var day = differenceTime / (1000 * 60 * 60 * 24) // 2.20525462962963
day = Math.ceil(day) // 2
使用 differenceTime
减去两天所包含的毫秒数,剩下的就是不够一天的毫秒数
用不够一天的毫秒数计算出有多少个小时
因为一个小时是 1000 * 60 * 60
毫秒
用不够一天的毫秒数除以一小时的毫秒数,就能得到多少小时了
var time1 = new Date('2019-01-01 00:00:00')
var time2 = new Date('2019-01-03 04:55:34')
time1 = time1.getTime()
time2 = time2.getTime()
var differenceTime = time2 - time1
// 计算整的天数
var day = differenceTime / (1000 * 60 * 60 * 24) // 2.20525462962963
day = Math.floor(day) // 2
// 计算整的小时数
var afterHours = differenceTime - (1000 * 60 * 60 * 24 * 2)
var hours = afterHours / (1000 * 60 * 60)
hours = Math.floor(hours) // 4
同理,使用 afterHours
- 4个小时包含的毫秒数,剩下的就是不够一个小时的毫秒数
用不够一个小时的毫秒数计算出有多少分钟
因为一分钟是 1000 * 60
毫秒
用不够一个小时的毫秒数除以一分钟的毫秒数就能得到多少分钟了
var time1 = new Date('2019-01-01 00:00:00')
var time2 = new Date('2019-01-03 04:55:34')
time1 = time1.getTime()
time2 = time2.getTime()
var differenceTime = time2 - time1
// 计算整的天数
var day = differenceTime / (1000 * 60 * 60 * 24) // 2.20525462962963
day = Math.floor(day) // 2
// 计算整的小时数
var afterHours = differenceTime - (1000 * 60 * 60 * 24 * 2)
var hours = afterHours / (1000 * 60 * 60)
hours = Math.floor(hours) // 4
// 计算整分钟数
var afterMinutes = afterHours - (1000 * 60 * 60 * 4)
var minutes = afterMinutes / (1000 * 60)
minutes = Math.floor(minutes) // 55
和之前一样的道理计算出秒
var time1 = new Date('2019-01-01 00:00:00')
var time2 = new Date('2019-01-03 04:55:34')
time1 = time1.getTime()
time2 = time2.getTime()
var differenceTime = time2 - time1
// 计算整的天数
var day = differenceTime / (1000 * 60 * 60 * 24) // 2.20525462962963
day = Math.floor(day) // 2
// 计算整的小时数
var afterHours = differenceTime - (1000 * 60 * 60 * 24 * 2)
var hours = afterHours / (1000 * 60 * 60)
hours = Math.floor(hours) // 4
// 计算整分钟数
var afterMinutes = afterHours - (1000 * 60 * 60 * 4)
var minutes = afterMinutes / (1000 * 60)
minutes = Math.floor(minutes) // 55
// 计算整秒数
var afterSeconds = afterMinutes - (1000 * 60 * 55)
var seconds = afterSeconds / 1000
seconds = Math.floor(seconds) // 34
最后,同理减去整秒的数,剩下的就是毫秒数
var time1 = new Date('2019-01-01 00:00:00')
var time2 = new Date('2019-01-03 04:55:34')
time1 = time1.getTime()
time2 = time2.getTime()
var differenceTime = time2 - time1
// 计算整的天数
var day = differenceTime / (1000 * 60 * 60 * 24) // 2.20525462962963
day = Math.floor(day) // 2
// 计算整的小时数
var afterHours = differenceTime - (1000 * 60 * 60 * 24 * 2)
var hours = afterHours / (1000 * 60 * 60)
hours = Math.floor(hours) // 4
// 计算整分钟数
var afterMinutes = afterHours - (1000 * 60 * 60 * 4)
var minutes = afterMinutes / (1000 * 60)
minutes = Math.floor(minutes) // 55
// 计算整秒数
var afterSeconds = afterMinutes - (1000 * 60 * 55)
var seconds = afterSeconds / 1000
seconds = Math.floor(seconds) // 34
// 计算毫秒数
var milliSeconds = afterSeconds - (1000 * 34) // 0
最后我们把结果输出一下就可以了
document.write('2019-01-01 00:00:00 和 2019-01-03 04:55:34 之间相差')
document.write(day + '天' + hours + '小时' + minutes + '分钟' + seconds + '秒' + milliSeconds + '毫秒')
倒计时多少时间以后执行函数
语法: setTimeout(要执行的函数,多长时间以后执行)
会在你设定的时间以后,执行函数
var timerId = setTimeout(function () {
console.log('我执行了')
}, 1000)
console.log(timerId) // 1
每间隔多少时间就执行一次函数
语法: setInterval(要执行的函数,间隔多少时间)
var timerId = setInterval(function () {
console.log('我执行了')
}, 1000)
设置定时器的时候,他的返回值是部分 setTimeout
和 setInterval
的
只要有一个定时器,那么就是一个数字
var timerId = setTimeout(function () {
console.log('倒计时定时器')
}, 1000)
var timerId2 = setInterval(function () {
console.log('间隔定时器')
}, 1000)
console.log(timerId) // 1
console.log(timerId2) // 2
我们刚才提到过一个 timerId,是表示这个定时器是页面上的第几个定时器
这个 timerId 就是用来关闭定时器的数字
我们有两个方法来关闭定时器 clearTimeout
和 clearInterval
var timerId = setTimeout(function () {
console.log('倒计时定时器')
}, 1000)
clearTimeout(timerId)
var timerId2 = setInterval(function () {
console.log('间隔定时器')
}, 1000)
clearInterval(timerId2)
原则上是
clearTimeout
关闭 setTimeout
clearInterval
关闭 setInterval
但是其实是可以通用的,他们可以混着使用
var timerId = setTimeout(function () {
console.log('倒计时定时器')
}, 1000)
// 关闭倒计时定时器
clearInterval(timerId)
var timerId2 = setInterval(function () {
console.log('间隔定时器')
}, 1000)
// 关闭间隔定时器
clearTimeout(timerId2)
我们写的js代码分为两种
1.同步代码: 类似银行的vip客户,来了就办业务
不是异步的就都是同步代码
2.异步代码: 类似银行的普通客户,需要排队叫号办业务
== 目前我们学过的异步代码只有一种
== 就是定时器里面要执行的函数里面的代码(ding'shi'q)
3.代码执行顺序
== 先执行全部的同步代码
== 再执行排队的异步代码
console.log('js start');// 同步代码
// 定义变量,并给变量赋值一个函数是同步代码
var fn = function(){
// 由于fn里面的代码时定时器要执行的
// 所以要等待所有的同步代码执行完,并且时间到了才执行
console.log('我是定时器里面的函数')
}
console.log('world');// 同步代码
// 定义一个定时器,就相当于取号,是同步代码
setTimeout(fn,1000)
console.log('hello');// 同步代码
console.log('js end');// 同步代码
innerHeight
和 innerWidth
这两个方法分别是用来获取浏览器窗口的宽度和高度(包含滚动条的)
var windowHeight = window.innerHeight
console.log(windowHeight)
var windowWidth = window.innerWidth
console.log(windowWidth)
alert
是在浏览器弹出一个提示框
window.alert('我是一个提示框')
confirm
是在浏览器弹出一个询问框
var boo = window.confirm('我是一个询问框')
console.log(boo)
这个弹出层有一个询问信息和两个按钮
当你点击确定的时候,就会得到 true
当你点击取消的时候,就会得到 false
prompt
是在浏览器弹出一个输入框
var str = window.prompt('请输入内容')
console.log(str)
这个弹出层有一个输入框和两个按钮
当你点击取消的时候,得到的是 null
当你点击确定的时候得到的就是你输入的内容
location
location.href
这个属性存储的是浏览器地址栏内 url 地址的信息
console.log(window.location.href)
location.href
这个属性也可以给他赋值
location.href = './index.html'
// 这个就会跳转页面到后面你给的那个地址
location.reload()
这个方法会重新加载一遍页面,就相当于刷新是一个道理
window.location.reload()
history
history.back
是用来会退历史记录的,就是回到前一个页面,就相当于浏览器上的 ⬅️ 按钮
语法:history.back()
window.history.back()
history.forword
是去到下一个历史记录里面,也就是去到下一个页面,就相当于浏览器上的 ➡️ 按钮
window.history.forward()
这个不在是对象了,而是一个事件
表示这个事件会在浏览器的所有资源加载完成后触发
window.onload = function () {
console.log('页面已经加载完毕')
}
<html>
<head>
<meta charset="UTF-8" />
<script>
// 这个代码执行的时候,body 还没有加载
// 这个时候我们就获取不到 body 中的那个 div
// 就需要使用 window.onload 事件
window.onload = function () {
// 这个函数会在页面加载完毕以后在执行
// 那么这个时候页面的 DOM 元素都已经加载了,我们就可以获取 div 了
console.log(box)
}
script>
head>
<body>
<div>div>
body>
html>
<html>
<head>
<meta charset="UTF-8" />
head>
<body>
<div>div>
<script>
// 这个代码执行的时候,body 已经加载完毕了
// 在这里就可以获取到 div,写不写 window.onload 就无所谓了
window.onload = function () {
// 这个函数会在页面加载完毕以后在执行
// 那么这个时候页面的 DOM 元素都已经加载了,我们就可以获取 div 了
console.log(box)
}
script>
body>
html>
这个 onresize 事件是当浏览器的大小改变的的时候触发
window.onresize = function () {
console.log('浏览器大小改变了')
}
这个 onscroll 事件是当浏览器的滚动条滚动的时候触发
或者鼠标滚轮滚动的时候触发
window.onscroll = function () {
console.log('浏览器滚动了')
}
获取的是页面向上滚动的距离
一共有两个获取方式
document.body.scrollTop
document.documentElement.scrollTop
window.onscroll = function () {
console.log(document.body.scrollTop)
console.log(document.documentElement.scrollTop)
}
两个都是获取页面向上滚动的距离
兼容写法:document.body.scrollTop+document.documentElement.scrollTop
document.body.scrollTop||document.documentElement.scrollTop
区别:
DOCTYPE
声明的时候,用这两个都行DOCTYPE
声明的时候,只能用 document.documentElement.scrollTop
DOCTYPE
声明的时候,用 document.body.scrollTop
DOCTYPE
声明的时候,用 document.documentElement.scrollTop
window.pageYOffset
案例:实现点击按钮,滚动到指定位置。
Document
回到顶部
获取页面向左滚动的距离
也是两个方法
document.body.scrollLeft
document.documentElementLeft
window.onscroll = function () {
console.log(document.body.scrollLeft)
console.log(document.documentElement.scrollLeft)
}
两个之间的区别和之前的 scrollTop
一样
window中的方法scrollTo可以实现滚动到指定位置
语法:window.scrollTo(x,y)
window.scrollTo(0,0)
(也可以用对象,其中x
是文档中的横轴坐标(left),y
是文档中的纵轴坐标(top)。behavior
类型 String,表示滚动行为,支持参数 smooth(平滑滚动),instant(瞬间滚动),默认值 auto) window.scrollTo({top:‘向上滚动的距离’,left:‘向左滚动的距离’,behavior:‘smooth’})
一.解释:
1.浏览器专门开辟了一个空间
2.用于存储一些数据,这些数据都是字符串格式
3.同一个网站存储的数据,这个网站下面的所有页面都可以获取和修改
4.比如,我们使用 file:// 打开的页面都可以共享这个存储空间,file://1.html存储在本地存储或会话存储里面的内容,file://2.html也可以获取和修改
二.本地存储和会话存储的区别
== 本地存储只要不手动删除就一直存在
== 会话存储关闭浏览器就消失了
一.本地存储: window.localStorage
1.在本地存储存储一条数据
语法:window.localStorage.setItem(key,value)
key:这条数据的名称(键)
value:这条数据的值,你如果写的不是字符串,浏览器会通过toString强转再存储
window.localStorage.setItem('name','lucy')
window.localStorage.setItem('age',12)
2.修改本地存储的一条数据
由于key不能重复,再设置一次就是修改
window.localStorage.setItem('name','志强')
3.删除本地存储的一条数据
语法:window.localStorage.removeItem(key)
window.localStorage.removeItem('name')
4.获取一个键对应的本地存储值
语法:window.localStorage.getItem(key)
console.log(window.localStorage.getItem('age'))
5.清空所有本地存储的数据
window.localStorage.clear()
本地存储localStorage是一个window上的对象
console.log(window.localStorage)
会话存储:window.sessionStorage
会话存储:sessionStorage
1.设置 window.sessionStorage.setItem('username','jack')
window.sessionStorage.setItem('password','123456')
2.修改 = 重新设置 window.sessionStorage.setItem('password','admin')
3.删除 window.sessionStorage.removeItem('username')
4.获取 console.log(sessionStorage.getItem('password'))
5.清空
sessionStorage.clear()
getElementById('要获取的元素的id')
是通过标签的 id 名称来获取标签的
因为在一个页面中 id 是唯一的,所以获取到的就是一个元素
<body>
<div id="box">div>
<script>
var box = document.getElementById('box')
console.log(box) //
script>
body>
getElementsByClassName('要获取的元素集合的类名')
是用过标签的 class 名称来获取标签的
因为页面中可能有多个元素的 class 名称一样,所以获取到的是一组元素
用document调用表示在整个页面中获取
用dom元素(某个标签下)调用表示在这个dom元素中获取
哪怕你获取的 class 只有一个,那也是获取一组元素,只不过这一组中只有一个 DOM 元素而已
<body>
<div calss="box">div>
<script>
var box = document.getElementsByClassName('box')
console.log(box) // []
console.log(box[0]) //
script>
body>
getElementsByTagName('要获取的元素的标签名')
是用过标签的 标签 名称来获取标签的
因为页面中可能有多个元素的标签名称一样,所以获取到的是一组元素
用document调用表示在整个页面中获取
用dom元素(某个标签下)调用表示在这个dom元素中获取
哪怕真的只有一个这个标签名,那么也是获取一组元素,只不过这一组中只有一个 DOM 元素而已
<body>
<div>div>
<script>
var box = document.getElementsByTagName('div')
console.log(box) // []
console.log(box[0]) //
script>
body>
getElementsByClassName
一样,获取到的是一个长得很像数组的元素querySelector('要获取的元素的css选择器')
是按照选择器的方式来获取元素
也就是说,按照我们写 css 的时候的选择器来获取
用document调用表示在整个页面中获取
用dom元素(某个标签下)调用表示在这个dom元素中获取
这个方法只能获取到一个元素,并且是页面中第一个满足条件的元素
console.log(document.querySelector('div')) // 获取页面中的第一个 div 元素
console.log(docuemnt.querySelector('.box')) // 获取页面中第一个有 box 类名的元素
console.log(document.querySelector('#box')) // 获取页面中第一个 id 名为 box 的元素
注意:返回的是元素,不是集合
querySelectorAll('要获取的元素集合的css选择器')
是按照选择器的方式来获取元素
这个方法能获取到所有满足条件的元素,以一个伪数组的形式返回
用document调用表示在整个页面中获取
用dom元素(某个标签下)调用表示在这个dom元素中获取
console.log(document.querySelectorAll('div')) // 获取页面中的所有的 div 元素
console.log(docuemnt.querySelectorAll('.box')) // 获取页面中所有有 box 类名的元素
获取元素内部的 HTML 结构
<body>
<div>
<p>
<span>hellospan>
p>
div>
<script>
var div = document.querySelector('div')
console.log(div.innerHTML)
/*
hello
*/
script>
body>
设置元素的内容
<body>
<div>div>
<script>
var div = document.querySelector('div')
div.innerHTML = 'hello
'
script>
body>
获取元素内部的文本(只能获取到文本内容,获取不到 html 标签)
<body>
<div>
<p>
<span>hellospan>
p>
div>
<script>
var div = document.querySelector('div')
console.log(div.innerText) // hello
script>
body>
可以设置元素内部的文本
<body>
<div>div>
<script>
var div = document.querySelector('div')
div.innerText = 'hello
'
script>
body>
hello
当作一个文本出现在 div 元素里面,而不会把 p 解析成标签 var input = document.getElementsByTagName('input')[0]
// 获取表单元素的value值
console.log(input.value)
input.value = "我设置的value值"
// 案例:获取下拉列表被选中的值
var select = document.querySelector('select')
// 获取下拉列表被选中的值
console.log(select.value);// 2
// 设置下拉列表被选中的值
select.value = '3'
只有对标签有特殊含义的属性在js对象和标签中都有
<img src="./1.jpg" alt="xxx" title="yyy" a="1" aa="234" data-info="qf" data-my-banji="sz2209">
原生属性:有特殊含义的属性(eg:img里的src)
语法: dom.属性名 = 属性值 (操作对象的键值对方法一样)
// 操作dom元素的属性
var img = document.querySelector('img')
// 给dom对象添加键值对,这个dom对象上添加的键值对aaa没有特殊含义,只显示在dom对象上
img.aaa = "hello"
// 以对象格式输出dom元素
console.dir(img)
获取元素的某个属性(包括自定义属性)
语法:dom.getAttribute(‘标签上的属性名’)
<body>
<div a="100" class="box">div>
<script>
var div = document.querySelector('div')
console.log(div.getAttribute('a')) // 100
console.log(div.getAttribute('class')) // box
script>
body>
给元素设置一个属性(包括自定义属性)
语法:dom.setAttribute(‘标签上的属性名’,‘标签上的属性值’)
<body>
<div>div>
<script>
var div = document.querySelector('div')
div.setAttribute('a', 100)
div.setAttribute('class', 'box')
console.log(div) //
script>
body>
直接移除元素的某个属性
语法:dom.removeAttribute(‘标签上的属性名’)
<body>
<div a="100" class="box">div>
<script>
var div = document.querySelector('div')
div.removeAttribute('class')
console.log(div) //
script>
body>
html5出了新标准,如果要定义自定义属性,建议使用 data-xxx
// 1-3html5标准的标签自定义属性
// 如果自定义属性: data-xx => dom.dataset.xx
// 如果自定义属性: data-xx-yy-zz => dom.dataset.xxYyZz
// 获取
console.log(img.dataset.info)
console.log(img.dataset.myBanji)
// 设置
img.dataset.info = "xyz"
专门用来给元素添加 css 样式的
注意:添加的都是行内样式,dom.style只能获取元素的行内样式,而且获取到的是字符串
语法:dom.style.样式名称 = 样式值
<body>
<div>div>
<script>
var div = document.querySelector('div')
div.style.width = "100px"
div.style.height = "100px"
div.style.backgroundColor = "pink"
console.log(div)
//
script>
body>
我们在操作 DOM 的时候,很重要的一点就是要操作元素的 css 样式
那么在操作 css 样式的时候,我们避免不了就要获取元素的样式
之前我们说过可以用 元素.style.xxx
来获取
但是这个方法只能获取到元素 行间样式,也就是写在行内的样式
<style>
div {
width: 100px;
}
style>
<body>
<div style="height: 100px;">
<p>我是一个 p 标签p>
div>
<script>
var oDiv = document.querySelector('div')
console.log(oDiv.style.height) // 100px
console.log(oDIv.style.width) // ''
script>
body>
不管是外链式还是内嵌式,我们都获取不到该元素的样式
这里我们就要使用方法来获取了 getComputedStyle 和 currentStyle
这两个方法的作用是一样的,只不过一个在 非 IE 浏览器,一个在 IE 浏览器
获取元素的非行内样式
语法:window.getComputedStyle(元素, null).要获取的属性
返回值是一个样式值,是字符串
<style>
div {
width: 100px;
}
style>
<body>
<div style="height: 100px;">
<p>我是一个 p 标签p>
div>
<script>
var oDiv = document.querySelector('div')
console.log(window.getComputedStyle(oDiv).width) // 100px
console.log(window.getComputedStyle(oDiv).height) // 100px
script>
body>
专门用来操作元素的 类名的
<body>
<div class="box">div>
<script>
var div = document.querySelector('div')
console.log(div.className) // box
script>
body>
也可以设置元素的类名,不过是全覆盖式的操作
<body>
<div class="box">div>
<script>
var div = document.querySelector('div')
div.className = 'test'
console.log(div) //
script>
body>
是一个只读属性,返回一个元素的类属性集合
添加类名语法: 元素.classList.add(‘要添加的类名1’,‘要添加的类名2’,…)
删除类名语法: 元素.classList.rmeove(‘要删除的类名1’,‘要删除的类名2’,…)
切换类名语法: 元素.classList.toggle(‘要切换的类名’),注意:有则移除,无则添加
判断是否包含某个类名语法: 元素.classList.contains(‘要判断的类名’)
const div = document.createElement('div');
div.className = 'foo';
// 初始状态:
// 使用 classList API 移除、添加类值
div.classList.remove("foo");
div.classList.add("anotherclass");
//
// 如果 visible 类值已存在,则移除它,否则添加它
div.classList.toggle("visible");
// i<10为true才执行toggle类名visible
div.classList.toggle("visible", i < 10 );
console.log(div.classList.contains("foo"));
// 添加或移除多个类值
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");
// 将类值 "foo" 替换成 "bar"
div.classList.replace("foo", "bar");
内容宽高 + padding宽高 + border宽高
的和console.log(box.getBoundingClientRect())
总结:元素的尺寸和偏移量,和鼠标没有关系
offsetWidth 获取元素的宽度 = content+padding+border
offsetHeight 获取元素的宽度 = content+padding+border
clientWidth 获取元素的宽度 = content+padding
clientHeight 获取元素的宽度 = content+padding
clientLeft 获取元素的左边框宽度
clientTop 获取元素的上边框宽度
offsetLeft 获取元素左侧偏移量
offsetTop 获取元素上侧偏移量
createElement
:用于创建一个元素节点
// 创建一个 div 元素节点
var oDiv = document.createElement('div')
console.log(oDiv) //
createTextNode
:用于创建一个文本节点
// 创建一个文本节点
var oText = document.createTextNode('我是一个文本')
console.log(oText) // "我是一个文本"
appendChild
:是向一个元素节点的末尾追加一个节点
语法: 父节点.appendChild(要插入的子节点)
// 创建一个 div 元素节点
var oDiv = document.createElement('div')
var oText = document.createTextNode('我是一个文本')
// 向 div 中追加一个文本节点
oDiv.appendChild(oText)
console.log(oDiv) // 我是一个文本
insertBefore
:向某一个节点前插入一个节点
语法: 父节点.insertBefore(要插入的节点,插入在哪一个节点的前面)
语法: 父节点.insertBefore(要插入的节点,null)
等价于把要插入的节点插入到父元素的末尾
第二个参数不写,作用与append.child一样
<body>
<div>
<p>我是一个 p 标签p>
div>
<script>
var oDiv = document.querySelector('div')
var oP = oDiv.querySelector('p')
// 创建一个元素节点
var oSpan = document.createElement('span')
// 将这个元素节点添加到 div 下的 p 的前面
oDiv.insertBefore(oSpan, oP)
console.log(oDiv)
/*
我是一个 p 标签
*/
script>
body>
removeChild
:移除某一节点下的某一个节点
语法:父节点.removeChild(要移除的字节点)
<body>
<div>
<p>我是一个 p 标签p>
div>
<script>
var oDiv = document.querySelector('div')
var oP = oDiv.querySelector('p')
// 移除 div 下面的 p 标签
oDiv.removeChild(oP)
console.log(oDiv) //
script>
body>
remove() 自杀,移除自己
语法: 要移除的元素节点.remove()
删除结点的方法总结:
1.父节点.removeChild(要移除的子节点) - 删除子节点
2.要移除的元素节点.remove() - 删除自己
3.元素.innerHTML = '' - 清空自己的所有内容
replaceChild
:将页面中的某一个节点替换掉
语法: 父节点.replaceChild(新节点,旧节点)
<body>
<div>
<p>我是一个 p 标签p>
div>
<script>
var oDiv = document.querySelector('div')
var oP = oDiv.querySelector('p')
// 创建一个 span 节点
var oSpan = document.createElement('span')
// 向 span 元素中加点文字
oSpan.innerHTML = '我是新创建的 span 标签'
// 用创建的 span 标签替换原先 div 下的 p 标签
oDiv.replaceChild(oSpan, oP)
console.log(oDiv)
/*
我是新创建的 span 标签
*/
script>
body>
onclick
/ onload
/ onscroll
/ …一个事件由什么东西组成
var oDiv = document.querySelector('div')
oDiv.onclick = function () {}
// 谁来触发事件 => oDiv => 这个事件的事件源就是 oDiv
// 触发什么事件 => onclick => 这个事件类型就是 click
// 触发之后做什么 => function () {} => 这个事件的处理函数
var oDiv = document.querySelector('div')
oDiv.onclick = function () {
console.log('你点击了 div')
}
我们现在给一个注册事件都是使用 onxxx
的方式,这是dom0级的事件绑定方式
这个方式不是很好,只能给一个元素注册一个事件
一旦写了第二个事件,那么第一个就被覆盖了
oDiv.onclick = function () {
console.log('我是第一个事件')
}
oDiv.onclick = function () {
console.log('我是第二个事件')
}
我们还有一种dom2级的事件监听的方式去给元素绑定事件
使用 addEventListener
的方式添加
attachEvent
addEventListener
: 非 IE 6 7 8 下使用
语法: 元素.addEventListener('事件类型', 事件处理函数, 冒泡还是捕获)
(一般第三个参数默认是)
oDiv.addEventListener('click', function () {
console.log('我是第一个事件')
}, false)
oDiv.addEventListener('click', function () {
console.log('我是第二个事件')
}, false)
我是第一个事件
再打印 我是第二个事件
attachEvent
:IE6 7 8 下使用
语法: 元素.attachEvent('事件类型', 事件处理函数)
oDiv.attachEvent('onclick', function () {
console.log('我是第一个事件')
})
oDiv.attachEvent('onclick', function () {
console.log('我是第二个事件')
})
我是第二个事件
再打印 我是第一个事件
addEventListener
: 不用写 onattachEvent
: 要写 onaddEventListener
: 一般是三个常用参数attachEvent
: 两个参数addEventListener
: 顺序注册,顺序执行attachEvent
: 顺序注册,倒叙执行addEventListener
: 非 IE 7 8 的浏览器attachEvent
: IE 7 8 浏览器语法:dom2级: 元素.removeEventListener('事件类型', 事件处理函数, 冒泡还是捕获)
dom0级:同类事件会覆盖 语法:元素.onxxxx = null
默认是冒泡
用于接触指定元素的事件监听器
元素.dispatchEvent(事件对象)
// 创建一个click事件对象
var event = new Event('click');
box.addEventListener('click',function(){
alert(1)
})
box.onclick = function(){
alert(2)
}
setTimeout(function(){
// 派发点击事件
box.dispatchEvent(event)
},2000)
什么是事件对象?
就是当你触发了一个事件以后,对该事件的一些描述信息
例如:
每一个事件都会有一个对应的对象来描述这些信息,我们就把这个对象叫做 事件对象
浏览器给了我们一个 黑盒子,叫做 window.event
,就是对事件信息的所有描述
0,0
位置,那么你得到的这个事件对象里面对应的就会有这个点位的属性10, 10
位置,那么你得到的这个事件对象里面对应的就会有这个点位的属性oDiv.onclick = function () {
console.log(window.event.X轴坐标点信息)
console.log(window.event.Y轴坐标点信息)
}
这个玩意很好用,但是一般来说,好用的东西就会有 兼容性问题
在 IE低版本
里面这个东西好用,但是在 高版本IE
和 Chrome
里面不好使了
我们就得用另一种方式来获取 事件对象
在每一个事件处理函数的行参位置,默认第一个就是 事件对象
oDiv.onclick = function (e) {
// e 就是和 IE 的 window.event 一样的东西
console.log(e.X轴坐标点信息)
console.log(e.Y轴坐标点信息)
}
综上所述,我们以后在每一个事件里面,想获取事件对象的时候,都用兼容写法
oDiv.onclick = function (e) {
e = e || window.event
console.log(e.X轴坐标点信息)
console.log(e.Y轴坐标点信息)
}
offsetX
和 offsetY
是相对于你点击的元素的边框内侧开始计算(是点击的位置以被点击的元素为参考的坐标)
<style>
* {
margin: 0;
padding: 0;
}
div {
width: 300px;
height: 300px;
padding: 20px;
border: 10px solid #333;
margin: 20px 0 0 30px;
}
style>
<body>
<div>div>
<script>
var oDiv = document.querySelector('div')
// 注册点击事件
oDiv.onclick = function (e) {
// 事件对象兼容写法
e = e || window.event
console.log(e.offsetX)
console.log(e.offsetY)
}
script>
body>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-opUc7JJX-1661353959192)(H:\Typora\data\TyporaPages\image-20220817200452477.png)]
clientX
和 clientY
是相对于浏览器窗口来计算的,不管你页面滚动到什么情况,都是根据窗口来计算坐标(是点击的位置以窗口可视区域为参考的坐标)
<style>
* {
margin: 0;
padding: 0;
}
body {
width: 2000px;
height: 2000px;
}
div {
width: 300px;
height: 300px;
padding: 20px;
border: 10px solid #333;
margin: 20px 0 0 30px;
}
style>
<body>
<div>div>
<script>
var oDiv = document.querySelector('div')
// 注册点击事件
oDiv.onclick = function (e) {
// 事件对象兼容写法
e = e || window.event
console.log(e.clientX)
console.log(e.clientY)
}
script>
body>
pageX
和 pageY
是相对于整个页面的坐标点,不管有没有滚动,都是相对于页面拿到的坐标点(点击位置以页面为参考的坐标,页面即你写的页面)
<style>
* {
margin: 0;
padding: 0;
}
body {
width: 2000px;
height: 2000px;
}
div {
width: 300px;
height: 300px;
padding: 20px;
border: 10px solid #333;
margin: 20px 0 0 30px;
}
style>
<body>
<div>div>
<script>
var oDiv = document.querySelector('div')
// 注册点击事件
oDiv.onclick = function (e) {
// 事件对象兼容写法
e = e || window.event
console.log(e.pageX)
console.log(e.pageY)
}
script>
body>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7IhMQ7A-1661353959193)(H:\Typora\data\TyporaPages\image-20220817200430487.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O7wswdE9-1661353959194)(H:\Typora\data\TyporaPages\image-20220816193239668.png)]
事件对象.button
来获取信息0
为鼠标左键,1
为鼠标滚轮,2
为鼠标右键load
: 页面全部资源加载完毕scroll
: 浏览器滚动的时候触发resize
: 页面大小改变click
:点击事件dblclick
:双击事件contextmenu
: 右键单击事件mousedown
:鼠标左键按下事件mouseup
:鼠标左键抬起事件mousemove
:鼠标移动mouseover
:鼠标移入事件mouseout
:鼠标移出事件mouseenter
:鼠标移入事件mouseleave
:鼠标移出事件keyup
: 键盘抬起事件keydown
: 键盘按下事件(不识别大小写,可以识别一些功能按键)keypress
: 键盘按下再抬起事件(识别大小写,不识别一些功能按键)change
: 表单内容改变事件input
: 表单内容输入事件submit
: 表单提交事件blur
:表单元素失去焦点事件focus
:表单元素获取焦点事件touchstart
: 触摸开始事件touchend
: 触摸结束事件touchmove
: 触摸移动事件刚才了解了一下鼠标事件,现在来聊聊键盘事件
我们在键盘事件里面最主要的就是要做两个事情
我们先要明确一个问题,就是是不是所有元素都可以绑定键盘事件
document
来绑定键盘事件document.onkeyup = function () { // code.. }
oInput.onkeyup = function () { // code.. }
我们的键盘上每一个按键都有一个自己独立的编码
我们就是靠这个编码来确定我们按下的是哪个按键的
我们通过 事件对象.keyCode
或者 事件对象.which
来获取
为什么要有两个呢,是因为 FireFox2.0 不支持 keycode
所以要用 which
document.keyup = function (e) {
// 事件对象的兼容写法
e = e || window.event
// 获取键盘码的兼容写法
var keyCode = e.keyCode || e.which
console.log(keyCode)
}
组合案件最主要的就是 alt
/ shift
/ ctrl
/ win
三个按键
在我点击某一个按键的时候判断一下这三个键有没有按下,有就是组合了,没有就是没有组合
事件对象里面也为我们提供了三个属性
altKey
:alt 键按下得到 true,否则得到 falseshiftKey
:shift 键按下得到 true,否则得到 falsectrlKey
:ctrl 键按下得到 true,否则得到 falsemetaKey
:win 键按下得到 true,否则得到 false我们就可以通过这三个属性来判断是否按下了
document.onkeyup = function (e) {
e = e || window.event
keyCode = e.keyCode || e.which
if (e.altKey && keyCode === 65) {
console.log('你同时按下了 alt 和 a')
}
}
拓展:事件处理函数是异步的(代码有同步和异步,异步: 定时器的回调函数,事件处理函数;同步: 非异步就是同步)异步的意思就是,先取号排队,再执行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gnukEfRz-1661353959194)(H:\Typora\data\TyporaPages\image-20220817200603263.png)]
好处:可以减少事件绑定的次数,可以给动态生成的元素绑定事件
书写事件委托:元素的事件只能委托给html结构上的父级或者父级的父级的同样的事件上,父级或者父级的父级必须是存在的
点击子元素的时候,不管子元素有没有点击事件,只要父元素有点击事件,那么就可以触发父元素的点击事件
<body>
<ul>
<li>1li>
<li>2li>
<li>3li>
ul>
<script>
var oUl = docuemnt.querySelector('ul')
oUl.addEventListener('click', function (e) {
console.log('我是 ul 的点击事件,我被触发了')
})
script>
body>
target 这个属性是事件对象里面的属性,表示你点击的目标
当你触发点击事件的时候,你点击在哪个元素上,target 就是哪个元素
这个 target 也不兼容,在 IE 下要使用 srcElement
<body>
<ul>
<li>1li>
<li>2li>
<li>3li>
ul>
<script>
var oUl = docuemnt.querySelector('ul')
oUl.addEventListener('click', function (e) {
e = e || window.event
var target = e.target || e.srcElement
console.log(target)
})
script>
body>
这个时候,当我们点击 li 的时候,也可以触发 ul 的点事件
并且在事件内不,我们也可以拿到你点击的到底是 ul 还是 li
这个时候,我们就可以把 li 的事件委托给 ul 来做
<body>
<ul>
<li>1li>
<li>2li>
<li>3li>
ul>
<script>
var oUl = docuemnt.querySelector('ul')
oUl.addEventListener('click', function (e) {
e = e || window.event
var target = e.target || e.srcElement
// 判断你点击的是 li
if (target.nodeName.toUpperCase === 'LI') {
// 确定点击的是 li
// 因为当你点击在 ul 上面的时候,nodeName 应该是 'UL'
// 去做点击 li 的时候该做的事情了
console.log('我是 li,我被点击了')
}
})
script>
body>
有的时候,我们不希望浏览器执行默认事件
我们有两个方法来阻止默认事件
e.preventDefault()
: 非 IE 使用(标准浏览器)e.returnValue = false
:IE 使用我们阻止默认事件的时候也要写一个兼容的写法
<a href="https://www.baidu.com">点击我试试a>
<script>
var oA = document.querySelector('a')
a.addEventListener('click', function (e) {
e = e || window.event
console.log(this.href)
e.preventDefault ? e.preventDefault() : e.returnValue = false
})
script>
第一套: mouseover mouseout (不建议使用)
和普通事件一样,会冒泡
当一个元素移入,另一个元素移出事件触发
eg:
<style>
.outer{
width: 400px;
height: 400px;
background-color: brown;
margin:50px auto;
position: relative;
}
.middle{
width: 300px;
height: 300px;
position: absolute;
top:50px;
left:50px;
background-color: pink;
}
style>
head>
<body>
<div class="outer">
<div class="middle">
div>
div>
var middle = document.querySelector('.middle')
var outer = document.querySelector('.outer')
middle.onmouseover = function(){
console.log('middle 鼠标移入')
}
middle.onmouseout = function(){
console.log('middle 鼠标移出')
}
outer.onmouseover = function(e){
console.log(e)
console.log('outer 鼠标移入')
}
outer.onmouseout = function(){
console.log('outer 鼠标移出')
}
第二套: mouseenter mouseleave (推荐使用)
和普通事件不一样,天生不会冒泡
var middle = document.querySelector('.middle')
var outer = document.querySelector('.outer')
middle.addEventListener('mouseenter',function(){
console.log('middle 移入')
})
middle.addEventListener('mouseleave',function(){
console.log('middle 移出')
})
outer.addEventListener('mouseenter',function(e){
console.log(e)
console.log('outer 移入')
})
outer.addEventListener('mouseleave',function(){
console.log('outer 移出')
})
json
是一种文件的格式,在各种语言中都识别
json
是一种特殊的字符串格式,本质是一个字符串
需要传递数据的时候可以把数据转成json字符串格式再进行传递
凡是字符串使用双引号,对象和数组里面的值可以是数值类型,字符串,对象,布尔,null,undefined,数组
var jsonObj = '{ "name": "Jack", "age": 18, "gender": "男" }'
var jsonArr = '[{ "name": "Jack", "age": 18, "gender": "男" }, { "name": "Jack", "age": 18, "gender": "男" }, { "name": "Jack", "age": 18, "gender": "男" }]'
就是对象内部的 key
和 value
都用双引号包裹的字符串(必须是双引号)
json.parse
是将json字符串转成js的对象或者数组 前端接收后端给我们的数据json.stringify
是将 js 的对象或者数组转换成为 json 格式的字符串 前端要把数据给后端JSON.parse
是将 json 格式的字符串转换为 js 的对象或者数组
语法: JSON.parse(json字符串)
返回值: js对象或者数组
var jsonObj = '{ "name": "Jack", "age": 18, "gender": "男" }'
var jsonArr = '[{ "name": "Jack", "age": 18, "gender": "男" }, { "name": "Jack", "age": 18, "gender": "男" }, { "name": "Jack", "age": 18, "gender": "男" }]'
var obj = JSON.parse(jsonStr)
var arr = JSON.parse(jsonArr)
console.log(obj)
console.log(arr)
obj
就是我们 js 的对象arr
就是我们 js 的数组JSON.parse
是要将 js 的对象或者数组转换成为 json 格式的字符串
语法: JSON.stringify(js对象或者数组)
返回值: json格式的字符串
var obj = {
name: 'Jack',
age: 18,
gender: '男'
}
var arr = [
{
name: 'Jack',
age: 18,
gender: '男'
},
{
name: 'Jack',
age: 18,
gender: '男'
},
{
name: 'Jack',
age: 18,
gender: '男'
}
]
var jsonObj = JSON.stringify(obj)
var jsonArr = JSON.stringify(arr)
console.log(jsonObj)
console.log(jsonArr)
jsonObj
就是 json 格式的对象字符串jsonArr
就是 json 格式的数组字符串总结:
每一个函数内部都有一个关键字是 this
可以让我们直接使用的
重点: 函数内部的 this 只和函数的调用方式有关系,和函数的定义方式没有关系
函数内部的 this 指向谁,取决于函数的调用方式
全局定义的函数直接调用,this => window
function fn() {
console.log(this)
}
fn()
// 此时 this 指向 window
对象内部的方法调用,this => 调用者
var obj = {
fn: function () {
console.log(this)
}
}
obj.fn()
// 此时 this 指向 obj
定时器的处理函数,this => window
setTimeout(function () {
console.log(this)
}, 0)
// 此时定时器处理函数里面的 this 指向 window
事件处理函数,this => 事件源
div.onclick = function () {
console.log(this)
}
// 当你点击 div 的时候,this 指向 div
自调用函数,this => window
(function () {
console.log(this)
})()
// 此时 this 指向 window
call
方法是附加在函数调用后面使用,可以忽略函数本身的 this 指向
语法: 函数名.call(指定this指向,要给函数传递的参数1,要给函数传递的参数2, ...)
如果指定this的指向为null就是不改变this的指向
var obj = { name: 'Jack' }
function fn(a, b) {
console.log(this)
console.log(a)
console.log(b)
}
fn(1, 2)
fn.call(obj, 1, 2)
fn()
的时候,函数内部的 this 指向 windowfn.call(obj, 1, 2)
的时候,函数内部的 this 就指向了 obj 这个对象apply
方法是附加在函数调用后面使用,可以忽略函数本身的 this 指向
语法: 函数名.apply(要改变的 this 指向,[要给函数传递的参数1, 要给函数传递的参数2, ...])
如果指定this的指向为null就是不改变this的指向
var obj = { name: 'Jack' }
function fn(a, b) {
console.log(this)
console.log(a)
console.log(b)
}
fn(1, 2)
fn.call(obj, [1, 2])
fn()
的时候,函数内部的 this 指向 windowfn.apply(obj, [1, 2])
的时候,函数内部的 this 就指向了 obj 这个对象bind
方法是附加在函数调用后面使用,可以忽略函数本身的 this 指向
和 call / apply 有一些不一样,就是不会立即执行函数,而是返回一个已经改变了 this 指向的函数
语法: var 新函数 = 旧函数名.bind(要改变的 this 指向); newFn(传递参数)
var obj = { name: 'Jack' }
function fn(a, b) {
console.log(this)
console.log(a)
console.log(b)
}
fn(1, 2)
var newFn = fn.bind(obj)
newFn(1, 2)
fn(1, 2)
的时候 this 指向 windownewFn(1, 2)
的时候执行的是一个和 fn 一摸一样的函数,只不过里面的 this 指向改成了 obj案例:求数组最大值
var arr = [213,2130,3,32,4,32,4,32,213,213,3,4,32,43];
// 回忆求最大值: Math.max(213,213,3,32,4,32,4,32,213,213,3,4,32,43)
console.log(Math.max.apply(null,arr))
我们以前都是使用 var
关键字来声明变量的
在 ES6 的时候,多了两个关键字 let
和 const
,也是用来声明变量的
只不过和 var 有一些区别
let
和 const
不允许重复声明变量// 使用 var 的时候重复声明变量是没问题的,只不过就是后面会把前面覆盖掉
var num = 100
var num = 200
// 使用 let 重复声明变量的时候就会报错了
let num = 100
let num = 200 // 这里就会报错了
// 使用 const 重复声明变量的时候就会报错
const num = 100
const num = 200 // 这里就会报错了
let
和 const
声明的变量不会在预解析的时候解析(也就是没有变量提升)
// 因为预解析(变量提升)的原因,在前面是有这个变量的,只不过没有赋值
console.log(num) // undefined
var num = 100
// 因为 let 不会进行预解析(变量提升),所以直接报错了
console.log(num) // undefined
let num = 100
// 因为 const 不会进行预解析(变量提升),所以直接报错了
console.log(num) // undefined
const num = 100
let
和 const
声明的变量会被所有代码块限制作用范围
// var 声明的变量只有函数能限制其作用域,其他的不能限制
if (true) {
var num = 100
}
console.log(num) // 100
// let 声明的变量,除了函数可以限制,所有的代码块都可以限制其作用域(if/while/for/...)
if (true) {
let num = 100
console.log(num) // 100
}
console.log(num) // 报错
// const 声明的变量,除了函数可以限制,所有的代码块都可以限制其作用域(if/while/for/...)
if (true) {
const num = 100
console.log(num) // 100
}
console.log(num) // 报错
let
和 const
的区别
let
声明的变量的值可以改变,const
声明的变量的值不可以改变(基于上面的情况const定义的变量不能重复赋值,所以也把const定义的变量叫做常量)
let num = 100
num = 200
console.log(num) // 200
const num = 100
num = 200 // 这里就会报错了,因为 const 声明的变量值不可以改变(我们也叫做常量)
let
声明的时候可以不赋值,const
声明的时候必须赋值
let num
num = 100
console.log(num) // 100
const num // 这里就会报错了,因为 const 声明的时候必须赋值
箭头函数是 ES6 里面一个简写函数的语法方式
重点: 箭头函数只能简写函数表达式(赋值式函数),不能简写声明式函数
function fn() {} // 不能简写
const fun = function () {} // 可以简写
const obj = {
fn: function () {} // 可以简写
}
语法: (函数的形参1,形参2,...) => { 函数体内要执行的代码 }
原来函数怎么调用,现在也怎么调用,就是:函数名()
const fn = function (a, b) {
console.log(a)
console.log(b)
}
// 可以使用箭头函数写成
const fun = (a, b) => {
console.log(a)
console.log(b)
}
const obj = {
fn: function (a, b) {
console.log(a)
console.log(b)
}
}
// 可以使用箭头函数写成
const obj2 = {
fn: (a, b) => {
console.log(a)
console.log(b)
}
}
只能简写赋值式函数
箭头函数内部没有 this,箭头函数的 this 是上下文的 this
// 在箭头函数定义的位置往上数,这一行是可以打印出 this 的
// 因为这里的 this 是 window
// 所以箭头函数内部的 this 就是 window
const obj = {
fn: function () {
console.log(this)
},
// 这个位置是箭头函数的上一行,但是不能打印出 this
fun: () => {
// 箭头函数内部的 this 是书写箭头函数的上一行一个可以打印出 this 的位置
console.log(this)
}
}
obj.fn()
obj.fun()
箭头函数内部没有 arguments
这个参数集合
const obj = {
fn: function () {
console.log(arguments)
},
fun: () => {
console.log(arguments)
}
}
obj.fn(1, 2, 3) // 会打印一个伪数组 [1, 2, 3]
obj.fun(1, 2, 3) // 会直接报错
函数的行参只有一个的时候可以不写 ()
其余情况必须写
const obj = {
fn: () => {
console.log('没有参数,必须写小括号')
},
fn2: a => {
console.log('一个行参,可以不写小括号')
},
fn3: (a, b) => {
console.log('两个或两个以上参数,必须写小括号')
}
}
函数体只有一行代码的时候,可以不写 {}
,并且会自动 return
const obj = {
fn: a => {
return a + 10
},
fun: a => a + 10
}
console.log(fn(10)) // 20
console.log(fun(10)) // 20
我们在定义函数的时候,有的时候需要一个默认值出现
就是当我不传递参数的时候,使用默认值,传递参数了就使用传递的参数
如果形参有多个,其中不是所有的都有默认值,有默认值的要放在形参的最后面
语法:
== function fn(形参1=默认值,形参2=默认值){}
== (形参1=默认值,形参2=默认值)=>{}
注意
== 如果箭头函数只有一个形参,但是设置了默认值,就不能省略()
function fn(a) {
a = a || 10
console.log(a)
}
fn() // 不传递参数的时候,函数内部的 a 就是 10
fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
function fn(a = 10) {
console.log(a)
}
fn() // 不传递参数的时候,函数内部的 a 就是 10
fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
const fn = (a = 10) => {
console.log(a)
}
fn() // 不传递参数的时候,函数内部的 a 就是 10
fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
快速的从对象中获取成员
{} 是专门用来解构对象使用的
// ES5 的方法向得到对象中的成员
const obj = {
name: 'Jack',
age: 18,
gender: '男'
}
let name = obj.name
let age = obj.age
let gender = obj.gender
// 解构赋值的方式从对象中获取成员
const obj = {
name: 'Jack',
age: 18,
gender: '男'
}
// 前面的 {} 表示我要从 obj 这个对象中获取成员了
// name age gender 都得是 obj 中有的成员
// obj 必须是一个对象
let { name, age, gender } = obj
快速的从数组中获取成员
[] 是专门用来解构数组使用的
// ES5 的方式从数组中获取成员
const arr = ['Jack', 'Rose', 'Tom']
let a = arr[0]
let b = arr[1]
let c = arr[2]
// 使用解构赋值的方式从数组中获取成员
const arr = ['Jack', 'Rose', 'Tom']
// 前面的 [] 表示要从 arr 这个数组中获取成员了
// a b c 分别对应这数组中的索引 0 1 2
// arr 必须是一个数组
let [a, b, c] = arr
{}
是专门解构对象使用的[]
是专门解构数组使用的ES5 中我们表示字符串的时候使用 ''
或者 ""
在 ES6 中,我们还有一个东西可以表示字符串,就是 ``(反引号)
let str = `hello world`
console.log(typeof str) // string
和单引号好友双引号的区别
反引号可以换行书写
// 这个单引号或者双引号不能换行,换行就会报错了
let str = 'hello world'
// 下面这个就报错了
let str2 = 'hello
world'
let str = `
hello
world
`
console.log(str) // 是可以使用的
反引号可以直接在字符串里面拼接变量
// ES5 需要字符串拼接变量的时候
let num = 100
let str = 'hello' + num + 'world' + num
console.log(str) // hello100world100
// 直接写在字符串里面不好使
let str2 = 'hellonumworldnum'
console.log(str2) // hellonumworldnum
// 模版字符串拼接变量
let num = 100
let str = `hello${num}world${num}`
console.log(str) // hello100world100
${}
就是用来书写变量的位置ES6 里面号新添加了一个运算符 ...
,叫做展开运算符
1.可以合并数组
2.可以合并对象
3.可以传递函数实参
4.可以展示数组
作用是把数组展开
let arr = [1, 2, 3, 4, 5]
console.log(...arr) // 1 2 3 4 5
合并数组的时候可以使用
let arr = [1, 2, 3, 4]
let arr2 = [...arr, 5]
console.log(arr2)
也可以合并对象使用
let obj = {
name: 'Jack',
age: 18
}
let obj2 = {
...obj,
gender: '男'
}
console.log(obj2)
在函数传递参数的时候也可以使用
let arr = [1, 2, 3]
function fn(a, b, c) {
console.log(a)
console.log(b)
console.log(c)
}
fn(...arr)
// 等价于 fn(1, 2, 3)
var mySet = new Set(['a','b']);
mySet.size;//返回set中元素的个数
mySet.add('foo')
// 在Set对象尾部添加一个元素
mySet.clear()
// 移除Set对象内的所有元素。
mySet.delete('foo')
// 移除Set中与这个值相等的元素,返回一个布尔值
mySet.has(value)
// 返回一个布尔值,表示该值在Set中存在与否。
案例: 数组去重
var arr = [23,421,4,32,12,3,213,23,6,243,32,432,4];
// 定义一个set类型,参数就是这个arr
var newSet = new Set(arr);
var result = [...newSet];
console.log(result)
let myMap = new Map( [["key1", "value1"], ["key2", "value2"]]);
let keyObj = {};
let keyFunc = function() {};
let keyString = 'a string';
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
myMap.size; // 3
// 读取值
myMap.get(keyString); // "和键'a string'关联的值"
myMap.get(keyObj); // "和键keyObj关联的值"
myMap.get(keyFunc); // "和键keyFunc关联的值"
myMap.get('a string'); // "和键'a string'关联的值"
// 因为keyString === 'a string'
myMap.get({}); // undefined, 因为keyObj !== {}
myMap.get(function() {}); // undefined, 因为keyFunc !== function () {}
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
// array1里面有几个元素,{}里面的代码就执行几次
// element就是每次循环的值
// array1就是要遍历的数据
console.log(element);
}
// a
// b
// c
public.js
// 书写函数rand,求m和n之间的随机整数
export function rand(m,n){
var min = Math.min(m,n)
var max = Math.max(m,n)
min = Math.ceil(min)
max = Math.floor(max)
return min+Math.floor(Math.random()*(max-min+1));
}
// 编写一个函数,可以返回一个随机颜色#21321a
export function getColor(){
var color = '#'
for(var i=0;i<6;i++){
var num = Math.floor(Math.random()*16);
var str = num.toString(16)
color += str; // color = color+str;
}
return color;
}
console.log('public.js执行了')
// 如果有多个变量要给引入的文件使用,在要导出的变量前面加export
正则表达式,又名 “规则表达式”,简称正则
由我们自己来书写 “规则”,专门用来检测 字符串 是否符合 “规则” 使用的
我们使用一些特殊的字符或者符号定义一个 “规则公式”,然后用我们定义好的 “规则公式” 去检测字符串是不是合格
var reg = /\d+/
// 字符串中是否包含一个以上的数字
var str1 = '123'
var str2 = 'abc'
console.log(reg.test(str1)) // true
console.log(reg.test(str2)) // false
reg
就是定制好的规则str1
这个字符串的时候,符合规则str2
这个字符串的时候,不符合规则//
中间的东西,叫做正则表达式,比如 /abcdefg/
// 下面就是字面量创建一个正则表达式,表示要求字符串里面要有abcdefg
var reg = /abcdefg/
// 下面就是构造函数创建一个正则表达式,表示要求字符串里面要有abcdefg
var reg = new RegExp('abcdefg')
console.log(reg) // /abcdefg/
.
: 匹配非换行的任意字符
\
: 转译符号,把有意义的 符号 转换成没有意义的 字符,把没有意义的 字符 转换成有意义的 符号
\s
: 匹配空白字符(空格/制表符/…)
\S
: 匹配非空白字符
\d
: 匹配数字
\D
: 匹配非数字
\w
: 匹配数字字母下划线
\W
: 匹配非数字字母下划线
有了元字符我们就可以简单的制定一些规则了
var reg = /\s/
var str = 'a b'
var str2 = 'ab'
console.log(reg.test(str)) // true
console.log(reg.test(str2)) // false
var reg = /\d/
var str = 'abc1'
var str2 = 'abc'
console.log(reg.test(str)) // true
console.log(reg.test(str2)) // false
var reg = /\w/
var str = 'a1'
var str2 = '#@$'
console.log(reg.test(str)) // true
console.log(reg.test(str2)) // false
*
: 前一个内容重复至少 0 次,也就是可以出现 0 ~ 正无穷 次
+
: 前一个内容重复至少 1 次,也就是可以出现 1 ~ 正无穷 次
?
: 前一个内容重复 0 或者 1 次,也就是可以出现 0 ~ 1 次
{n}
: 前一个内容重复 n 次,也就是必须出现 n 次
{n,}
: 前一个内容至少出现 n 次,也就是可以出现 n ~ 正无穷 次
{n,m}
: 前一个内容至少出现 n 次至多出现 m 次,也就是出现 n ~ m 次
限定符是配合元字符使用的
// 下面正则表示验证数字出现 0 ~ 正无穷次都可以
var reg = /\d*/
var str = 'abc'
var str2 = 'abc1'
var str3 = 'abc123'
console.log(reg.test(str)) // true
console.log(reg.test(str2)) // true
console.log(reg.test(str3)) // true
// 下面正则表示验证数字出现 1 ~ 正无穷次都可以
var reg = /\d+/
var str = 'abc'
var str2 = 'abc1'
var str3 = 'abc123'
console.log(reg.test(str)) // false
console.log(reg.test(str2)) // true
console.log(reg.test(str3)) // true
// 下面正则表示验证数字出现 0 ~ 1 次都可以
var reg = /\d?/
var str = 'abc'
var str2 = 'abc1'
console.log(reg.test(str)) // true
console.log(reg.test(str2)) // true
// 下面正则表示验证数字必须出现 3 次
var reg = /\d{3}/
var str = 'abc'
var str2 = 'abc1'
var str3 = 'abc123'
console.log(reg.test(str)) // false
console.log(reg.test(str2)) // false
console.log(reg.test(str3)) // true
// 下面正则表示验证数字出现 3 ~ 正无穷次
var reg = /\d{3,}/
var str = 'abc'
var str2 = 'abc1'
var str3 = 'abc123'
var str4 = 'abcd1234567'
console.log(reg.test(str)) // false
console.log(reg.test(str2)) // false
console.log(reg.test(str3)) // true
console.log(reg.test(str4)) // true
// 下面正则表示验证数字只能出现 3 ~ 5 次
var reg = /\d{3,5}/
var str = 'abc'
var str2 = 'abc1'
var str3 = 'abc123'
var str4 = 'abc12345'
console.log(reg.test(str)) // false
console.log(reg.test(str2)) // false
console.log(reg.test(str3)) // true
console.log(reg.test(str4)) // true
边界符是限定字符串的开始和结束的
^
: 表示他后面的内容必须在开头
$
: 表示他前面的内容必须在结尾
^$
:表示有且仅有他们中间的内容,也就是精确匹配
// 下面表示从开头到结尾只能有数字,并且出现 3 ~ 5 次
var reg = /^\d{3,5}$/
var str = 'abc'
var str2 = 'abc123'
var str3 = '1'
var str4 = '1234567'
var str5 = '123'
var str6 = '12345'
console.log(reg.test(str)) // false
console.log(reg.test(str2)) // false
console.log(reg.test(str3)) // false
console.log(reg.test(str4)) // false
console.log(reg.test(str5)) // true
console.log(reg.test(str6)) // true
()
: 限定一组元素
[]
: 字符集合,表示写在 []
里面的任意一个都行
[^]
: 反字符集合,表示写在 [^]
里面之外的任意一个都行
-
: 范围,比如 a-z
表示从字母 a 到字母 z 都可以
|
: 或,正则里面的或 a|b
表示字母 a 或者 b 都可以
现在我们就可以把若干符号组合在一起使用了
// 1.下面是一个简单的邮箱验证
// 非_$开头,任意字符出现至少6次,一个@符号,(163|126|qq|sina)中的任意一个,一个点,(com|cn|net)中的任意一个
var reg = /^[^_$].{6,}@(163|126|qq|sina)\.(com|cn|net)$/
//2.以字母开头,数字结尾,中间任意一个字符
let reg = /^[a-zA-Z].\d$/
let str = "h1"
let str2 = "h12"
console.log(reg.test(str));// false
console.log(reg.test(str2));// true
// 3 写出中国人姓名正则,2-4个中文
let h1 = document.querySelector('h1')
// 中文基本汉字范围是 \u4E00-\u9FA5(\u表示声明编码)
h1.innerHTML = '\u9FA5'
// 写出中国人姓名正则
let reg = /^[\u4E00-\u9FA5]{2,4}$/
let str = "lucy";
let str2 = "志强"
let str3 = "梦洁1"
console.log(reg.test(str));// false
console.log(reg.test(str2));// true
console.log(reg.test(str3));// false
i
: 表示忽略大小写
/\w/i
g
: 表示全局匹配(指的就是把全部的找出来)
/\w/g
m
: 表示多行匹配
这个 m是写在正则的最后面的
eg:/\w/m
加m是多行匹配,每行的开头都算开头,每行的结尾都算结尾
var str="This is an\n antzone good";
var reg=/an$/;
console.log(str.match(reg)); // 匹配不到
var reg2=/an$/m;
console.log(str.match(reg2)); // 能匹配到,进行了多行匹配,行的结尾也算结尾了
var reg3 = /^b/;
var str = 'test\nbbs';
console.log(str.match(reg3)); // 匹配不到
var reg4 = /^b/m;
console.log(str.match(reg4)); // 能匹配到,进行了多行匹配,行的开始也算开始了
test
是用来检测字符串是否符合我们正则的标准,符合返回true,不符合返回false
语法: 正则.test(字符串)
返回值:布尔值
console.log(/\d+/.test('123')) // true
console.log(/\d+/.test('abc')) // false
exec
是把字符串中符合条件的内容捕获出来
语法: 正则.exec(字符串)
返回值: 把字符串中符合正则要求的第一项以及一些其他信息,以数组的形式返回。没找到的话,返回null
如果用了全局匹配:多次调用会连续匹配后面的符合条件
如果没有全局匹配:只匹配第一项
var reg = /\d{3}/
var str = 'hello123world456你好789'
var res = reg.exec(str)
console.log(res)
/*
["123", index: 5, input: "hello123world456你好789", groups: undefined]
0: "123"
groups: undefined
index: 5
input: "hello123world456你好789"
length: 1
__proto__: Array(0)
*/
search
是查找字符串中是否有满足正则条件的内容
语法: 字符串.search(正则)
返回值 : 有的话返回第一个匹配的元素的索引,没有返回值-1
是否全局匹配没有区别
var reg = /\d{3}/
var str = 'hello123'
var str2 = 'hello'
console.log(str.search(reg)) // 5
console.log(str2.search(reg)) // -1
match
找到字符串中符合正则条件的内容返回
语法: 字符串.match(正则)
返回值 :
var reg = /\d{3}/
var str = 'hello123world456'
var str2 = 'hello'
console.log(str.match(reg))
// ["123", index: 5, input: "hello123wor456", groups: undefined]
console.log(str2.match(reg)) // null
var reg = /\d{3}/g
var str = 'hello123world456'
var str2 = 'hello'
console.log(str.match(reg))
// ["123", "456"]
console.log(str2.match(reg)) // null
replace
是将字符串中满足正则条件的字符串替换掉
语法: 字符串.replace(正则,要替换的字符串)
返回值 : 替换后的字符串
没有标示符g:只替换一次
有标示符g:所有符合条件的都替换掉
var reg = /\d{3}/
var str = 'hello123world456'
var str2 = 'hello'
console.log(str.replace(reg)) // hello666world456
console.log(str2.replace(reg)) // hello
var reg = /\d{3}/g
var str = 'hello123world456'
var str2 = 'hello'
console.log(str.replace(reg)) // hello666world666
console.log(str2.replace(reg)) // hello
var str = "aabab"
var reg = /a.*b/ // 贪婪
console.log(str.replace(reg,'1')); //1
var reg2 = /a.*?b/ // 懒惰
console.log(str.replace(reg2,'1'));// 1ab
// 案例:
let reg3 = /\d*/
console.log(str.replace(reg3,''));// 'hello123world'
// 最开始匹配了空字符串就已经成功了,就不会继续匹配了(因为默认认为每个字符之间都有个空字符串)
<div id="box">div>
var box = document.getElementById("box");
var t = null;
t = setInterval(function(){
//书写运动的属性
})
2.案例
<script>
/*
需求:页面一打开,div匀速从left=0运动到left=800,速度为5px
原理: 设置定时器,每隔20ms,让div的定位的left = div的原来的left + 速度
*/
// 获取运动的元素
let oDiv = document.querySelector('div');
// 设置定时器,每隔20ms
let timer = setInterval(()=>{
// 元素.offsetLeft: 元素距离最近的有定位的父元素的left(就是css中的left的值)
// div的定位的left = div的原来的left + 速度
oDiv.style.left = oDiv.offsetLeft + 5 + "px"
// 如何让元素停下来,left = 800
if(oDiv.offsetLeft===800){
// 停止定时器,停止运动
clearInterval(timer)
}
},20)
</script>
t = setInterval(function(){终止条件})
// 元素的属性值 === 目标点
if(dom.attr === target){
clearInterval(t);
}
//开启定时器,首先要记得清理定时器。
案例.
<script>
/*
需求分析
1 点击按钮button
2 div运动到800停止
*/
// 获取元素
let btn = document.querySelector('button');// 按钮
let oDiv = document.querySelector('div');// 要运动的div
let timer;
// 点击按钮button
btn.addEventListener('click',()=>{
// 要用定时器先清上次点击产生定时器
clearInterval(timer)
// div开始运动就是开启定时器
timer = setInterval(()=>{
// 判断是否到达目标
if(oDiv.offsetLeft === 800){
// 清除定时器就是不再进行下一次的20ms
// 但是本地的20ms的代码要执行完成
clearInterval(timer);
return;
}
// 每隔20ms,div的left = div原来的left+速度
oDiv.style.left = oDiv.offsetLeft + 1+"px"
},20)
})
// 用户点击按钮是不可控制的
// 分两种情况
// 1 到达目标了,用户还是要点击
// 开始运动前就判断是否到达目标,到达就停止定时器和本次的定位
// 2 没有到达目标,用户又点击了
// 点击一次开一个定时器
// 点1次 : 0 20ms 40ms 每20ms增加1px timer = 1
// 点第2次: 10 30ms 50ms 70ms 每20ms增加1px timer = 2
// 点第3次: 5 25ms 45ms 65ms 每20ms增加1px timer = 3
// 定时器的使用原则: 一个物体同时只能被一个定时器控制
// 也就是要用定时器先清定时器
一个运动的起始点其实就是当前元素的位置,我们通过API获取当前元素的位置,让这个位置作为运动的起始。
单属性运动框架:(缓动)
function move( ele , attr , target){
// 1. 关闭开启定时器;
clearInterval( ele.timer );
ele.timer = setInterval( function(){
// 2. 计算速度;
if(attr === "opacity"){
var iNow = parseInt(getComputedStyle(ele)[attr] * 100); //0 ~ 100
}else{
var iNow = parseInt(getComputedStyle(ele)[attr]); //100
}
var speed = (target - iNow) / 10;
// 3. 速度取整;
if(speed > 0){
speed = Math.ceil(speed);
}else{
speed = Math.floor(speed);
}
if(attr === "opacity"){
ele.style[attr] = (iNow + speed) / 100 ;
}else{
ele.style[attr] = iNow + speed + "px";
}
// 4. 终止条件;
if(iNow === target){
clearInterval(ele.timer);
}
} , 50)
}
多属性缓动运动框架
function animate(dom,target,fn){
clearInterval(dom.timer);
dom.timer = setInterval(()=>{
let flag = true;
for(let attr in target){
let current
if(attr=='opacity'){
current = parseInt(getComputedStyle(dom)[attr]*100)
}else{
current = parseInt(getComputedStyle(dom)[attr]);
}
let speed = (target[attr] - current)/10
speed = speed>0?Math.ceil(speed):Math.floor(speed);
let next = current + speed;
if(attr=='opacity'){
dom.style[attr] = next/100
}else{
dom.style[attr] = next+"px";
}
if(next!=target[attr]){
flag = false
}
}
if(flag){
clearInterval(dom.timer)
if(fn){
fn()
}
}
},20)
}
Document
-
-
-
-
-
<
>
Document
js 给我们内置了一个 Object 构造函数
这个构造函数就是用来创造对象的
当 构造函数 和 new 关键字连用的时候,就可以为我们创造出一个对象
因为 js 是一个动态的语言,那么我们就可以动态的向对象中添加成员了
// 就能得到一个空对象
var o1 = new Object()
// 正常操作对象
o1.name = 'Jack'
o1.age = 18
o1.gender = '男'
直接使用字面量的形式,也就是直接写 {}
可以在写的时候就添加好成员,也可以动态的添加
// 字面量方式创建对象
var o1 = {
name: 'Jack',
age: 18,
gender: '男'
}
// 再来一个
var o2 = {}
o2.name = 'Rose'
o2.age = 20
o2.gender = '女'
先书写一个工厂函数
这个工厂函数里面可以创造出一个对象,并且给对象添加一些属性,还能把对象返回
使用这个工厂函数创造对象
// 1. 先创建一个工厂函数
function createObj() {
// 手动创建一个对象
var obj = new Object()
// 手动的向对象中添加成员
obj.name = 'Jack'
obj.age = 18
obj.gender = '男'
// 手动返回一个对象
return obj
}
// 2. 使用这个工厂函数创建对象
var o1 = createObj()
var o2 = createObj()
工厂函数需要经历三个步骤
构造函数会比工厂函数简单一下
先书写一个构造函数
在构造函数内向对象添加一些成员
使用这个构造函数创造一个对象(和 new 连用)
构造函数可以创建对象,并且创建一个带有属性和方法的对象
面向对象就是要想办法找到一个有属性和方法的对象
面向对象就是我们自己制造 构造函数 的过程
// 1. 先创造一个构造函数
function Person(name, gender) {
this.age = 18
this.name = name
this.gender = gender
}
// 2. 使用构造函数创建对象
var p1 = new Person('Jack', 'man')
var p2 = new Person('Rose', 'woman')
和普通函数一样,只不过 调用的时候要和 new 连用,不然就是一个普通函数调用
function Person() {}
var o1 = new Person() // 能得到一个空对象
var o2 = Person() // 什么也得不到,这个就是普通函数调用
首字母大写(只是为了区分你希望这个函数作为构造函数调用)
function person() {}
var o1 = new person() // 能得到一个对象
function Person() {}
var o2 = new Person() // 能得到一个对象
当调用的时候如果不需要传递参数可以不写 ()
,建议都写上
function Person() {}
var o1 = new Person() // 能得到一个空对象
var o2 = new Person // 能得到一个空对象
构造函数内部的 this,由于和 new 连用的关系,是指向当前实例对象的
function Person() {
console.log(this)
}
var o1 = new Person() // 本次调用的时候,this => o1
var o2 = new Person() // 本次调用的时候,this => o2
因为构造函数会自动返回一个对象,所以构造函数内部不要写 return
我们在使用构造函数的时候,可以通过一些代码和内容来向当前的对象中添加一些内容
function Person() {
this.name = 'Jack'
this.age = 18
}
var o1 = new Person()
var o2 = new Person()
我们在写构造函数的时候,是不是也可以添加一些方法进去呢?
function Person() {
this.name = 'Jack'
this.age = 18
this.sayHi = function () {
console.log('hello constructor')
}
}
var o1 = new Person()
var o2 = new Person()
sayHi
这个函数但是这样好不好呢?缺点在哪里?
function Person() {
this.name = 'Jack'
this.age = 18
this.sayHi = function () {
console.log('hello constructor')
}
}
// 第一次 new 的时候, Person 这个函数要执行一遍
// 执行一遍就会创造一个新的函数,并且把函数地址赋值给 this.sayHi
var o1 = new Person()
// 第二次 new 的时候, Person 这个函数要执行一遍
// 执行一遍就会创造一个新的函数,并且把函数地址赋值给 this.sayHi
var o2 = new Person()
sayHi
函数就是一个代码一模一样,功能一模一样o1.sayHi
是一个地址,o2.sayHi
是一个地址console.log(o1 === o2.sayHi)
的到的结果是 false
怎么解决这个问题呢?
每一个函数天生自带一个成员,叫做 prototype,是一个对象空间
即然每一个函数都有,构造函数也是函数,构造函数也有这个对象空间
这个 prototype
对象空间可以由函数名来访问
function Person() {}
console.log(Person.prototype) // 是一个对象
function Person() {}
Person.prototype.name = 'prototype'
Person.prototype.sayHi = function () {}
我们发现了一个叫做 prototype
的空间是和函数有关联的
并且可以向里面存储一些东西
重点: 在函数的 prototype 里面存储的内容,不是给函数使用的,是给函数的每一个实例化对象使用的
那实例化对象怎么能使用?
每一个对象都天生自带一个成员,叫做 __proto__
,是一个对象空间
即然每一个对象都有,实例化对象也是对象,那么每一个实例化对象也有这个成员
这个 __proto__
对象空间是给每一个对象使用的
当你访问一个对象中的成员的时候
__proto__
这个对象空间里面找,里面有的话就给你结果那么这个 __proto__
又指向哪里呢?
__proto__
就指向这个构造函数的 prototype
function Person() {}
var p1 = new Person()
console.log(p1.__proto__ === Person.prototype) // true
__proto__
和所属的构造函数的 prototype
是一个对象空间prototype
中添加成员__proto__
中查找prototype
中__proto__
中找function Person() {}
Person.prototype.sayHi = function () {
console.log('hello Person')
}
var p1 = new Person()
p1.sayHi()
p1
自己没有 sayHi
方法,就会去自己的 __proto__
中查找p1.__proto__
就是 Person.prototype
Person.prototype
中添加了 sayHi
方法p1.sayHi
就可以执行了到这里,当我们实例化多个对象的时候,每个对象里面都没有方法
protottype
中查找function Person() {}
Person.prototype.sayHi = function () {
console.log('hello')
}
var p1 = new Person()
var p2 = new Person()
console.log(p1.sayHi === p2.sayHi)
p1
是 Person
的一个实例p2
是 Person
的一个实例p1.__proto__
和 p2.__proto__
指向的都是 Person.prototype
p1
去调用 sayHi
方法的时候是去 Person.prototype
中找p2
去调用 sayHi
方法的时候是去 Person.prototype
中找结论
prototype
是一个对象__proto__
属性__proto__
属性又指向哪里呢?每一个对象都有一个自己所属的构造函数
比如: 数组
// 数组本身也是一个对象
var arr = []
var arr2 = new Array()
Array
比如: 函数
// 函数本身也是一个对象
var fn = function () {}
var fun = new Function()
Function
__proto__
里面也有一个成员叫做 constructor
Object
的实例化对象__proto__
指向的是 Object.prototype
Object.prototype
也是个对象,那么它的 __proto__
又指向谁呢?Object
的 js 中的顶级构造函数,我们有一句话叫 万物皆对象Object.prototype
就到顶了,Object.prototype
的 __proto__
就是 null__proto__
中找__proto__
里面没有就再去 __proto__
里面找Object.prototype
里面都没有,那么就会返回 undefiend
__proto__
有关系语法:
class 类名 {
constructor(形参1,形参2,){
}
函数名1(){
}
函数名2(){
}
}
class Person {
constructor(name,age){
// constructor就类似es5语法的构造函数
this.name = name;
this.age = age;
}
// 可以在这里定义函数,就是在原型上添加函数
sayHi(){
console.log('hello world')
}
}
// 实例化对象
let p1 = new Person('jack',18)
console.log(p1)