文章目录
一、初识JavaScript:
- JavaScript是世界上最流行的语言之一,是一种运行在客户端的脚本语言(script是脚本的意思)
- 脚本语言;不需要编译,运行过程中有js解释器(js引擎)逐行来进行解释并运行
- 现在也可以基于node.js技术进行服务器端编程
JS的作用:
- 表单的验证
- 网页特效
- 服务端开发(node.js)
- 桌面程序(Electron)
- App(Cordova)
- 控制硬件-物联网(Ruff)
- 游戏开发(cocos2d-js)
- 服务器端编程,数据交互(Ajax、Node.js)
浏览器执行JS简介:浏览器分为两部分:渲染引擎和JS引擎
渲染引擎:用来解析HTML与CSS,俗称内核,比如Chrome浏览器的blink,老版本的webkit
JS引擎:也称为JS解释器。用来读取网页中的JavaScript代码,对其处理后运行,比如Chrome浏览器V8
浏览器本身并不会执行JS代码,而是通过内置JS引擎(解释器)来执行JS代码,JS 引擎执行代码时逐行解释每一句源码(转换为机器语言),然后由计算机去执行,所以JS语言归为脚本语言,会逐行解释执行。
JS的组成:
-
1.ECMAScript:JavaScript的核心
ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript(网景工司)或JScript(微软公司),所以它可以理解为是JavaScript的一个标准,但实际上后两者是ECMA-262标准的实现和扩展。
-
2.DOM:Document Object Model(文档对象模型)
-
3.浏览器对象模型Browser Object Model(BOM)
2.*Bom window对象*
1.Bom
- 就是用js去操作浏览器和页面中的html元素
2.window对象
window对象包含了6大核心模块,分别是:
- document对象,即文档对象
- frames,即HTML自框架
- history,即页面的历史记录
- location,即当前页面的地址
- navigator,包含浏览器相关信息
- screen,用户显示屏幕相关属性
3.常用属性
1.closed,返回窗口是否已被关闭
2.location,获取当前页面的url地址
3.history,有关客户访问过的url地址
4.opener,返回对创建此窗口的窗口的引用
5.parent,返回父窗口
6.self,返回对当前窗口的引用
7.franes,返回窗口中所有命名的框架 注释:该集合是window对象的数组,每个window对象在窗口中含有一个框架。
8.status,设置窗口状态栏的文本
注释:opener和parent都可以给子窗口传递参数。不同点:opener用于超链接。而且打开的这个超链接必须要有父子关系,如果不是通过超链接打开,向父窗口传递参数时,会传不过去。
parent用于iframe框架。
3.window方法
1.window.alert(‘嘿’)显示带有一段消息和一个确认按钮的警告框。2.window.
2.window.prompt()显示可提升用户输入的对话框
3.window.confirm()现时代有一段消息以及确认按钮和取消按钮的对话框
4.window.focus()把键盘焦点给予一个窗口
5.window.blur()把键盘焦点从顶层窗口移开
6.window.colse()关闭浏览器窗口
7.window.open()打开一个新的浏览器窗口或者找一个已命名的窗口
8.window.setTimeout()在指定的毫米数后调用函数或计算表达式
9.window.clearTimeout()取消由 setTimeout() 方法设置的 timeout。
10.window.setInterval()按照指定的周期(以毫秒计)来调用函数或计算表达式
11.window.clearInteral()取消由 setInterval() 设置的 timeout
12.window.scrollBy()按照指定的像素值来滚动内容
13.window.scrollTo()把内容滚动到指定的坐标
14.window.moveBy()可相对窗口当前的坐标,移动指定的像素
15.window.moveTo()把窗口的左上角移动到指定的坐标
16.window.resizeBy()按照指定的像素,调整窗口的大小
17.window.resizeTo()把窗口的大小调整到指定的宽度和高度
18.window.print()打印当前窗口的内容
19.window.creapopup()创建一个pop-up窗口
4.screen屏幕
1.静态属性
(1)常用
1.当前分辨率 注;自己设备的分辨率
1.Rosolution r = Screen.currentResolution;
2.当前屏幕的分辨率的宽 高;r.width/r.height
2.屏幕窗口当前宽高(Game窗口的宽高),一般写代码要用Game窗口宽高调试
Screen.width/Screen.height
3.屏幕休眠模式
Screen.sleepTimeout = sleepTimeout.Nevesleep / sleepTimeout.systemsetting(不息屏/系统设置)
(2)不常用
1.运行时是否全屏模式
screen.fullscreen = true
2.窗口模式
1.独占全屏 FullSceenMode.ExclusiveFullScreen
2.全屏窗口 FullSceenMode.FullScreenWindow
3.最大化窗口 FullScreenMode.MaximizedWindow
4.窗口模式 FullSceenMode.windowed
2.静态方法
设置分辨率,一般移动设备不使用
5.history历史记录
1.history 是window上的一个对象,由来存储浏览器访问过的历史
2.用途:可以动态跳转任意一个已在历史记录中的地址
3.history方法:
1.forward() : 向后翻一页
2.back(): 回退一页
3.go(num) : num为负值时 表示回退 num为正值时表示前进
4.pushState(data, title, url): 添加一条历史记录,受同源策略限制,
5.replaceState(data, title, url) 替换当前的历史记录,受同源策略限制,
6.location地址栏信息
1.location对象
作用;location是window对象的一个属性,本身也是对象类型他的作用是是用来获取文档对象的相关信息如文档的URL地址信息
2**.location对象的属性**
1.href 获取或者设置整个URL
2.hoset 返回主机
3.port 返回端☐号如果未写返回空字符串
- pathname 返回路径
5.search 返回参数
6.hash 返回片段#后面内容常见于链接锚点
注; 其中最常用的就是(重点记) location.href和location. search
3.location对象的方法
1.location.assign() 跟href一样,可以跳转页面(也称为重定向页面)
2.location.replace() 替换当前页面,因为不记录历史,所以不能后退页面
3.location.reload() 重新加载页面,相当于刷新按钮或者f5如果参数为true强制刷新ctrl+f5
7.navigator对象
1.navigator 对象包含有关浏览器的信息。
2.查看 navigator 对象所有信息:
console.log(navigator);
8.open方法
1.window的open()方法用于导航到一个特定的URL或者打开一个新的浏览器窗口。它接收4个参数:要加载的URL,窗口名称,特性字符串和一个布尔值。以下从window.open的参数设置和返回值两部分来介绍
2.参数设置
参数一.要加载的URL(可选)
参数二.窗口名称(可选)
参数三.特性字符串(可选)
参数四.布尔值
3.window.open的返回值
window.open()方法返回一个对新窗口的引用,以便我们对新窗口进行更多的控制。如:
1.mywin= window.open(“https://www.baidu.com”,“_blank”,“height=400,width=600”,top=100,left=100,scrollbars=no)
2.mywin.close(); //调用close()函数关闭新打开的网页
3.mywin.resizeTo(500,500);//调整大小
4.mywin.moveTo0(300,300) ; //移动位置
8.事件
*1*.事件是是指用户在某事务上由于某种行为所执行的操作; (对页面元素的某种操作)
*2*.事件三大要数
1.事件源
2.事件:事件是指执行的动作
3.事件驱动程序(事件处理程序):即执行的结果
3.通过js获取元素对象,再添加事件
如:
//1.获取元素对象
var input = document.getElementbyid(“username”)
//2.注册监听
input.onclick = function(){
//alert(“hehe”)
}
9.编码
*1*.JS的三种方法1
\1. encodeURI
2.encodeURIComponent
3**.**escape
2.三种编码方法对应的解码方法分别是
编码1 .encodeURI 解码1.decodeURI
编码2 .encodeURIComponent 解码2.decodeURIComponent
编码3 .escape 解码3.unescape
3.Base64编码
Base64 就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符。使用它的主要目的,不是为了加密,而是为了不出现特殊字符,简化程序的处理。
JavaScript 原生提供两个 Base64 相关的方法。
btoa(): 任意值转为 Base64 编码
atob(): Base64 编码转为原来的值
JS初体验:
行内式JS:<input type="button" value="唐伯虎" onclick="alert('秋香姐')" />
内嵌式:<script> alert('hellow Word~!');</script>
外部JS文件<script src="my.js">此处不可写代码</script>
JS输入输出语句:
方法 | 说明 | 归属 |
---|---|---|
alert(msg) | 浏览器弹出警示框 | 浏览器 |
console.log(msg) | 浏览器控制台打印输出信息 | 浏览器 |
prompt(info) | 浏览器弹出输入框,用户可以输入 | 浏览器 |
变量:
变量就是用于存放数据的容器,我们通过变量名来获取数据,甚至数据可以修改。
本质:变量是程序在内存中申请的一块用来存放数据的空间。
**变量的使用:**1、声明变量 2、赋值
声明变量
var age;//声明一个名称为age的变量
- var是一个JS关键字,用来声明变量(variable 变量的意思)。 使用该关键字声明变量后个,计算机会自动为变量分配内存空间,不需要程序员管。
- age是程序员定义的变量名,我们通过变量名来访问内存中分配的空间。
变量的初始化:
var age =18; 声明变量并赋值,我们称之为变量的初始化。
情况 | 说明 | 结果 |
---|---|---|
var age;console.log(age); | 只声明,不赋值 | undefined |
console.log(age) | 不声明,不赋值 直接使用 | 报错 |
age = 10;console.log(age); | 不声明,只赋值 | 10 |
变量的 命名规范:
- 变量必须以字母开头
- 变量也能以 $ 和 _ 符号开头(不过我们不推荐这么做)
- 变量名称对大小写敏感(y 和 Y 是不同的变量)
- 变量可以使用短名称(比如 x 和 y),也可以使用描述性更好的名称(比如 age, sum, totalvolume)。
JS数据类型
JavaScript 变量还能保存其他数据类型,比如文本值 (name=“Bill Gates”)。
在 JavaScript 中,类似 “Bill Gates” 这样一条文本被称为字符串。
JavaScript 变量有很多种类型,但是现在,我们只关注数字和字符串。
当您向变量分配文本值时,应该用双引号或单引号包围这个值。
当您向变量赋的值是数值时,不要使用引号。如果您用引号包围数值,该值会被作为文本来处理。
数据类型:
变量的数据类型:
变量是用来存储系数值的所在处,它们有名字和数据类型。变量的数据类型决定了如何将代表这些值的位存储到计算机的内存中。JS是一种弱类型或者是说动态语言。这意味着不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。
数据类型的分类:
简单数据类型(Number,String,Boolean,Undefined ,null)
简单数据类型 | 说明 | 默认值 |
---|---|---|
Number | 数字型,包含整型值和浮点值 | 0 |
Boolean | 布尔值类型,如true、false、等价于1和0 | flase |
String | 字符串类型,如"张三"注意js里面字符串都带引号 | “” |
Undefined | var a;声明了变量a但是没有给值,此时a=undefined | undefined |
Null | var a = null; 声明了变量a为空值 | null |
数字型Number
-
alter(Number.MAX_VALUE);//1.7984834813124339759e+308
-
alter(Number.MIN_VALUE);//5e-324
-
alert(Infinity);Infinity,代表无穷大,大于任何数值
-
alert(-Infinity);-Infinity代表无穷小,小于任何数值
-
alert(NaN);NaN,Not 啊number,代表一个非数值
//isNaN()这个方法用来判断非数字类型,并且返回一个值,如果是数字返回的是false,如果不是数字返回的是true
console.log(isNaN(“房价可能”));
字符串型String:
JS可以用单引号嵌套双引号,或者用双引号嵌套单引号(外双内单,外单内双)
布尔型Boolean:
布尔型和数字进行相加的时候,true的值为1,false的值为0
console.log(true + 1);/ / 2
console.log(false + 1); / / 1
字符串转移字符:
转义符 | 解释说明 |
---|---|
\ n | 换行符,n是newline的意思 |
\ \ | 斜杠 |
\ ’ | ‘单引号 |
\ ‘’ | “双引号 |
\ t | tab缩进 |
\ b | 空格,b是blank的意思 |
数据类型的转换
方式 | 说明 | 案例 |
---|---|---|
toString() | 转成字符串 | var num=1;alert(num.toString()); |
String()强制转换 | 转成字符串 | var num=1;alert(String(num)); |
加号拼接字符串 | 和字符串拼接的结果都是字符串 | var num=1;alert(num+“字符串”); |
转换为数字型(重点)
方式 | 说明 | 案例 |
---|---|---|
parseInt(string)函数 | 将string类型转型成整数数值型 | parseInt(‘78’) |
parseFloat(string)函数 | 将string类型转型成浮点数数值型 | parseFloat(‘78.21’) |
Number()强制转换函数 | 将string类型转换成数值型 | number(‘12’) |
js隐式转换 | 利用算数运算隐式转换成数值型 | ‘12’-0 |
var num1 = prompt("请输入第一个值:") - 0;
var num2 = prompt("请输入第二个值:") - 0;
// var num = parseFloat(num1) + parseFloat(num2);
var num = num1 + num2;
// var num = Number(num1) + Number(num2);
alert("最后的解锁:\n" + num);
转换为布尔型:
方式 | 说明 | 案例 |
---|---|---|
Boolean()函数 | 其他类型转换布尔值 | Boolean(‘true’); |
代表空、否定的值会被转换为false,如’'、0、NaN、null、undefined
其余值都会转换为true
- console.log(Boolean(“”)); //false
- console.log(Boolean(0)); //false
- console.log(Boolean(NaN)); //false
- console.log(Boolean(null)); //false
- console.log(Boolean(undefined)); //false
- console.log(Boolean(“小白”)); //true
- console.log(Boolean(12)); //true
解释型语言和编译型语言:
1、 计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言才能执行程序。程序语言翻译成机器语言的工具,被称为翻译器。
JS编程语言---->翻译器------>机器语言(二进制)
翻译器翻译的方式有两种:一种是编译,另一种解释,两种方式之间的区别在于翻译的时间点不同。
编译器是在代码执行之前进行编译,生成中间代码文件
解释器是在运行时进行及时解释,并立即执行(当编译器以解释方式运行的时候,也称之为解释器)
2、标识符、关键字、保留字
标识符:就是指导开发人员为变量、属性、函数、参数取的名字。
标识符不能是关键字或保留字。
关键字:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7opM9jCl-1670505761583)(C:\Users\地狱魔爵人\Desktop\截图\2022-10\chrome_Sfe2SvwMjH.png)]
- Javascript关键字是不能作为变量名和函数名使用的。使用Javascript关键字作为变量名或函数名,会使Javascript在载入过程中出现编译错误。
- js中的关键字可用于表示控制语句的开始或结束,或者用于执行特定操作等。按照规则,关键字也是语言保留的,不能用作标识符。
保留字:
保留字就是 JavaScript 语言内部预备使用的一组名字(或称为命令)。这些名字目前还没有具体的用途,是为 JavaScript 升级版本预留备用的,建议用户不要使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uxn2bNVf-1670505761589)(C:\Users\地狱魔爵人\Desktop\截图\2022-10\chrome_L7DAOE39Ew.png)]
ECMAScript 3 将 Java 所有关键字都列为保留字,而 ECMAScript 5 规定较为灵活,例如:
- 在非严格模式下,仅规定 class、const、enums、export、extends、import、super 为保留字,其他 ECMAScript 3 保留字可以自由使用;
- 在严格模式下,ECMAScript 5 变得更加谨慎,严格限制 implements、interface、let、package、private、protected、public、static、yield、eval(非保留字)、arguments(非保留字)的使用。
JavaScript 预定义了很多全局变量和函数,用户也应该避免使用它们,具体说明如表所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKO47Zyu-1670505761590)(C:\Users\地狱魔爵人\Desktop\截图\2022-10\chrome_OdjXYnD55c.png)]
不同的 JavaScript 运行环境都会预定义一些全局变量和函数,上表列出的仅针对 Web 浏览器运行环境。
运算符
比较运算符:
符号 | 作用 | 用法 |
---|---|---|
= | 赋值 | 把右边给左边 |
== | 判断 | 判断两边值是否相等(注意此时还会有隐式转换,可以自动把字符串类型转换为数字型) |
=== | 全等 | 判断两边的值和数据类型是否完全相同 |
逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
1、短路运算(逻辑中断)
短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式了。
1、逻辑与
语法:表达式1 && 表达式2
如果第一个表达式的值为真,则返回表达式2
如果第一个表达式的值为假,则返回表达式1
console.log(123 && 456); //456
console.log(0 && 456); //0
console.log(0 && 1 + 2 && 455 * 123); //0
如果有空的或者否定的为假的,其余都是真的。0 ‘’(空字符串) null undefined NaN
2、逻辑或
表达式1 || 表达式2
如果表达式1 结果为真 则返回表达式1,如果表达式1 结果为假 则返回表达式2。
var num = 0;
console.log(123 || num);
console.log(num);
输出结果:123 0 (此时在第二部时 num 的值逻辑中断)
2、运算符优先级
优先级 | 运算符 | 顺序 |
---|---|---|
1 | 小括号 | () |
2 | 一元运算符 | ++ – ! |
3 | 算数运算符 | 先 * / %后+ - |
4 | 关系运算符 | > >= < <= |
5 | 相等运算符 | == != === !== |
6 | 逻辑运算符 | 先&& 后|| |
7 | 赋值运算符 | = |
8 | 逗号运算符 | , |
优先级运算例子:
console.log(4 >= 6 || (“人” != “阿凡达” && !(12 * 2 == 144) && true));//true
var num = 10;
console.log(5 == num / 2 && (2 + 2 * num).toString() === “22”);//true
二、流程控制
在程序执行时,各条代码的执行顺序对程序的结果是有直接影响的。很多时候需要通过控制代码的执行顺序来实现我们要完成的功能。
流程控制主要有三种结构:
顺序结构:最基本的流程控制,按照代码先后顺序依次执行。
分支结构:根据不同的条件去执行不同的代码最后得到不同的结果。
循环结构:
if分支语句
//条件成立执行的代码,否则什么也不做
if (条件表达式) {
//条件成立执行的代码语句
}
if-else语句:
//条件成立 执行if里面的代码,否则执行else里面的代码
if (条件表达式) {
//[如果]条件成立执行的代码
}else {
//[否则]执行的代码
}
if else if多分支语句 :
if (条件表达式1){
//语句1;
}else if (条件表达式2) {
//语句2;
}else if (条件表达式3) {
//语句3;
}else {
//最后的语句
}
三元表达式
由三元运算符组成的句子。 结构为:条件表达式 ?表达式1 :表达式2
若条件表达式为真则返回表达式1,反之则返回表达式2。
var num = prompt("请输入数字:");
var result = num < 10 ? "0" + num : num;
alert(result);
swich语句
针对变量设定一系列的特定值时使用该语句。语法结构如下:
switch (表达式 //num) {
case value1:
//表达式 等于value1时要执行的代码。
break;
case value2:
//表达式 等于value2时要执行的代码。
break;
case value3:
//表达式 等于value3时要执行的代码。
break;
default:
//表达式 不等于任何一个value值时要执行的代码。
}
注意数据类型要一致
num 的值和 case 里面的值要相匹配,即 全等(值和数据类型都要相等) 。
swich语句与if else if语句的区别
一般情况下,它们两个语句可以相互替换
1、switch…case语句通常处理case为比较确定值的情况,而if一else语句更加灵活,常用于判断(大于、
等于某个范围)
2、switch语句进行条件断后直接执行到程序的语句,效率更高。而if…else语句有几种条件,就得 判断多
少次。
3、当分支比较少时,if…else语句的执行效率比switch语句高。
4、当分支比较多时,switch语句的执行效率比较高,而且结构更加清晰。
循环结构
for循环
语法结构:
for(初始化变量;条件表达式;操作表达式) {
//循环体
}
//1、首先执行里面计数器变量 var i = 1 但是这句话在for 里面只是执行一次 index
//2、去 i <= 100 来判断是否满足条件, 如果满足条件 就去执行 循环体 不满足条件则退出循环
//3、最后去执行i++ i++是后置的代码 递增 第一轮结束
//4、接着去执行 i <= 100 如果满足条件 就去执行循环体 不满足条件退出循环。
- 初始化变量 就是用var 声明一个普通变量,通常用于作为计数器使用
- 条件表达式 就是用来决定每一次循环是否继续执行的代码 就是终止的条件
- 操作表达式 是每次循环最后执行的代码 经常用于我们计数器变量进行更新
let count = 0;
let even = 0;
let odd = 0;
let sum = 0;
let num = prompt("请输入要计算数值的前n项和:");
for (let i = 1; i <= num; i++) {
count += i;
//判断是否为偶数
if (i % 2 == 0) {
even += i;
} else {
odd += i;
}
// 判断是否能被3整除
if (i % 3 == 0) {
sum += i;
}
}
let ave = count / num;
// 求前n项的和
console.log("前" + num + "和为" + count);
// 求前n项的平均值
console.log(ave);
//求前n项的偶数和
console.log(even);
// 求前n项的奇数和
console.log(odd);
// 求前n项的是3 的倍数的和
console.log(sum);
// 输入班级总人数,弹出提示框输入每个学生的成绩,然后求输入总成绩的和,再求出班级的平均成绩
let score = 0;
let stu = prompt("请输入班级人数:");
for (let i = 1; i <= stu; i++) {
let count = parseInt(prompt("请输入第" + i + "学生的成绩"));
score += count;
}
let ave = score / stu;
console.log(score);
console.log(ave);
双重for循环
1、语法结构:
for (外层的初始化变量;外层的条件表达式;外层的操作表达式) {
for(里层的初始化变量;里层的条件表达式;里层的操作表达式)
}
2、可以把里面的循环看做是外层循环的语句
3、外层循环一次,里层的循环执行全部
// 打印倒三角形
/* let sum = prompt("请输入要打印的层数:");
let str = "";
for (let i = 1; i <= sum; i++) {
for (let j = i; j <= sum; j++) {
str += "☆";
}
str += "\n";
}
*/
//打印九九乘法表
let num = prompt("请输入要打印的层数:");
let str = "";
for (let i = 1; i <= num; i++) {
for (let j = 1; j <= i; j++) {
str += j + "×" + i + "=" + i * j;
str += "\t";
// str += "♥";
}
str += "\n";
}
console.log(str);
while循环:
结构及执行思路如下:
while (条件表达式) {
//循环体代码
}
/*里边应该有计数器,初始化变量
里边应该有操作表达式 完成计数器的更新 防止死循环*/
例子1: /* var sum = 0;
var j = 1;
while (j <= 100) {
sum += j;
j++;
}
console.log(sum); */
例子2:
/* var message = prompt("你爱我吗!");
while (message !== "我爱你") {
message = prompt("你爱我吗~");
}
alert("我也爱你啊"); */
执行思路:
1、先执行条件表达式,如果结果为true,则执行循环体代码;如果为false,则退出循环,执行后面代码
2、执行循环体代码
3、循环体代码执行完毕后,程序会继续判断执行条件表达式,如条件仍为true,则会执行循环体,直到循
环条件为false时,整个循环过程才会结束。
do while循环
语法结构及执行思路如下:
do{
//循环体代码-条件表达式为true时重复执行循环体代码
}while(条件表达式);
例子1:
do {
var mess = prompt("你爱我吗");
} while (mess !== "我爱你");
alert("我也爱你啊");
例子2:
/* let sum = 0;
let i = 1;
let count = prompt("请输入数值:");
do {
sum += i;
i++;
} while (i <= count);
console.log(sum); */
例子3:
/* do {
var user = prompt("请输入用户名:");
var password = prompt("请输入密码:");
} while (user !== "admin" || password !== "123456");
alert("输入正确~登陆成功"); */
执行思路:
1、先执行一次循环体代码。
2、再执行条件表达式,如果结果为true,则继续执行循环体代码,如果为false,则退出循环,继续执行后面
代码。
注意:先再执行循环体,再判断,我门会发现do…while循环语句至少会执行一次环体代码。
continue与break
continue关键字用于立即跳出本次循环继续下次循环,如此在本次循环中continue语句后面的代码会少执行一次。
break用于立即跳出整个循环(循环结束)。
//1、求1~100至今,除了能被7整除之外的整数和
var sum = 0;
for (var i = 1; i <= 100; i++) {
if (i % 7 == 0) {
continue;
}
sum += i;
}
console.log(sum);
//2、break退出整个循环
for (let i = 1; i <= 100; i++) {
if (i == 3) {
break;
}
console.log(i);
}
/* 求整数1~100的累加值,但要求跳过所有个位为3的数【用continue实现】 */
var sum = 0;
for (var i = 1; i <= 100; i++) {
if (i % 10 == 3) {
continue;
}
sum += i;
}
console.log(sum);
三、数组
数组的概念
*数组* 是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。
//普通变量一次只能存储一个值
var num = 10;
//数组一次可以存储多个值
var arr = [1,2,3,4,5];
创建数组
JS中创建数组有两种方式:
利用new创建数组:
var 数组名 = new Array() ;
var arr = new Array(); //创建一个新的空数组
利用数组字面量创建数组:
1.使用数组字面量方式创建空的数组
var 数组名 = [ ];
2.使用数组字面量方式创建带初始值的数组
var 数组名 = [‘小白’,‘小黑’,‘大黄’,‘花花’];
注意:
数组的字面量是方括号[]
声明数组并赋值称为数组的初始化
这种字面量方式也是我们以后最多使用的方式
数组元素的类型:
数组中可以存放任意类型的数据,例如字符串,数字,布尔值等
var arrStus = [‘小白’,12,true,28.9];
获取数组元素:
数组的索引:索引(下标):用来访问数组元素的序号(数组下标从0开始)
var arr = [‘小白’,‘小黑’,‘大黄’,‘花花’];
索引号: 0 1 2 3
数组可以通过索引来访问、设置、修改对应的数组元素,我们可以通过“数组名[索引]”的形式来获取数组的元素
这里访问就是获取得到的意思
var arrStus = [1,2,3]; //定义数组
alert(arrStus[1]); //获取数组中的第二个元素,因为索引号是从0开始的
数组的长度
使用“数组名.length” 可以访问数组元素的数量(数组长度)
var arr = [‘关羽’,‘张飞’,‘刘备’,‘马超’,‘赵云’,‘黄忠’,‘姜维’];
console.log(arr.length); //在控制台输出的长度为 7
遍历数组:
数组中的每一项我们可以通过“数组名[索引号]”的方式一项一项的取出来
var arr = [‘关羽’,‘张飞’,‘刘备’,‘马超’,‘赵云’,‘黄忠’,‘姜维’];
console.log(arr[0]); //关羽
console.log(arr[1]); //张飞
console.log(arr[2]); //刘备
怎么把数组里面的元素全部取出来?
通过循环我们可以把里面的元素都取出来,从代码中可以发现,从数组中取出每一个元素时,代码都是重复的,有所不一样的是索引值在递增。
var arr = ["red", "green", "blue"];
for (i = 0; i < 3; i++) {
console.log(arr[i]);
}
注意:因为我们的数组索引号从0开始,所以i必须从0开始 i < 3
输出的时候arr[i] i 计数器当索引号来用
但是如果我们把 i < 3 写固定了的话,后面有新增的元素就还要再改变条件的值,所以我们用数组的长度来解决这个问题。
var arr = ["red", "green", "blue"];
for (i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
*/\* 计算数组的和以及平均值 \*/*
var sum = 0;
var arr = [2, 6, 1, 7, 4];
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
console.log(sum);
var ave = sum / arr.length;
console.log(ave);
例子2:
/* 取出数组的最大值: */
var array = [2, 6, 77, 52, 25, 7];
var max = array[0]; //声明一个最大值,取出第一个元素
for (var i = 0; i < array.length; i++) {
if (max < array[i]) {
max = array[i];
}
}
console.log(max);
例子3:
/* 数组转换为字符串 */
var array = ["red", "green", "blue", "pink"];
var str = "";
var sep = "!";
for (var i = 0; i < array.length; i++) {
str += array[i];
str += sep;
}
console.log(str);
新增数组元素
通过修改length长度新增数组元素
- 可以通过修改length长度来实现数组扩容的目的。
- length 属性是可以读写的。
通过修改数组索引号的方法新增数组元素
可以通过修改数组索引的方式追加数组元素
//1.新增数组元素,修改数组长度
var arr = ["red", "green", "blue"];
console.log(arr.length);
arr.length = 5;
console.log(arr);
/* 2.通过修改数组索引号来新增数组元素,追加数组元素。
如果索引号已经被占用,就替换之前的元素。如果索引号未被占用,
就直接添加新元素,索引号之间的元素为empty */
var arr1 = ["red", "green", "blue"];
arr1[3] = "girl"; //新增元素
console.log(arr1);
arr1[2] = "boy"; //修改元素
console.log(arr1);
arr1 = "哈啊哈";//不要给数组直接赋值,否则里面的数组元素就没有了
console.log(arr1);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lvmK2aMg-1670505761593)(C:\Users\地狱魔爵人\Desktop\截图\2022-11\chrome_V2dClnmTKM.png)]
例子1:
//使用for循环在数组里面增加元素
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = i + 1;
}
console.log(arr);
例子2:
//筛选数组 , 将数组[2,0,6,1,77,0,52,0,25,7]中大于等于10的元素选出来,放入新数组。
var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
var newarr = [];
// var j = 0;
newarr.length = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 10) {
//新增数组应该从0开始,依次递增。 数组的长度可以自动检测数组元素的变化。
newarr[newarr.length] = arr[i];
// newarr[j] = arr[i];
// j++;
}
}
console.log(newarr);
例子3:
/* 把一个数组倒序排列 */
var arr = ["red", "green", "blue", "pink", "purple"];
var newarr = [];
newarr.length = 0;
for (var i = arr.length - 1; i >= 0; i--) {
newarr[newarr.length] = arr[i];
}
console.log(newarr);
例子4:
//冒泡排序
var arr = [4, 10, 5, 2, 8, 9, 1, 6, 0];
for (var i = 0; i < arr.length; i++) {
//外层循环管趟数
for (var j = 0; j < arr.length - i; j++) {
//里面的循环管 每一趟的交换次数
//内部交换2个变量的值 前一个和后面一个数组元素相比较
if (arr[j] > arr[j + 1]) {
var str = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = str;
}
}
}
console.log(arr);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDvUNIRa-1670505761595)(C:\Users\地狱魔爵人\Desktop\截图\2022-11\chrome_6qkzSLOd2s.png)]
四、函数
1.函数概念,声明及调用
函数的基本概念、声明及调用;函数作用域、作用域链、闭包;this指向及修改和绑定this指向等。
JS中的函数:把一段需要重复使用的代码,用function语法包起来,方便重复调用,分块和简化代码。复杂一点的,也会加入封装、抽象、分类等思想。
声明方式:严格意义上两种方式,但还有匿名函数
- 方式一:
function 方法名(){
//函数体
}
1、function 声明函数的关键字 全部小写
2、函数是做某件事情,函数名一般是动词
3、函数不调用自己不执行
- 方式二:ES6中声明方式箭头函数,()=>{}
- 方式三:匿名函数,将函数存到变量里 var func = function(){};
函数调用:两种方式调用
1、调用方式一:名字(); 函数可以多次调用
//函数声明
function fn(){
console.log(1);
}
//函数的调用
fn();
例子1:
function getSum(a, b) {
var sum = 0;
for (var i = a; i <= b; i++) {
sum += i;
}
console.log(sum);
}
getSum(1, 100);
2、调用方式二:在事件中调用,直接写函数名,不使用括号
//函数声明
function fn(){
console.log(1);
}
//函数在事件中的调用
document.onclick = fn;
2.函数表达式(匿名函数)
函数表达式:就是把函数存到变量里。
匿名函数:没有名字的函数;
匿名函数在使用时只有两种情况:
- 匿名函数自执行:声明后不需要调用就直接执行
(function(){
console.log("匿名函数自执行");
})();
- 函数表达式:把函数存到变量,或将函数存到数组的对应位置里等,调用时通过变量或数组对应位置进行调用。调用时需要写括号。
//2,函数表达式:把函数存到变量或数组等里,调用时通过变量进行调用
var fn = function(){
console.log("函数表达式:将函数存到变量里");
};
fn();//调用时需要写括号
//2,函数表达式:把函数存到数组第0位,调用时通过数组第0位进行调用
var arr = [];
arr[0] = function(){
console.log("函数表达式:将函数存到数组的对应位置");
};
arr[0]();//调用时需要写括号要写括号
结果:
事件函数扩展:给元素添加事件的说法是不正确的。事件时元素本身就具有的特征,只是触发事件后,默认没有相关的一些处理。这种操作其实就是给元素的某个事件添加一个事件处理函数。当事件被触发后,判断到属于该事件类型,就触发该事件函数的处理函数。
可以通过console.dir()把对象的所有属性和方法打印出来,查看对象或元素本身具有的事件。
<script>
//事件时元素本身就具有的特征,只不过,触发事件后,默认没有相关的一些处理。事件函数其实就是给元素的某个时间添加一个事件处理函数。
//可以通过console.dir()把对象的所有属性和方法打印出来
document.onclick = function(){
console.log("事件的处理函数");
};
//当被触发后,判断到属于该事件类型,就触发该事件函数的处理函数
if(typeof document.onclick == "function"){
document.onclick();
}
</script>
结果:
3.函数传参
获取元素,最好从父级元素获取,全部从document中获取,可能会出现混乱。
- 形参:形式上的参数——给函数声明一个参数;
- 实参:实际的参数——在函数调用时给形参赋的值
function func(形参1,形参2){
//函数执行代码
}
func(实参1,实参2);//调用时传参
//1、如果实参的个数和形参的个数一致,则正常输出结果
//2、如果实参个数多于形参的个数,会取到形参的个数
//3、如果实参的个数小于形参的个数 多于的形参定义为undefined,最终结果就是NaN 形参可以看做是不用声明的变量, 形参2是一个变量但是没有接收值 结果是undefined
4.修改input的值
value和innerHTML都可以用来获取和修改元素的值(或内容);value只能获取特定的textarea和input的值,但是innerHTML可以获取所有HMTL元素的值。
不同之处如下:
1)value可以用来修改(获取)textarea和input的value属性的值或元素的内容;
2)innerHTML用来修改(获取)HTML元素(如div)html格式的内容。
- 找到btn
- btn 添加点击事件:1、隐藏 p 标签,显示edit 。 2、让txt的value=inner的内容。
- edit-btn 添加点击事件:1、效验txt的value不能为空。2、让inner 的内容 = txt 的value 。3、显示 p 标签,隐藏edit。
<body>
<input type="text" value="123" id="mytest" />
<p><button onclick="myedit()">修改value的值</button></p>
<script>
function myedit() {
document.getElementById("mytest").value = "456";
}
</script>
1、使用input标签创建一个文本框,并设置其默认值为123。
2、设置input标签的id为mytest。
3、创建一个按钮,并给它绑定onclick点击事件,当它被点击时,执行myedit函数。
4、在js标签内,创建myedit函数,在函数内,通过getElementById方法通过id获得input对象,再通过给value赋值,实现改变input的value值。
5.函数的可变参 关键字 arguments
**arguments的使用:**当我们不知道有多少个参数要传递的时候,可以用arguments来获取。在JavaScript中,arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参。
(arguments,代表所有实参的集合,通过下标获取参数的每一位;通过length获取实参的个数;集合是类数组,可以使用下标,但是没有数组中的各种方法。)
<script>
//arguments的使用 只有函数才有arguments对象 而且是每个函数都内置好了这个arguments
function fn() {
console.log(arguments); //里面存储了所有传递过来的参数
console.log(arguments.length);
console.log(arguments[2]);
// 可以按照数组的方式遍历arguments
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
fn(1, 2, 3, 4, 5);
/* 伪数组并不是真正意义上的数组
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
1、具有length属性。 2、按索引方式存储数据。3、不具有数组的push(),pop()等方法。 */
</script>
案例:利用函数求任意个数的最大值
function getMax() {
var max = arguments[0];
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(1, 2, 3));
console.log(getMax(1, 2, 3, 123, 31, 23));
console.log(getMax(11, 2, 3, 26, 90, 0, 50));
案例1:// 利用函数封装的方式,对数组排序,冒泡 排序: sort
function sort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
var re = sort([9, 6, 3, 0, 5, 8, 1, 4]);
console.log(re);
案例2:
//要求:输入一个年份,判断是否是闰年(闰年:能被4整除但不能被100整除,或者能被400整除)
function isRunYear(year) {
var year = prompt("请输入年份:");
//如果是闰年则返回true,否则 返回 false
var flag = false;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
flag = true;
}
return flag;
}
console.log(isRunYear());
案例3;函数调用另一个函数
function fn1() {
console.log(111);
fn2();
console.log(222);
}
function fn2() {
console.log(333);
console.log(444);
}
fn1();
案例4:
<script>
//用户输入年份,输出当前2月份的天数
function backDay() {
var year = prompt("请输入年份:");
if (isRunYear(year)) {
alert("当前年份闰年2月份有29天");
} else {
alert("当前年份平年2月份有28天");
}
}
backDay();
//判断是否为闰年的函数
function isRunYear(year) {
//如果是闰年则返回true,否则 返回 false
var flag = false;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
flag = true;
}
return flag;
}
</script>
案例3:
6.函数返回值(return)
函数返回值即函数执行之后的返回结果。
-
所有函数都会有函数返回值即函数执行后一定会返回一个结果,如果没有定义默认返回undefined;
-
在函数中,return后定义返回值;
-
在函数中,return之后的代码就不会再执行了。(终止函数)
-
return只能用于函数中,用在其他地方会报错
-
break,continue,return的区别:
break:结束当前的循环体(如for、while)
continue:跳出本次循环,继续执行下次循环(如for、while)
return:不仅可以退出循环,还能够返回return语句中的值,同时还可以结束当前的函数体内的代码
函数的返回格式:
function 函数名() {
return 需要返回的结果
}
函数名();
//(1)我们函数只是实现某种功能,最终的结果需要返回给函数的调用者 函数名() 通过return实现的。
//(2)只要函数遇到return,就把后面的结果返回函数的调用者 函数名() = return后面的结果。
例子1:
function getResult(a, b) {
return a + b;//作为返回值
}
// getResult(); 此处 getResult() = 520
console.log(getResult(1, 2));
例子2:求两个数之间的最大数值。
function getMax(a, b) {
/* if (a > b) {
return a;
} else {
return b;
} */
return a > b ? a : b;//三元表达式
}
console.log(getMax(1, 9));
例子3:求最大值
function getArrMax(arr) {
//arr就收一个数组
var max = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
//实参是把一个数组传过去,用一个变量来接收函数的返回结果。
var result = getArrMax([5, 2, 99, 101, 67, 77]);
console.log(result);
例子4:
//求任意两个数的 加减乘除的结果
function getResult(a, b) {
return [a + b, a - b, a * b, a / b];
}
var re = getResult(9, 3);
console.log(re);
例子5:
// 作业四: 输入一个数字判断是否为质数
function getNum() {
var num = parseFloat(prompt("请输入一个数值:"));
for (var i = 2; i < num; i++) {
if (num % i == 0) {
return num + "不是质数";
}
}
return num + "是质数";
}
alert(getNum());
例子6://作业一:用户输入任意两个数字的任意算数运算,并能弹出
/* function getNum() {
var num1 = parseFloat(prompt("请输入第一个数字:"));
var sign = prompt("请输入+ - * / 运算符:");
var num2 = parseFloat(prompt("请输入第三个数字:"));
if (sign == "+") {
return num1 + num2;
} else if (sign == "-") {
return num1 - num2;
} else if (sign == "*") {
return num1 * num2;
} else if (sign == "/") {
return num1 / num2;
} else {
alert("输入有误!");
}
}
var re = getNum();
alert("计算的结果是:" + re); */
7.封装获取元素的方法
封装通过id/CSS选择器获取(一般在父级下获取,所以传入父级和选择器名字)获取多个元素的方法,然后返回获取到的值
//通过id名获取元素
function _id(idName){
return document.getElementById(idName);
}
//通过CSS选择器获取一个元素
function _selector(parent,selector){
return parent.querySelector(selector);
}
//通过CSS选择器获取一组元素
function _selectorAll(parent,selectors){
return parent.querySelectorAll(selectors);
}
使用:
var wrap1 = _id("wrap1");
var wrap2 = _id("wrap2");
var wrap3 = _id("wrap3");
var wraps = _selectorAll(document,".wrap");
var btn = _selectorAll(wraps,".tab button");
var divs = _selectorAll(wraps,".cont div");
8.获取计算后样式—getComputedStyle(el)
点击时获取box的宽度,在原有基础上+100
- 从style中获取的是行间样式,但是通常样式不会写在行间;
- 获取计算后样式:getComputedStyle(el)获取元素的计算后样式。属于window的方法,即window.getComputedStyle(el)。在JS中使用window下的方法时,window可以不写;
- getComputedStyle(el)方法不兼容IE6,7,8;
- 计算后样式:优先级最高的样式,即当前显示出来的样式
- 使用getComputedStyle(el)是获取window下所有的样式,getComputedStyle(el)[‘样式名’]即可获取到特定样式
- 写样式名时,使用驼峰样式的名字(否则IE下会有兼容问题),如margin-left必须写成marginLeft。
- IE下获取元素的计算后样式,需要使用el.currentStyle[‘样式名’]
- 兼容IE和其他浏览器:判断el.currentStyle返回true即表示IE,否则就是其他浏览器,然后在对应浏览器下使用对应方法
- 获取计算后样式会经常使用,因此可以封装成方法,进行复用
- getComputedStyle(el)和el.currentStyle获取不到伪元素的样式,因为伪元素不是DOM的内容
- 伪类样式计算后样式可以获取到,伪元素获取不到
标准浏览器下:transition:.5s;设置过渡效果的时间,否则div会直接变到从100+100的宽度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="../index.js"></script>
<title>getComputedStyle(el)</title>
<style>
#box { width: 100px; height: 100px; background: red; transition:.5s; }
</style>
</head>
<body>
<div id="box"></div>
<script>
//需求:点击div,div的width在原有基础上+100px
var box = _id("box");
var ss = 0;
box.onclick = function(){
//直接获取box.style.width是获取不到的,因为样式时写到style标签,而非行间样式
//console.log(box.style.width);//获取不到值
//使用getComputedStyle(el)['样式名']即可获取到当前显示出来的样式
var curStyle = getComputedStyle(box)['width'];
//获取到的结果是“100px”,需要将其进行转为数字。width最后有可能会有小数点,所以最好使用parseFloat()。大多数情况下parseInt()亦可
//但是parseInt(curStyle);才能去掉px进行相加
//注意px前面的数字千万不要加引号
this.style.width = parseInt(curStyle) + 100 + 'px';
};
</script>
</body>
</html>
结果:
不断点击后:
兼容问题:发现在IE6,7,8下并不支持getComputedStyle(el)方法
解决:通过el.currentStyle判断返回true代表在IE浏览器下,为false就不是在IE浏览器下。在IE浏览器下必须使用el.currentStyle才行
//解决浏览器兼容问题:通过el.currentStyle判断返回true代表在IE浏览器下,为false就不是在IE浏览器下。在IE浏览器下必须使用el.currentStyle才行
function currStyle(el,styleName){
// var curStyle = '';
// if(el.currentStyle){
// curStyle = el.currentStyle[styleName];
// }else{
// curStyle = getComputedStyle(el)[styleName];
// }
// return curStyle;
//使用三元获取返回值
return el.currentStyle?el.currentStyle[styleName]:getComputedStyle(el)[styleName];
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="../index.js"></script>
<title>getComputedStyle(el)</title>
<style>
#box { width: 100px; height: 100px; background: red; transition:.5s; }
</style>
</head>
<body>
<div id="box"></div>
<script>
//需求:点击div,div的width在原有基础上+100px
var box = _id("box");
box.onclick = function(){
//直接获取box.style.width是获取不到的,因为样式时写到style标签,而非行间样式
//console.log(box.style.width);//获取不到值
//使用getComputedStyle(el)['样式名']即可获取到当前显示出来的样式
// var curStyle = getComputedStyle(box)['width'];
var curStyle = currStyle(box,'width');
//获取到的结果是“100px”,需要将其进行转为数字。width最后有可能会有小数点,所以最好使用parseFloat()。大多数情况下parseInt()亦可
//但是parseInt(curStyle);才能去掉px进行相加
//注意px前面的数字千万不要加引号
this.style.width = parseInt(curStyle) + 100 + 'px';
};
</script>
</body>
</html>
结果:
8.JS预解析机制(变量提升Hoisting)
js引擎运行js分为两步:预解析 代码执行:
1、预解析 js引擎会把js 里面所有的var 还有 function 提升到当前作用域的最前面。
2、代码执行 按照代码执行书写的顺序从上往下执。
<script>
/*
var num = 10;
function fn() {
console.log(num);
var num = 20;
console.log(num);
}
fn(); */
//相当于以下代码
var num;
function fn() {
var num;
console.log(num);
num = 20;
console.log(num);
}
fn();
</script>
<script>
/* fl();
console.log(c);
console.log(b);
console.log(a);
function fl() {
var a = (b = c = 9);
console.log(a);
console.log(b);
console.log(c);
} */
//相当于以下代码
function fl() {
var a;
a = b = c = 9;
//相当于 var a = 9; b = 9;c = 9; b和c直接赋值 没有var 声明 相当于 全局变量
//集体声明 var a = 9, b = 9, c = 9;
console.log(a);
console.log(b);
console.log(c);
}
fl();
console.log(c);
console.log(b);
console.log(a); //a是局部变量
</script>
JS预解析机制(变量提升(Hoisting)):JS在读取到一个script标签(或者一个函数作用域)时,会先进行一个预解析的过程,在这个过程中,会把var声明的变量和function声明的函数体,提升到整个script标签(或者一个函数作用域)最前边去。在预解析完之后,JS才会从上到下一行一行解析代码并执行。
- var在预解析时,会把声明提升到最前边(赋值前打印返回undefined)。只提升声明,不会把赋值过程进行提升。
- function的函数体在预解析时,会把整个函数体提升至最前边。(函数体:function fn(){ console.log(1);})
- 函数表达式(函数表达式:var fn = function(){};)只会提升函数表达式的声明,不会执行(真正执行函数表达式前调用会返回undefined)
- 在预解析时,会先预解析var(包括变量声明和函数表达式的变量声明),把var放在最前面,然后再预解析function,所以当var和function重名时,function会覆盖var;
//JS var变量的预解析
console.log("var变量的预解析:"+a);//undefined
var a = 0;
//JS函数体的预解析
console.log("函数体的预解析:"+fn);
function fn(){
console.log("函数");
}
//JS函数表达式的预解析
console.log("函数表达式的预解析:"+fnn);
var fnn = function(){
console.log("函数表达式");
};
结果:
在预解析时,会先预解析var,把var放在最前面,然后再预解析function,所以当var和function重名时,function会覆盖var:
/*
解析过程:先预解析var声明的a,再预解析函数体a,后面覆盖前面,所以最后结果是function函数体
var a;
function a(){console.log("函数a");};//此函数体解析后会覆盖变量a
*/
console.log(a);
var a = 0;
function a(){
console.log("函数a");
}
结果:
JS预解析示例:
//JS预解析过程:遇到Script标签或一个函数作用域,会先进行预解析,先预解析var声明的变量(包括普通变量声明和函数表达式声明),再声明function函数体,如果有重名function声明会覆盖var声明
/*
预解析之后代码:
var a;//变量a
var a;//函数表达式a(函数表达式也是var声明)
function a(){//函数体
console.log(1);
};
console.log(a);//打印函数体a
var a = 10;
console.log(a);//10
console.log(a);//10
a = function(){
console.log(2);//打印函数表达式a
};
console.log(a);
*/
console.log(a);
var a = 10;
console.log(a);
function a(){
console.log(1);
};
console.log(a);
var a = function(){
console.log(2);
};
console.log(a);
结果:
JS预解析机制不是良好的编码习惯,不利于代码维护,建议不要使用,编码时建议先声明,再使用。
扩展:从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。
ES6之后就不能像JS预解析这么编写JS代码了。
9.作用域
通常来说一段程序代码中使用的变量和函数并不总是可用的,限定其可用性的范围即作用域,作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。
通俗的说,作用域:数据起作用的范围(某条数据可以在什么范围内使用),目的是为了提高程序的可靠性更重要的是减少命名的冲突。
作用域的分类:
-
全局作用域:整个script标签:后者是一个单独的js文件。
通过var或function声明在全局(声明在任意函数之外和代码块之外)中的数据,在全局的任意地方都可以调用或修改(即全局变量)和在window下的属性
-
局部作用域:
- 函数作用域:声明在函数内部的某个数据(var,function,参数),就只能在函数内部使用(函数的局部作用域)
- 块级作用域(ES6新增)
<script>
//变量作用域:根据作用域的不同可以把变量分为全局变量和局部变量。
//1、在全局作用域下的变量 在全局下都可以使用。
//如果在函数内部,没有声明直接赋值的变量也属于全局变量
var num = 10;
console.log(num);
function fn() {
console.log(num);
}
fn();
//2.局部变量:在局部作用域下的变量,后者在函数内部的变量就是 局部变量
//注意:函数的形参也可以看做是局部变量。
function fun() {
var num1 = 10; //num1就是局部变量,只能在函数内部使用。
num2 = 20;
}
fun();
// console.log(num1);此处不执行
console.log(num2);
// 3.从执行效率来看全局变量和局部变量。
//(1)全局变量只有浏览器关闭的时候才会销毁, 比较占内存资源。
//(2)局部变量 当我们程序执行完毕的就会销毁,计较节约内存资源。
</script>
10.window
- 在JS中,默认情况下 var声明的全局变量和function声明的全局函数会挂载在window上(所以要避免声明全局变量和全局函数)
- 在JS中,默认全局数据都会保存在window下(ES6之前)
- 另外window是JS在浏览器里的顶层对象,所以window的属性和方法也都是全局的
- 在JS中,调用window下的属性和方法,默认可以不写window,所以如果在函数里面声明变量没有写var,会把其当做window的一个属性;(不规范写法,要避免)
//var声明的全局变量和function声明的全局函数,都默认挂载在window上
var a = 0;
console.log(window);
function fn(){
b = 10;
console.log(1);
}
fn();
console.log(b);//此处b没有写声明的var所以,b=10即为window.b = 10;相当于挂载在window上的全局变量
//在JS中,默认全局数据都会保存在window下
//window是浏览器的最顶层对象,所以默认window下的属性和方法都是全局的。所以window下的方法和属性,默认可以不写window
结果:全局变量a和全局函数fn()都默认挂载在window上。而且,此处b没有写声明的var,所以,b=10即相当于window.b = 10;也是挂载在window上的全局变量。
11.全局污染(命名冲突问题)
全局变量污染:大家都在全局中写代码,很容易造成命名冲突,导致代码冲突。ES6中代码冲突会直接报错。所以要养成好的习惯不要在全局去声明变量。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>全局污染</title>
</head>
<body>
<div id="list"></div>
<div class="memulist"></div>
<script>
var list = document.getElementById("list");
var list = document.querySelector(".memulist");
console.log(list);
</script>
</body>
</html>
结果:发现最后获取的只有一个元素,所以很容易造成代码冲突
解决:不要声明全局变量
(function(){
var list = document.getElementById("list");
console.log(list);
})();
(function(){
var list = document.querySelector(".memulist");
console.log(list);
})();
结果:
JS中提供了id使用的简便方法,直接调用id名即可:但尽量不要这么写,不规范:
console.log(list);
匿名函数:匿名函数自执行本身就是为了避免在全局写代码,避免冲突的。匿名函数自执行也叫开启一个新的命名空间。即开启新的作用域,此作用域和其他的不会冲突。
12.作用域链(scope chain)
作用域链决定了哪些数据能被函数访问。当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。
作用域链:内部函数访问外部函数的变量,采取的是链式查找的方式来决定取哪个值 这种结构我们称为作用域链。(就近原则)
作用域链查找过程:
在JS中我们调用一条数据时,会先在当前作用域进行查找,如果找不到,就从向上找父作用域的数据,还找不到就接着向上,一直找到全局作用域(window对象),window都找不到就报错。
//调用fn()时,在其子函数fn2()被调用时,首先会在fn2()自己的作用域内找变量a
//找不到就在其父级作用域即fn()作用域中找,即a=10,然后打印a=10
function fn(){//外部函数
var a = 10;
function fn2(){//内部函数
console.log(a);
}
fn2();
}
fn();
作用域链示例:
<script>
function fn(){
var b = 0;
return function(){
b++;
console.log(b);
};
}
var f = fn();
console.log(f);//ƒ (){ b++; console.log(b); }
f();//1
f();//2
f();//3
fn()();//1
</script>
结果:
解析(函数拆分)三个f():
注意:这里的var f = fn();是将函数fn()的返回值函数体f(){ b++; console.log(b); };赋给变量f,但是并没有执行该返回值函数体,当f()调用时,便执行了该函数体。f此时是fn的子函数,那它可以访问和更改父级fn的作用域中的b。
b变量会一直赋值,是因为JS的垃圾回收机制决定的,只要检测都有引用存在,就不会释放。
<script>
// function fn(){
// var b = 0;
// return function(){
// b++;
// console.log(b);
// };
// }
// var f = fn();
// f();//1
// f();//2
// f();//3
// fn()();//1
//这里的var f = fn();是将函数fn()的返回值函数体function(){ b++; console.log(b); };赋给变量f,但是并没有执行该返回值函数体,当f()调用时,便执行了该函数体
//即这里的三个f()相当于,在函数fn()中写了三个子函数,再进行调用
//由于b变量对于fn1(),fn2(),fn3()都是父级变量,所以每次b++都会将b的值+1,所以最后得到的值即1,2,3
function fn(){
var b = 0;
function fn1(){
b++;
console.log(b);
}
fn1();
function fn2(){
b++;
console.log(b);
}
fn2();
function fn3(){
b++;
console.log(b);
}
fn3();
}
fn();
</script>
结果:所以执行三个f();和执行三个fn();得到的结果是不一样的。
以上进一步分解:
<script>
// function fn(){
// var b = 0;
// return function(){
// b++;
// console.log(b);
// };
// }
// var f = fn();
// f();//1
// f();//2
// f();//3
// fn()();//1
//这里的var f = fn();是将函数fn()的返回值函数体function(){ b++; console.log(b); };赋给变量f,但是并没有执行该返回值函数体,当f()调用时,便执行了该函数体
//即这里的三个f()相当于,在函数fn()中写了三个子函数,再进行调用
//由于b变量对于fn1(),fn2(),fn3()都是父级变量,所以每次b++都会将b的值+1,所以最后得到的值即1,2,3
function fn()
var b = 0;
// function fn1(){
// b++;
// console.log(b);
// }
// fn1();
// function fn2(){
// b++;
// console.log(b);
// }
// fn2();
// function fn3(){
// b++;
// console.log(b);
// }
// fn3();
function fnn(){
b++;
console.log(b);
}
fnn();
fnn();
fnn();
}
fn();
</script>
结果:所以调用三次f()和再fn()函数里执行三次fnn()是一样的
函数拆分fn()():
<script>
// function fn(){
// var b = 0;
// return function(){
// b++;
// console.log(b);
// };
// }
// var f = fn();
// f();//1
// f();//2
// f();//3
// fn()();//1
//此处的fn()();第一个括号表示执行fn()函数,第二个括号表示执行fn()函数中返回值中的函数,所以fn()()相当于整个fn函数再执行一次
//函数每次调用,都相当于把这个代码复制出来执行了一遍。所以fn()();每次执行都是重新执行一遍代码,相当于以下:
function fn(){
var b = 0;
return function(){
b++;
console.log(b);
};
}
function fnA(){
var b = 0;
return function(){
b++;
console.log(b);
};
}
fn()();//1
fnA()();//1
</script>
</body>
</html>
结果:
函数每次调用,如fn()和fnA()之间没有任何关联,都相当于把这个代码复制出来执行了一遍。
13.闭包
闭包是对作用域链的一种表现和使用。
函数对象可以通过作用域链相互关联起来,函数体内的数据(变量和函数声明)都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”。既函数体内的数据被隐藏于作用于链内,看起来像是函数将数据“包裹”了起来。从技术角度来说,js的函数都是闭包:函数都是对象,都关联到作用域链,函数内数据都被保存在函数作用域内。
fn()();调用函数后的返回值还是一个函数,也对其进行执行
函数的每次执行之间没有任何关系。每次执行都相当于在JS内部把代码重新写了一遍。
面试时:闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
闭包:
- 形式:函数嵌套函数;
- 作用:子函数可以访问父函数的作用域,但是父级不能访问子级的。
闭包示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>闭包</title>
</head>
<body>
<script>
function fn(){
var a = 0;
function fn2(){
console.log(a);//fn2是fn的子函数,所以可以访问父级作用域的a
}
fn2();
}
fn();
console.log(a);//在fn函数作用域外,不能访问其子级作用域fn中的变量a
</script>
</body>
</html>
结果:
闭包应用:i传参给了fn函数,而点击事件是fn函数的子函数,所以也可以获取到fn函数中的i
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>闭包应用</title>
</head>
<body>
<button>按钮一</button>
<button>按钮二</button>
<button>按钮三</button>
<script>
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
fn(i);
}
//i传参给了fn函数,而点击事件是fn函数的子函数,所以也可以获取到fn函数中的i
function fn(index){
btns[index].onclick = function(){
console.log(index);
};
}
</script>
</body>
</html>
结果:按钮进行循环后会将当前index传参给fn函数,当点击按钮时,再通过父级作用域获取到父级的index
闭包应用二:(匿名函数自执行方式)页面刷新时解析for循环并将index传给fn,并且立即执行fn函数,当点击按钮时,再通过父级作用域fn获取到index
<script>
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
(function fn(index){
btns[index].onclick = function(){
console.log(index);
};
})(i);
}
//i传参给了fn函数,而点击事件是fn函数的子函数,所以也可以获取到fn函数中的i
// function fn(index){
// btns[index].onclick = function(){
// console.log(index);
// };
// }
</script>
结果:
闭包应用三:点击按钮后,再立即执行一个匿名函数自执行。匿名函数自执行后,得到的是一个函数返回值,当点击时再执行该函数中的内容
<script>
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
//匿名函数自执行后,得到的是一个函数返回值,当点击时再执行该函数中的内容
btns[i].onclick = (function fn(index){
return function(){
console.log(index);
};
})(i);
}
</script>
结果:
14.this当前执行代码的环境对象
默认情况下:
- 函数外:window
- 函数内:函数中的this指向谁,取决于这个函数是怎么调用的
- 严格模式下,默认为undefined
- 作为对象的属性(方法,事件(方法的一种))调用,指向当前对象
- 其余情况执行window
<script>
//函数外:window
// 函数内:函数中的this指向谁,取决于这个函数是怎么调用的
// 作为对象的属性(方法)调用,指向当前对象
// 其余情况执行window
function fn(){
console.log(this);
}
//直接调用函数,this代表window
console.log("没有作为对象的属性进行调用,而是直接调用:");
fn();//this指向window
//作为对象的属性或方法调用
//作为对象的属性进行调用
console.log("作为对象的属性进行调用:");
document.fn = fn;
document.fn();//this 执行document
//事件里,把this绑定在事件上
console.log("作为对象的属性(事件)进行调用:");
document.onclick = fn;
document.onclick();//this 执行document
//数组里,把函数放到数组里,再由数组调用,此时this指向当前数组
console.log("作为对象(数组)进行调用:");
var arr = [fn,1,2];
arr[0]();//this指向当前数组
//obj对象里
console.log("作为对象(object对象)进行调用:");
var obj = {
fn:fn
};
obj.fn();//this指向object对象
</script>
结果:
15.严格模式下的this指向
在script标签最上面加上 ‘use strict’;,加上’use strict’后预解析已经不能使用,会报错。
严格模式下的function指向问题:在严格模式下,function如果不是作为对象的属性和方法被调用(即直接调用方法)就指向undefined。
<script>
'use strict';
function fn(){
console.log(this);
};
console.log("严格模式下,函数直接被调用(没有通过函数的属性或方法被调用,this就指向undefined):");
//严格模式下,函数直接被调用(没有通过函数的属性或方法被调用,this就指向undefined)
fn();
//通过函数的属性或方法被调用,就指向被调用的对象
console.log("通过函数的属性或方法被调用,就指向被调用的对象:");
document.onclick = fn;
document.onclick();
</script>
结果:
16.this指向的修改
16.1 function.call()
- function.call(this指向谁,参数1,参数2…)调用函数,并修改函数中的this指向;
- 执行函数的call方法,会调用该函数,并且修改函数中 的this指向;
- call中的第0个参数,代表当前函数执行时,函数中的this指向谁
- 其他参数都是给函数传的实参
- 注意修改执行为body时,一定要使用document.body
<script>
function fn(a,b){
console.log(this,a,b);
}
//直接执行,this指向window
console.log("直接调用函数,this指向window:");
fn(1,2);//window
//通过call更改当前函数的this指向
//更改this指向为document
console.log("调用函数的call方法,更改this指向document:");
fn.call(document,'a','b');//document
//更改this指向为document.body
console.log("调用函数的call方法,更改this指向document.body:");
fn.call(document.body,'a','b');//body
</script>
结果:
16.2 function.apply()
- function.apply(this指向谁,[参数1,参数2…])调用函数,并修改函数中的this指向
- 指向函数的apply方法,会调用该函数,并且修改函数中的this指向;
- apply中的第0个参数,代表当前执行时,函数中的this指向谁;
- apply中第1个参数是个数组,数组中代表了我们要往函数中传递的参数;且所有参数只能放在一个数组里,有多个数组时,除了第一个,其他数值的参数不会被接收
- apply和call唯一的区别在于,call方法直接在方法里传参,而apply是将所有参数已数组形式进行传递;
- 注意修改执行为body时,一定要使用document.body
<script>
function fn(a,b){
console.log(this,a,b);
}
//直接调用,this指向window
console.log("直接调用,this指向window:");
fn('s','r');
//调用函数的apply方法,更改this指向为document
console.log("调用函数的apply方法,更改this指向document:");
fn.apply(document,['2','4']);
//调用函数的apply方法,更改this指向document.body
console.log("调用函数的apply方法,更改this指向document.body:");
fn.apply(document.body,['2','4']);
</script>
结果:
16.3 function.bind()
- function.bind(指向,参数1,参数2,…)绑定this指向
- 调用函数的bind方法,会返回一个绑定了this执行的新函数;
- 第0个参数是bind返回的新函数的this指向
- 返回新函数的this指向被绑定了,不能再被更改
- 新函数的this指向在修改原函数this指向时就已经被绑定,一旦被绑定不能再次修改
总结:调用函数的bind方法,会生成新的函数,绑定的this指向是针对新函数的,新函数this指向被绑定后,不能再继续被绑定(call和apply也不行);如果调用时再传入新的参数,会将新的参数和被绑定的参数进行合并,被绑定的参数会一直存在;而原函数的this指向一直没有变,还可以继续调用bind方法,生成新的函数,同时给新的函数绑定新的this指向
<script>
function fn(a,b){
console.log(this,arguments);
}
//直接调用函数,this指向window
console.log("直接调用函数,this指向window:");
fn(1,2);//window
//使用函数的bind方法
console.log("使用函数的bind方法,返回新的函数:");
var fn2 = fn.bind(document,3,4);
console.log(fn2 == fn);//false 新函数和旧函数不是同一个
console.log("原函数的this指向:");
fn(5,6);//原函数的this指向不变,依然是window,且还可以继续调用bind方法
console.log("新的函数的this指向:");
console.log("如果新的函数调用时传入新的参数,会将绑定的参数和新传入的参数进行合并:");
fn2(7,8);//3,4,7,8 新函数的this指向即原函数绑定的this指向
//新函数的this指向在修改原函数this指向时就已经被绑定,一旦被绑定不能再次修改,且被绑定的参数也不能再被修改
//只是如果调用新函数时传入新参数,会合并两次的参数
console.log("新函数的this指向再修改原函数this指向时就已经被绑定,一旦被绑定不能再次修改:");
fn2.call(window,9,0);//这里即使再次更改this指向,fn2新函数的this指向永远不会再改变
//再次调用fn的bind方法
console.log("再次调用fn的bind方法,返回新的函数:");
var fn3 = fn.bind(document.body,'a','b');
console.log(fn3 == fn);
fn3('c','d');
</script>
结果:
16.4 bind方法原理分析:
为什么调用bind生成的新函数,this指向被绑定后就不能再绑定了?bind方法在ES5.1后才出来,如果想实现此功能,可以自己写可以再次绑定的bind方法。
自己实现bind方法:
参数:fn 要绑定this函数;_this返回新函数this指向谁
<script>
//为什么调用bind生成的新函数,this指向被绑定后就不能再绑定了
//自己实现bind方法的原理
//fn要绑定的新函数,_this给新函数绑定的this指向,...arg传参(展开运算符ES6新增)
function bind(fn,_this,...arg){
return function(...args2){
fn.call(_this,...arg,...args2);
};
}
function fn(){
console.log(this,arguments);
}
//调用bind方法,返回新的函数function(){ fn.call(_this); };
var fn2 = bind(fn,document,11);
//执行fn2()即执行返回的新函数,并给新的函数绑定this指向
fn2(2,3);//document
fn();
//重新调用绑定函数,并返回新的函数同时给其绑定this执行
var fn3 = bind(fn,document.body,12);
fn3(4,5);//this
</script>
结果:
五、对象:
在js中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串,数值,数组,函数等。
- 属性:事物的特征。(常用名词)
- 方法:事物的行为。(常用动词)
1、创建对象的三种方式:
1.1利用字面量创建对象
对象字面量;就是用呢花括号{}里面包含了表达了这个具体事物(对象)属性和方法。
<script>
var object = {}; //创建一个空的对象
var obj = {
uname: "李小龙",
age: 12,
sex: "男",
sayHi: function () {
console.log("hi~");
},
};
//(1)、里面的属性或者方法采用键值对=的形式 键 属性名: 值 属性值
//(2)、多个属性或者方法中间用逗号隔开的
//(3)、方法后面跟的是一个匿名函数
//2.使用对象
// (1)调用对象 对象名.属性名
console.log(obj.uname);
// (2)调用对象还有一种方法 对象名['属性名']
console.log(obj["age"]);
// (3)调用对象的方法 sayHi 对象名.方法名 要添加小括号
obj.sayHi();
</script>
变量、属性、函数、方法的区别:
//变量和属性的相同点: 都是用来存储数据的。
var num = 10;
var obj = {
age: 18,
fn: function () {},
};
function fn() {}
console.log(obj.age);
//1、变量 单独声明并赋值 使用的时候直接写变量名 单独存在
// 属性 在对象里面的不需要声明的,用来描述对象的特征 使用的时候必须是 对象.属性
//2、函数和方法的相同点都是 实现某种功能.
//函数是单独声明 并且调用通过 函数名() 调用 单独存在的
//方法 在对象里面 调用的时候 对象.方法名()
1.2利用new Object创建对象
var obj = new Object();//创建一个空对象
obj.uname = "鸣人";
obj.sex = "男";
obj.age = "19岁";
obj.skill = function () {
console.log("影子分身术");
};
//(1)、利用等号赋值的方法 添加对象的属性和方法 每个对象和方法之间 用分号结束
console.log(obj.sex);
console.log(obj["age"]);
obj.skill();
1.3利用构造函数创建对象
- 前面两种创建对象的方式一次只能创建一个对象。
- **构造函数:**主要用来初始化对象,即为对象成员赋初始值,它总与new运算符一起使用 。把对象里面相同的属性和方法抽象出来封装到函数里面。
/* 利用构造函数创建对象
我们需要创建四大天王的对象 相同的属性: 名字 年龄 性别 相同的方法:唱歌
function 构造函数名() {
this.属性 = 值;
this.方法 = function () {};
}
new 构造函数名(); */
function Star(uname, age, sex) {
//1、构造函数名字首字母要大写
this.name = uname;
this.age = age;
this.sex = sex;
this.sing = function (sang) {
console.log(sang);
};
}
var ldh = new Star("刘德华", 18, "男"); //调用函数返回的是一个对象
var zxy = new Star("张学友", 23, "男");
var gfc = new Star("郭富城", 25, "男");
ldh.sing("冰雨");
console.log(ldh.name);
console.log(ldh["sex"]);
console.log(zxy.name);
zxy.sing("李香兰");
//2、构造函数里面不需要return就可以返回结果
//3、调用函数的时候必须使用new
//4、只要 new Start() 调用函数就创建一个对象 ldh {}
//5、属性和方法面前必须添加this
function Hero(uname, type, blood) {
this.name = uname;
this.type = type;
this.blood = blood;
this.attack = function (way) {
console.log(way);
};
}
var lp = new Hero("廉颇", "力量型", "500血量");
var hy = new Hero("后羿", "射手型", "120血量");
console.log(lp.type);
console.log(hy.blood);
lp.attack("近战");
hy.attack("远程");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzhgABJQ-1670505761613)(C:\Users\地狱魔爵人\Desktop\截图\2022-11\chrome_LkZzKv4H9p.png)]
new关键字:
- 在内存中创建一个空对象。
- 让this指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象(所以构造函数里面不需要添加return()。
遍历对象属性;
var obj = {
name: "李小龙",
age: "19",
sex: "男",
fn: function () {},
};
// for in 遍历对象
// for (变量 in 对象){}
for (var k in obj) {
console.log(k); // k 变量 输出 得到的是属性名
console.log(obj[k]);// obj[k] 得到的是 属性值
}
//我们使用 for in 里面的变量 喜欢使用 k 或者 key 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TkEGkR6j-1670505761615)(C:\Users\地狱魔爵人\Desktop\截图\2022-11\chrome_g8jQYnyrNs.png)]
2、内置对象:
js中的对象分为三种:自定义对象,内置对象,浏览器对象
前两种对象是js基础内容,属于ECMAScript;第三个浏览器对象属于我们JS独有的 我们JS API 讲解
**内置对象 **就是指JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或者是基本而必要的功能 (属性和方法)
内置对象最大的优点就是帮助我们快速开发
JS 提供了多个内置对象: Math、Date、Array、String等
2.1查文档:MDN
如何学习对象中的方法?
可以通过MDN/W3C来查询。
MDN 提供了有关开放网络技术(Open Web)的信息,包括 HTML、CSS和万维网及HTML5应用的 API
2.2Math对象:
Math 对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用 Math 中的成员。
属性、方法名 | 功能 |
---|---|
Math.PI | 圆周率 |
Math.floor() | 向下取整 |
Math.ceil() | 向上取整 |
Math.round() | 四舍五入版 就近取整 注意 -3.5 结果是 -3 |
Math.abs() | 绝对值 |
Math.max()/Math.min() | 求最大和最小值 |
Math.random() | 获取范围在[0,1)内的随机值 |
1.Math是一个内置对象,它具有数学常数和函数的属性和方法。不是一个函数对象
2.与其他全局对象不同的是,Math不是一个构造函数,Math的所有属性和方法都是静态的,不需要是用 new 。
console.log(Math.max());*//-Infinity*
console.log(Math.abs("-10")); //隐式转换 会把字符串型 自动转换为数字型。(取绝对值) 值:10
console.log(Math.floor("1.1")); //向下取整数 值:1
console.log(Math.ceil(1.1)); //向上取整数 值:2
console.log(Math.round(-1.5)); //四舍五入 值:-1 注意:.5 往大的值取
<script>
//利用对象封装自己的数学对象 里面有PI 最大值和最小值
var myMath = {
PI: 3.141592653,
max: function () {
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
},
min: function () {
var min = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] < min) {
min = arguments[i];
}
}
return min;
},
};
console.log(myMath.max(11, 5, 9));
console.log(myMath.PI);
console.log(myMath.min(12, 76, 3));
</script>
Math.random() :一个浮点型伪随机数字,在0
(包括 0)和1
(不包括)之间。 此方法里面不跟参数
function getRandom(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
}
console.log(getRandom(1, 100));
var arr = ["张三","张三丰","李小龙","大黄","星星","琪琪","惠虎红","大傻逼"];
console.log(arr[getRandom(0, arr.length - 1)]);
//猜数字游戏
function getRandom(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) - min);
}
var random = getRandom(1, 50);
alert("您共有3次机会,请输入1~50之间的数字");
var i = 0;
while (i <= 2) {
//死循环
var num = parseFloat(prompt("请输入您要猜的数字:"));
if (random > num) {
alert("对不起,您猜小了!\n 还剩余2次机会");
} else if (random < num) {
alert("对不起,您猜大了!\n 还剩余1次机会");
} else if (random == num) {
alert("恭喜您猜对了!");
break;
} else {
alert("您输入不合法!\n 浪费1次机会");
}
i++;
}
alert("抱歉,您的机会用完了!");
2.3日期对象:
Date 对象和 Math 对象不一样,Date是一个构造函数,所以使用时需要实例化后才能使用其中具体方法和属性。Date 实例用来处理日期和时间
创建一个新Date
对象的唯一方法是通过new
操作符,例如:var date = new Date();
若将它作为常规函数调用(即不加 new
操作符),将返回一个字符串,而非 Date
对象。
如果没有输入任何参数,则 Date 的构造器会依据系统设置的当前时间来创建一个 Date 对象。
如果括号里面有时间,就返回参数里面的时间,例如日期格式字符串为’2019-5-9’,可以写成new Date(‘2019-5-9’) 或者
new Date(2019/5/9)
使用Date实例的方法和属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQAmNZWN-1670505761619)(C:\Users\地狱魔爵人\Desktop\截图\2022-11\chrome_t5XnbPRtCb.png)]
//获取当前时间
function getTime() {
var time = new Date();
var h = time.getHours();
h = h < 10 ? "0" + h : h;
var m = time.getMinutes();
m = m < 10 ? "0" + m : m;
var s = time.getSeconds();
s = s < 10 ? "0" + s : s;
return h + ":" + m + ":" + s;
}
console.log(getTime());
获得 Date 总的毫秒数 (时间戳)
不是当前时间的毫秒数, 而是距离1970年1月1号过了多少毫秒数:
1.通过valueOf() 或 getTime()
var date = new Date();
console.log(date.value0f()); //就是我们现在时间距离1970.1.1 总的毫秒数
console.log(date.getTime());
2、简单的写法(最常用的写法)
var date1 = +new Date(); // +new Date() 返回的就是总的毫秒数
console.log(date1);
+new Date(time) //距离 time 时间过去的总毫秒数
3、H5 新增的 获得总的毫秒数
console.log(Date.now());//注意大写
制作 倒计时
function countDown(time) {
var nowTime = +new Date(); //返回的是当前时间总的毫秒数
var inputTime = +new Date(time); //返回的是用户输入时间总的毫秒数
var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数
var d = parseInt(times / 60 / 60 / 24); //天
d = d < 10 ? '0' + d :d;
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0'+h:h;
var m = parseInt(times / 60 % 60); //分
m = m < 10 ? '0' + m : m;
var s = parseInt(times % 60); //当前的秒
s = s < 10 ? '0' + s :s;
return d + '天' + h + '时' + m + '分' +s+'秒';
}
console.log(countDown('2022-2-9 8:00:00'));
2. 4 数组对象
利用 new Array() 创建数组
var arr1 = new Array();//创建了一个空的数组
var arr1 = new Array(2);//这个2表示数组的长度为2里面有2个空的数组元素
var arr1 = new Array(2, 3); //等价于[2,3] 这样写表示里面有2个数组元素是2和3
console.log(arr1);
在 new Array() 括号中
写一个数字 就代表这个数组有 几个空的数组元素
( new Array(2)这个2表示数组的长度为2,里面有2个空的数组元素 )
写多个数字 就代表 这个数组就是 由这几个数字组成的数组
new Array(2, 3);
( 等价于[2,3] 这样写表示里面有2个数组元素是2和3 )
检测是否为数组
(1) instanceof 运算符 它可以用来检测是否为数组 运算符 不只是Array 其他也行
//翻转数组
function reverse(arr) {
//判断是否为数组
if (Array.isArray(arr)) {
// if (arr instanceof Array) {
var newArr = [];
for (var i = arr.length - 1; i >= 0; i--) {
newArr[newArr.length] = arr[i];
}
return newArr;
} else {
//返回错误信息
return "这个参数必须要求是数组格式[1,2,3]";
}
}
console.log(reverse([1, 2, 3]));
console.log(reverse(1, 2, 3)); //[]
//检测是否为数组
//(1)instanceof 运算符 它可以用来检测是否为数组
var arr = [];
console.log(arr instanceof Array); //true
var obj = {};
console.log(obj instanceof Array); //false
//(2) **Array.isArray( 参数 )** ; H5新增的方法 ie9以上版本支持
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
添加删除数组元素的方法
-
数组中有进行增加、删除元素的方法,部分方法如下表
方法名 说明 返回值 push(参数1…) 末尾添加一个或者多个元素,注意修改原数组 并返回新的长度 pop() 删除数组最后一个元素,把数组长度减1 无参数、修改原数 返回它删除元素的值 unshift(参数1…) 向数组的开头添加一个或者更多的元素,注意修改原数组 并按返回新的长度 shift 删除数组的第一个元素,数组长度减1无参数、修改原数组 并返回第一各元素的值
注意:push、unshift为增加元素方法;pop、shift为删除元素的方法
数组排序
-
数组中有对数组本身排序的方法,部分方法如下表
方法名 说明 是否修改原数组 reverse() 颠倒数组中元素的顺序,无参数 该方法会改变原来的数组 返回新数组 sort() 对数组的元素进行排序(个位数会自动排序) 该方法会改变原来的数组 返回新数组
注意:sort方法需要传入参数来设置升序、降序排序
- 如果传入“function(a,b){ return a-b;}”,则为升序
- 如果传入“function(a,b){ return b-a;}”,则为降序
//数组排序(冒泡排序)
var arr1 = [1, 43, 6, 12, 0, 3, 58];
arr1.sort(function (a, b) {
// return a - b; //升序
return b - a; //降序
});
console.log(arr1);
数组索引方法
-
数组中有获取数组指定元素索引值的方法,部分方法如下表
方法名 说明 返回值 indexOf() 数组中查找给定元素的第一个索引 如果存在返回索引号 如果不存在,则返回-1 lastIndexOf() 在数组中的最后一个索引 如果存在返回索引号 如果不存在,则返回-1
//返回数组元素索引号的方法
var arr2 = ["pink", "black", "red", "write"];
console.log(arr2.indexOf("red"));
/* 数组去重: 把旧数组里面不重复的元素选取出来放到新数组里面,重复的元素只保留一个
核心算法: 遍历旧的数组,拿着旧数组里面的元素去查询新数组,如果该元素在新数组里面没有就添加,否则不添加。
判断元素有没有存在的方法: 利用 新数组.indexOf(数组元素) 如果返回值为 -1 就说明新数组里面没有该元素 */
//封装一个去重的函数 unique 独一无二的
function unique(arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
var demo = unique(["c", "a", "z", "a", "x", "a", "x", "c", "b"]);
console.log(demo);
数组转换为字符串
-
数组中有把数组转化为字符串的方法,部分方法如下表
方法名 说明 返回值 toString() 把数组转换成字符串,逗号分开每一项 返回一个字符串 join(‘分隔符’) 方法用于把数组中所有元素转换成为一个字符串 返回一个字符串
注意:join方法如果不传入参数,则按照 “ , ”拼接元素
其他方法
-
数组中还有其他操作方法
方法名 说明 返回值 concat() 连接两个或者多个数组 不影响原数组 返回一个新的数组 slice() 数组截取slice(begin,end) 返回被截取项目的新数组 splice() 数组删除splice(第几个开始,要删除的个数) 返回被删除项目的新数组 注意,这个会影响原数组 split() str.split(“,”) 根据逗号拆分 将字符串拆分为数组
2.5字符串对象:
基本包装类型
为了方便操作基本数据类型,JavaScript 还提供了三个特殊的引用类型:String、Number和 Boolean。
基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。
// 下面代码有什么问题?
var str = 'andy';
console.log(str.length); //4
按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为 js 会把基本数据类型包装为复杂数据类型,其执行过程如下 :
// 1. 生成临时变量,把简单类型包装为复杂数据类型
var temp = new String('andy');
// 2. 赋值给我们声明的字符变量
str = temp;
// 3. 销毁临时变量
temp = null;
字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
当重新给字符串变量赋值的时候,变量之前保存的字符串不会被修改,依然在内存中重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变。
由于字符串的不可变,在大量拼接字符串的时候会有效率问题。
根据字符返回位置
字符串通过基本包装类型可以调用部分方法来操作字符串,以下是返回指定字符的位置的方法:
案例:查找字符串"abcoefoxyozzopp"中所有o出现的位置以及次数
-
先查找第一个o出现的位置
-
然后 只要indexOf 返回的结果不是 -1 就继续往后查找
-
因为indexOf 只能查找到第一个,所以后面的查找,利用第二个参数,当前索引加1,从而继续查找
//str.indexOf('要查找的字符',[起始的位置]) var str = "oabcoefoxyozzopp"; var index = str.indexOf("o"); var num = 0; while (index !== -1) { console.log(index); num++; index = str.indexOf("o", index + 1); } console.log("o出现的次数是" + num);
根据位置返回字符
字符串通过基本包装类型可以调用部分方法来操作字符串,以下是根据位置返回指定位置上的字符:
在上述方法中,charCodeAt方法返回的是指定位置上字符对应的ASCII码,ASCII码对照表如下:
案例:判断一个字符串 ‘abcoefoxyozzopp’ 中出现次数最多的字符,并统计其次数
- 核心算法:利用 charAt() 遍历这个字符串
- 把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1
- 遍历对象,得到最大值和该字符
注意:在遍历的过程中,把字符串中的每个字符作为对象的属性存储在对象总,对应的属性值是该字符出现的次数
//根据位置返回字符
var str = "abcoefoxyozzopp";
var obj = {};
for (var i = 0; i < str.length; i++) {
var chars = str.charAt(i); //chars 是字符串的每一个字符
if (obj[chars]) {
//obj[chars]得到的是属性值
obj[chars]++;
} else {
obj[chars] = 1;
}
}
console.log(obj);
// 遍历对象
// k 得到的是属性名
// obj[k] 得到的是属性值
var max = 0;
var ch = "";
for (var k in obj) {
if (obj[k] > max) {
max = obj[k];
ch = k; //k除了for循环就不起作用了
}
}
console.log(max);
console.log("出现最多的字符是" + ch);
字符串操作方法
字符串通过基本包装类型可以调用部分方法来操作字符串,以下是部分操作方法:
substring()还会自动调节参数的位置,如果第二个参数小于第一个,则自动交换。
replace()方法
replace() 方法用于在字符串中用一些字符替换另一些字符,其使用格式如下:
字符串.replace(被替换的字符串, 要替换为的字符串);
//把o替换成*号
var str = "abcoefoxyozzopp";
while (str.indexOf("o") !== -1) {
str = str.replace("o", "*");
}
console.log(str);
split()方法
split()方法用于切分字符串,它可以将字符串切分为数组。在切分完毕之后,返回的是一个新数组。
其使用格式如下:
字符串.split("分割字符")
//2.字符替换为数组
var str2 = "red,pink,blue";
console.log(str2.split(","));
var str3 = "red&pink&blue";
console.log(str3.split("&"));
3、正则表达式:
定义一下字符串的规则。
计算机可以根据正则表达式,来检查一个字符串是否符合规则
,将获取字符串中符合规则的内容提取出来。
正则表达式是一个对象。
<script type="text/javascript">
/* 1、正则表达式 语法: var 变量 = new RegExp("正则表达式","匹配模式");
在构造函数里面可以传递一个匹配模式作为参数,可以是 i :忽略大小写 g :全局匹配模式
正则表达式的方法: test() 使用这个方法用来检查一个字符串是否符合正则表达式的规则,
如果符合返回true,否则返回false
*/
/* var reg = new RegExp('a', 'i');
var str = 'a';
// console.log(typeof reg); //object
var result = reg.test('Achub');
console.log(result); //true */
/* 2、使用字面量来创建正则表达式:
语法: var 变量 = /正则表达式/匹配模式
使用字面量的方式更加简单,但是使用构造函数创建更加灵活
*/
/* var reg = /a/i;
console.log(reg.test('abc')); */
// 3、创建一个正则表达式,检查一个字符串是否有a或b或c,使用|表示或者
/* var reg = /a|b|c/;
console.log(reg.test('acd'));//true */
/* 4、创建一个正则表达式检查一个字符串中是否有字母 []里面的内容也是或的关系[ab]=a|b
[a-z]表示任意小写字母 [A-z]表示任意字母 */
/* var reg = /[A-z]/;
console.log(reg.test('M')); //true*/
/* 5、检查一个字符串中是否含有abc或者adc或者aec */
/* var reg = /a[b-e]c/;
console.log(reg.test('aec'));//true */
// 6、[^ ] 除了...以外的
/* var reg = /[^ab]/;
console.log(reg.test('abc')); //true */
// 7、split()根据任意字母将字符串拆分 ,将字符串拆分为一个数组。此方法即使不指定全局匹配,也会拆分
/* var str = '1a2b3c4d5e6f7h8i9g';
var result = str.split(/[a-z]/);
console.log(result); // ['1', '2', '3', '4', '5', '6', '7', '8', '9', '']
console.log(result.length); //10 */
/*8、 search() 可以搜索字符串中是否含有指定的内容
如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到则返回-1
可以接收一个正则表达式作为参数,根据正则表达式去检索字符串
search() 只会查找第一个,即使设置全局匹配也没有用 */
/* var str = 'hello abc hello aec afc';
result = str.search(/[a-c]/);
console.log(result); //6 */
/*9、 match() 可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
默认情况下match只会找到第一个符合要求的内容,找到以后就停止检索
可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
match() 会将匹配到到的内容封装到一个数组中返回,即使查询到一个结果*/
/* var str = '1a2B3c4d5E6f7h8I9g';
result = str.match(/[A-z]/gi); //gi或者ig表示即全局匹配又忽略大小写
console.log(result); // ['a', 'B', 'c', 'd', 'E', 'f', 'h', 'I', 'g'] */
/*10、 replace() 可以将字符串中中指定的内容替换为新的内容
1、被替换的内容(可以接收一个正则表达式作为参数) 2、新的内容 默认只会替换第一个*/
/* var str = '1a2B3a4d5E6f7a8I9a';
result = str.replace(/[A-z]/gi, '@@'); //可以使用'' ,将所有字符替换为空
console.log(result); //1@@2@@3@@4@@5@@6@@7@@8@@9@@ */
/* 11、创建一个正则表达式检查一个字符串是否含有ab
量词: 通过量词可以设置一个内容出现的次数 {n} 正好出现n次 {m,n}正好出现m-n次 {m, }m次以上
+ 表示至少一个 * 表示有没有都行 ?表示0或1个*/
/* var reg = /a(ab){1,3}b/; //{1,3}表示(ab)出现的次数为1-3次
console.log(reg.test('aababb')); //true */
/*12、检查一个字符串是否以a为开头 /^a/匹配开头的a /a$/匹配结尾的a /^a$/匹配的结果只能是一个a
/^a|a$/匹配到的是以a开头或者是以a结尾 */
/* var reg = /^a/;
console.log(reg.test('bbwac')); //false */
/* 13、创建一个正则表达式,用来检查一个字符串是否是一个合法的手机号
手机号规则:
1、(11位) 2、以1开头 3、第二位:3-9 任意数字 4、三位之后:任意数字0-9个
^1 [3-9] [0-9]{9}$ */
/* var phoneStr = '18339635718';
var phoneReg = /^1[3-9][0-9]{9}$/;
console.log(phoneReg.test(phoneStr)); */
/* 14、检查一个字符串中是否含有 . .表示的任意字符 在正则表达式中使用\作为转移字符
\\ 表示一个\*/
/* var reg = /\\./;
console.log(reg.test('gd.\\'));
console.log('gd.\\'); // gd.\ */
/* 15. 取出字符串前后空格 */
var str = ' he llo ';
str = str.replace(/^\s*|\s*$/g, '');
console.log(str); // he llo
/* 16 、电子邮件:
hello .nihao @ abc .com.cn
任意字母数字下划线 .任意字母数字下划线 @ 任意字母数字 .任意字母(2-5位) .任意字母(2-5位)
\w{3,} (\.\w+)* @ [A-z0-9]+ (\.[A-z]{2,5}){1,2}*/
var emaiReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;
var email = 'abc@abc.com.123';
console.log(emaiReg.test(email)); //false
4、 简单数据类型和复杂数据类型
3.1 简单数据类型
简单类型(基本数据类型、值类型):在存储时变量中存储的是值本身,包括string ,number,boolean,undefined,null
null返回的是一个空对象
3.2 复杂数据类型
复杂数据类型(引用类型):在存储时变量中存储的仅仅是地址(引用),通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等;
3.3 堆栈
- 堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
-
简单数据类型的存储方式
值类型变量的数据直接存放在变量(栈空间)中
-
复杂数据类型的存储方式
引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
3.4 简单类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
function fn(a) {
a++;
console.log(a);
}
var x = 10;
fn(x);
console.log(x);
运行结果如下:
3.5 复杂数据类型传参
函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。
function Person(name) {
this.name = name;
}
function f1(x) { // x = p p把地址赋值给了x
console.log(x.name); // 2. 这个输出什么 ? //刘德华
x.name = "张学友";
console.log(x.name); // 3. 这个输出什么 ? //张学友
}
var p = new Person("刘德华");
console.log(p.name); // 1. 这个输出什么 ? //刘德华
f1(p);
console.log(p.name); // 4. 这个输出什么 ? //张学友
运行结果如下: