JS所有内容总结(ECMAScript、DOM、BOM)

JS基础(ECMAscript)

了解编程语言

编程

  • 编程:就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到結果的过程

  • 计算机程序:就是计算机所执行的一系列的指令集合,而程序全部都是用我们所掌握的语言来编写的,所以
    人们要控制计算机一定要通过计算机语言向计算机发出命令。

  • 从事编程的人员,就是程序员。但是般程序员都比较幽默,为了形容自己的辛苦工作,也成为“码农
    或者“程序猿”/“程序媛

计算机语言

  • 计算机语言:指用于人与计算机之间通讯的语言,它是人与计算机之间传递信息的媒介

  • 计算机语言的种类非常的多,总的来说可以分成机器语言,汇编语言高级语言三大类

  • 实际上计算机最终所执行的都是**机器语言**它是由“0和“1”组成的二进制数,二进制是计算机语言的基础

    0=00000000    1=00000001        2=00000010        3=00000011     
    4=00000100    5=00000101    	6=00000110        7=00000111   
    8=00001000    9=00001001 		10=00001010
    

计算机语言

  • 可以通过类似于人类语言的”语言”来控制计算机,让计算机为我们做事情,这样的语言就叫做编編程语言( Programming Language)。

  • 编程语言是用来控制计算机的系列指令,它有固定的格式和词汇(不同编程语言的格式和词汇不一样),必须遵守。
    如今通用的编程语言有两种形式:汇编语言和高级语言。

  • 汇编语言 和机器语言实质是相同的,都是直接对硬件操作,只不过指令采用了英文缩写的标识符,容易识别和记忆

  • 高级语言:主要是相对于低级语言而言,它并不是特指某种具体的语言,而是包括了很多编程语言,常用的有C语言、C++ 、Java、C#、 Python、PHP、 JavaScript、Go语言、 Objective-C、Swif等。

    C语言:puts("你好”);
    
    PHP:echo"你好";
    
    Java: system.out, print1n("你好");
    
    JavaScript: alert("你好");
    

编译器

  • 高级语言所编制的程序不能直接被计算机识别,必须经过转换才能被执行,为此,我们需要一个翻译器。

  • 翻译器 可以将我们所编写的源代码转换为机器语言,这也被称为二进制化。记住1和0。

在这里插入图片描述

编译语言与标记语言区别

  • 编程语言 有很强的逻辑和行为能力。在编程语言里你会看到很多 if else、for、 while等具有逻辑性和行为能力的指令,这是主动的。

  • 标记语言(htm) 不用于向计算机发出指令,常用于格式化和链接。标记语言的存在是用来被读取的他是被动的。

在这里插入图片描述

总结

  1. 计算机可以帮助人类解決某些问题
  2. 程序员利用编程语言编写程序发出指令控制计算机来实现这些任务
  3. 编程语言有机器语言、汇编语言、高级语言
  4. 高级语言需要—个翻译器转换为计算机识别的机器语言
  5. 编程语言是主动的有很强的逻辑性

计算机基础

计算机组成

在这里插入图片描述

硬件

在这里插入图片描述

数据存储

  • 1.计算机内部使用二进制0和1来表示数据。
  • 2.所有数据,包括文件、图片等最终都是以二进制数据(0和1)的形式存放在硬盘中的。
  • 3.所有程序,包括操作系统,本质都是各种数据,也以进制数据的形式存放在硬盘中。平时我们所说的安
    装软件,其实就是把程序文件复制到硬盘中
    1. 硬盘、内存都是保存的二进制数据

数据存储单位

bit byte < kb GB< TB<…

  • 位(bit):1bit可以保存一个0或者1(最小的存储单位)
  • 字节(Byte):1B=8b
  • 干字节(KB):1KB=1024B
  • 兆字节MB):1MB=1024KB
  • 吉字节(GB):1GB=1024MB
  • ·太字节(TB):1TB=1024GB

程序运行

在这里插入图片描述

  1. 打开某个程序是,先从硬盘中把程序的代码加载到内存中
  2. CPU 执行内存中的代码

【注】:之所以需要内存有个重要原因,是因为cpu运行太快了,如果只从硬盘中读数据,会浪费cpu性能,所
以,才使用存取速度更快的内存来保存运行时的数据。(内存是电,硬盘是机械)

认识 JS

JS 历史

  • 布兰登艾奇( Brendan eich,1961年~)。

  • 神奇的大哥在1995年利用10天完成 JavaScript设计

  • 网景公司最初命名为 Live Script,后来在与Sun合作之后将其改名为 JavaScript.

JS 是什么

  • JavaScript是世界上最流行的语言之一,是一种运行在客户端的脚本语言( Script是脚本的意思)
  • 脚本语言:不需要编译,运行过程中由js解释器(js引擎)逐行来进行解释并执行
  • 现在也可以基于**Node. js** 技术进行服务器端编程

JS 作用

  • ·表单动态校验(密码强度检测)(JS产生最初的目的)
  • 网页特效
  • 服务端开发( Node. js)
  • 桌面程序( lectron App(Cordova)
  • 控制硬件-物联网(Ruf)
  • 游戏开发( COCOS2d-js

浏览器执行 JS 简介

浏览器分成两部分:渲染引擎和 JS 引攣

  • 渲染引擎: 用来解析HTM与CSS,俗称内核,比如 chrome浏览器的bink,老版本的 webkit
  • JS引擎: 也为JS解释器。用来读取网页中的 javascript 代码,对其处理后运行,比如 chrome浏览器的v8

浏览器本身并不会执行 JS 代码,而是通过内置 JavaScript引擎(解释器)来执行J代码。JS 引擎执行代码时逐行解释

每一句源码(转换为机器语言),然后由计算机去执行,所以 JavaScript语言归为脚本语言,会逐行解释执行。

js 组成

  • DOM文档对象模型( Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。

    通过DOM提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)。

  • BOM浏览器对象模型( Browser Object Model,简称BOM)是指浏览器对象模型,它提供了独立于内容的、可以与浏览器窗口进行

    互动的对象结构。通过BOM可以操作浏饩器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。

Js特点

  • js 是解释性语言:(不需要编译成文件)跨平台

JavaScript 输入输出语句

方法说明归属
alert(msg)浏览器弹出警告框浏览器
console.log(msg)浏览器控制台打印输出信息浏览器
prompt(info)浏览器弹出输出框,用户可输入浏览器

主流浏览器

主流浏览器(必须有独立内核)市场份额大于 3%内核名称
IEtrident
chromewebkit/blink
firefoxgecko
operapresto
safarisafari

变量

变量是 ?

白话:变量就是一个装东西的盒子

通俗:变量是用来存放数据的 容器,我们通过 变量名 获取变量,甚至数据可以修改

在这里插入图片描述

变量在内层中的存储

本质:变量是程序内存中申请的一块用来存放数据的空间。

类似我们的酒店的房间,一个房间就可以看成一个变量。

在这里插入图片描述

变量的声明

∥ 声明变量
var age; ∥声明个名称为age的变量
  • var 是一个JS关键字,用来声明变量( variable变量的意思)。使用该关键字声明变量后,计算机会自动为变量分配
    内存空间,不需要程序员管

  • age是程序员定义的变量名,我们要通过变量名来访问内存中分配的空间

变量的赋值

age=10; ∥给age这个变量赋值为10
  • = 用来把右边的值赋给左边的变量空间中此处代表赋值的意思
  • 变量值是程序员保存到变量空可里的值

声明变量的特殊情况

一个变量被重新赋值后,它原有的值就会被覆盖,变量值将以最后一次赋的值为准。

var age = 18;
	age = 81;  //最后的结果为81,因为18被覆盖掉了。
情况说明结果
var age; console.log(age);只声明 不赋值undefined
console.log(age);不声明,不赋值 直接使用报错
age = 10; console.log(age)不声明 只赋值10 (不建议这样写)

变量命名规范

  • 由字母(A-Za-z)数字(0-9)、下划线( _ )美元符号($)组成,如: usrAge,num01,_name;
  • 严格区分大小写。 var app;和 var App是两个变量.
  • 不能以数字开头。18age是错误的
  • 不能是关键字、保留字。例如:var、for、 while
  • ·变量名必须有意义。 MMD BBD n→age ❌
  • 遵守驼峰命名法。首字母小写,后面单词的首字母需要大写。 myFirstName
  • 推荐翻译网站: 有道 爱词霸

变量总结

小结解释
变量是什么?变量就是一个容器,用来存放数据的。方便我们以后使用里面的数据
变量的本质是什么?变量是内存里的一块空间,用来存储数据。
变量怎么使用的?我们使用变量的时候,一定要声明变量,然后赋值
声明变量本质是去内存申请空间。
什么是变量的初始化?声明变量并赋值我们称之为变量的初贻始化
变量命名规范有哪些?变量名尽量要规范,见名知意驼峰命名法

变量的练习一

有个叫卡卡西的人在旅店登记的时候前台让他填一张表,这张表里的内容要存到电脑上,表中的内容有:

姓名、年龄、邮箱、家庭住址和工资,存储之后需要把这些信息显示出来,所显示的内容如下

<script>
    // 我叫旗木卡卡西,我住在火影村,我今年30岁了,我的邮箱是kakaxi@toast.cn, 我的工资2000
    var myname = '旗木卡卡西';
    var myaddress = '火影村';
    var age =  30;
    var myEmail = 'kakaxi@toast.cn';
    var mysalary = 2000;
    console.log('我叫:'+ myname+';我住在'+myaddress+
    ';我今年'+age+';我的邮箱是:'+myEmail+';我的工资是:'+mysalary);
    
</script>

变量的练习二

  • 1.弹出一个输入框,提示用户输入姓名。
  • 2.弹出一个对话框,输出用户刚才输入的姓名。
<script>
    var youname = prompt('请输入您的名字:');
    alert('欢迎:'+youname);
</script>

变量的练习三

  • 交换两个变量的值 (实现思路:使用一个临时变量用来做中间存储)

     <script>
            var temp;
            var redApple = '红苹果';
            var greenApple = '青苹果';
            temp = redApple;
            redApple = greenApple;
            greenApple = temp;
            console.log(redApple);
            console.log(greenApple);
       </script>
    

在这里插入图片描述

数据类型

简介

在计算机中,不同的数据所需占用的存储空间是不同的,为了便于把数据分成所需内存大小不同的数据,充分利

用存储空间,于是定义了不同的数据类型。

简单来说,数据类型就是数据的类别型号。比如姓名“张三”,年龄18,这些数据的类型是不一样的。

变量的数据类型

变量是用来存储值的所在处,它们有名字和数据类型。变量的数据类型决定了如何将代表这些值的位存储到计算机

的内存中。 JavaScript是一种弱类型或者说动态语言。这意味着不用提前声明变量的类型,在程序运行过程中,类型

会被自动确定

var age =10;  // 这是一个数字型
var areYouok = '是的'; ∥这是一个字符串

在代码运行时,变量的数据类型是由 JS 引擎根据 =右边变量值的数据类型来判断的,运行完毕之后, 变量就确定
了数据类型

JavaScript拥有动态类型,同时也意味着相同的变量可用作不同的类型

var x = 6; ∥x为数字
var x = 'Bill'; ∥x为字符串

简单的数据类型 (基本数据类型)

简单数据类型说明默认值
Number数字型,包含整型值和浮点型值,如21、0.210
Boolean布尔值类型,如true、 false,等价于1和0false
String字符串类型,如 “张三” 注意咱们s里面,字符串都带引号“”
Undefinedvar a; 声明了变量a但是没有给值, 此时 a= undefinedundefined
Nullvara=null; 声明了变量a为空值null

1. Number

最常见的进制有二进制、八进制、十进制、十六进制。

// 1.八进制数字序列范围:0~7
var num1 = 07; //对应十进制的7
var num2 = 019; //对应十进制的19
var num3 = 08; //对应十进制的8
// 2+六进制数字序列范围:0~9以及A~F
var num =OxA;

在JS中八进制前面加 0 ,十六进制前面加 0x

数字型三个特殊值

alert(Infinity); 	//Infinity
alert(-Infinity);	 // -Infinity
alert(NaN)
  • nfinity,代表无穷大,大于任何数值
  • -Infinity,代表无穷小,小于任何数值
  • NaN, Not a number,代表—个非数值

2. String

字符串型可以是引号中的任意文本,其语法为双引号和单引号

var strMsg = "我爱北京天安门~;	//使用双引号表示字符串
var strMsg2 = '我爱吃猪蹄~';	//使用单引号表示字符串

//常见错误
var strMsg3=我爱大肘子;	// 报错,没使用引号,会被认为是js代码,但js没有这些语法

因为HTML标签里面的属性使用的是双引号 JS 更推荐使用 单引号。

字符串转义符

转义符解释说明
\n换行符,n 是newline 的意思
\\斜杠 \
\’‘ 单引号
\"“ 双引号
\ttab 缩进
\b空格,b 是 blank 的意思

字符串长度 length

var stRMsg = "帅气多金的程序员";
alert(strMag.length); //显示8

字符串拼接

  • 多个字符串之间可以使用+进行拼接,其拼接方式为字符串+任何类型=拼接之后的新字符串
  • 拼接前会把与字符串相加的任何类型转成字符串,再拼接成一个新的字符串
//1.1 字符串 "相加"
alert('hello'+' '+'world');	// hello world
//1.2 数值字符串相加
alert(100+'100');//100100
//1.3数值字符串+数值
alert("11"+12);	//1112

// 加强
var age = 18;
console('我今年'+age+'岁');   //使用 + + 拼接

+号总结口诀:数值相加,字符相连

小练习

弹出一个输入框,提醒用户输入年龄,之后弹出一个警示框显示“您今年xx岁了”。(xx是刚才输入的年龄)

var age = prompt('请输入您的年龄');
alert('您今年'+age+'岁了');

3. Boolean

布尔类型有两个值:true 和 false,其中 true 表示真(对),false 表示假(错)

布尔类型的值和数字型相加的时候。true 的值为 1,false 的值为 0;

console.log(true + 1);	//2
console.log(false + 1);	//1;

4. Undefined 和 Null

一个声明后没有被赋值的变量会有一个默认的值 undefined

var variable;
console.log(variable);			//undefined
console.log('你好'+ variable);   //你好undefined
console.log(11+variable);		//NaN
console.log(true + variable);	//NaN

一个声明变量给null值,里面存的值为空

var vari = null;
console.log('你好' + vari);	//你好null
console.log(11 + vari);		 //11
console.log(true + vari);	  //1

字面量

字面量就是源代码中一个固定的表示法,通俗来讲,就是字面量表示如何表达这个值

  • 数字字面量:8,9,10
  • 字符串字面量:‘我’,’你‘,’她‘
  • 布尔字面量:true,false

数据类型转换

使用表单,prompt 获取过来的数据默认是字符串类型的,此时就不能简单的进行数学运算,而需要转换的数据类型,通俗的讲就是把一种数据类型的变量转换成另外一种数据类型

常用的有三种类型的转换

  • 转换为字符串类型

    方式说明案例
    toString()转成字符串var num = 1;
    alert(num.toString())
    String() 强制转换转成字符串var num = 1;
    alert(String(num))
    加号拼接字符串 (使用最多)和字符串拼接的结果都是字符串var num = 1;
    alert(num+‘我是字符串’)
    • toString() 和 String() 使用方式不一样。
    • toString()转换 undefined 类型和 null 会报错,而String()不会
    • 三种转换方式,最常用的就是+ 号拼接,这一种方式也被称为隐式转换
  • 转换为数字型 ♦️

    方式说明案例
    parseInt(string) 函数将string类型转换成整数值型parseInt(‘78’)
    parseFaoat(string) 函数将string类型转换成浮点数值型parseFloat(‘78.21’)
    Number() 强制转换函数将string类型转换为数值型Number(‘12’)
    js 隐式转换(- * / )利用算术运算隐式转换数值型‘12’ - 0

    计算年龄案例:

    在页面上弹出一个输入框,我们输入出生年份后,能计算出我们的年龄

     var year = +prompt('请输入出生年份');   // + 号会隐式转换
     var age = 2020 - year;
     alert('你今年:'+age+'岁了');
    

    简单加法器案例:

    计算两个数,用户输入第一个值后,继续弹出第二个输入框并输入第二个值,最后通过弹出窗口显示两个输入值相加的结果

    var num1 = +prompt('请输入第一个数字');
    var num2 = +prompt('请输入第二个数字');
    var result = num1 + num2;
    alert(num1 + '+' + num2+'='+result);
    

    打印用户信息案例:

    以此询问并获取用户的姓名、年龄、性别、并打印用户信息

    var yName = prompt('请输入您的姓名:');
    var age = prompt('请输入您的年龄:');
    var sex = prompt('请输入您的性别:');
    alert('您的姓名是:'+ yName +
          '\n您的年龄是:'+ age +
          '\n您的性别是:'+sex );
    
  • 转换为布尔值

    方式说明案例
    Boolean() 函数其它类型转成布尔值Boolean(‘true’)
    • 代表空、否定的值会被转换成 false,如:、0、NaN、null、undefined、document.all
    • 其余值都会被转换 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(document.all));	//false
    console.log(Boolean('小白'));		   //true
    console.log(Boolean(12));			//true
    

数据类型(具体)

简单数据类型和复杂数据类型

  • 简单类型又叫做基本数据类型或者 值类型, 复杂类型又叫做 引用类型

  • 值类型: 简单数据类型 / 基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型

  • string number boolean undefined null

  • 引用类型: 复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型

    通过new关键字创建的对象(系统对象、自定义对象), 如 Object、Aray、Date

堆 和 栈

堆栈空间分配区别:

1.栈(操作系统: 由操作系统自动分配释放 存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈

简单数据类型存放到栈里面

2.堆(操作系统): 存储复杂类型对象,一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收

复杂数据类型存放到堆里面

简单数据传参

函数的形参也可以看做是一个变量, 当我们把个值类型变量作为参数传给函数的形参时,其实是把变量在栈

空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量

function fn(a) {
    a++
	console. log(a);
}
var x= 10
fn(x);			// 11
console.log(x); // 10

复杂数据传参

函数的形参也可以看做是一个变量, 当我们把引用类型 变量传给形参时,其实是把变量在栈空间里保存的堆地

址复制给了形参,形参和实参其实保存的是同个堆地址,所以操作的是同个象

function Person(name) {
    this name= name;
}

function f1(x){			  //x-p
    console.1og(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.这个输出什么?	// '张学友'

JS运算符

简介

运算符(operator)也被称为操作数,是用来实现赋值、比较和执行算数运算等功能的符号。

算术运算符

算术运算使用的符号,用于执行两个变量或值的算术运算

运算符描述实例
+10 + 20 = 30
-10 - 20 = -10
*10 * 20 = 200
/10 / 20 = 0.5
%取余数(取模)返回除法的余数 9 % 2 = 1
console.log(0.1 + 0.2);  //0.30000000000000004 
console.log(0.07 * 100); //7.000000000000001

var num = 0.1 + 0.2;
console.log(num == 0.3);  //false...........

小数运算最后会转换成机器码(二进制)而出现误差,应避免使用小数运算。
不要使用直接去判断两个浮点数是否相等

小思考:

  1. 怎么判断一个数能够被整除呢?

    它的余数是 0 就说明这个数能够被整除,这就是 % 取余运算符的主要用途

  2. 1 + 2 * 3 = ?

    结果是 7,算术运算符是有优先级的,先乘除,后加减,有小括号先算小括号里面的

表达式和返回值:

表达式:是由数字、运算符、变量等组成的式子

表达式最终都会有一个结果,返回给我们,我们称为返回值

递增和递减运算符

++num前置递增,就是自加1,先自身加 1 ,在返回值

num++后置递增,先返回原值,在自身加 1

var a = 10;
++a;				//先自身加 1; a = 11
var b = ++a +2;		//++a 又先自加一次变成 12 b = 12+2;
console.log(b);		//14

var c = 10;
c++;				//c = 10  c++后自加1 是11
var d = c++ + 2;	//c = 11,变量变化改变了原来的10, c = 11, c++ = 12; d = 11 + 2
comsole.log(d);		//d = 13

var e = 10;
var f = e++ + ++e;	//e = 10,e++ = 11; 现在e是11, ++e = 11+1 = 12 f = 10 + 12
console.log(f);		// f = 22

比较运算符

比较运算符(关系运算符)是两个数据进行比较时所用的运算符,比较运算后,会返回一个布尔值 (true / false) 作为比较运算的结果

运算符名称说明案例结果
<大于号1 < 2true
>小于号1 > 2false
>=大于等于号(大于或者等于)2 >= 2true
<=小于等于号(小于或者等于)3 <= 2false
==判等号(会转型)37 == ‘37’true
!=不等号37 != 37false
=== !==全等 要求值和 数据类型都一直 !== 不全等37 === ‘37’false
var num1 = 10;
var num2 = 100;
var res1 = num1 > num2;		//false
var res2 = num1 == num2;	//false
var res3 = num1 != num2;	//true

逻辑运算符

逻辑运算符是用来进行布尔值运算的运算符,其返回值也是布尔值

逻辑运算符说明案例
&&“逻辑与” 简称 “与” andtrue && false
||“逻辑或” 简称 “或” oretrue || false
“逻辑非” 简称 “非” not!true

在这里插入图片描述

var num = 7;
var str = '我爱你~中国~';
console.log(num > 5 && str.length >= num);  	//true
console.log(num < 5 && str.length >= num);		//false
console.log(!(num < 10));						//false
comsole.log(!(num < 10 ||str.length == num));	//false

短路运算(逻辑中断)

短路运算的原理: 当有多个表达式(值)的时候,这边的表达式(值)可以确定结果时,就不再继续运算右边表达式的值;

  1. 逻辑与

    • 语法:表达式1 && 表达式2
    • 如果第一个表达式的值为真,则返回表达式2
    • 如果第一个表达式的值为假,即返回表达式1
    • 找假,找不到就返回最后一个
  2. 逻辑或

    • 语法:表达式1 && 表达式2

    • 如果第一个表达式的值为真,则返回表达式1

    • 如果第一个表达式的值为假,即返回表达式2

    • 找真,找不到就返回最后一个

赋值运算符

用来把数据赋值给变量的运算符

赋值运算符说明案例
=直接赋值var userName = ‘你是谁’;
+=、-=加、减一个数 后在赋值var age = 10; age += 5; //15
*=、/=、%=乘、除、取余 会在赋值var age = 2;age *= 5; //10

运算符优先级

优先级运算符顺序
1小括号()
2一元运算符++ – ! (++a , a++)
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

练习一

var a = 3 > 5 && 2 < 7 && 3 == 4; 
console.log(a);		//false

var b = 3 <= 4 || 3 > 1 ||3 != 2;
console.log(b);		//true

var c = 2 ==="2";
console.log(c);		//false;

var d = !c || b && a;
console.log(d);		//true

JS 流程控制

简介

在一个程序执行的过程中,各条代码的执行顺序对程序的结果是有直接影响的。很多时候我们腰通过控制代码
的执行顺序来实现我们腰完成的功能

简单理解:流程控制就是来控制我们的代码按什么结构顺序来执行

流程控制主要有三种结构,分别是 顺序结构、分支结构 和 循环结构,这三种结构代表三种代行的顺序

在这里插入图片描述

分支结构

if 语句流程控制

if 的语法结构

if(条件表达式){
	//执行语句
}

执行思路 如果 if 里面的 条件表达式结果为真 true 则执行大括号里面的执行语句

如果 if 条件表达式为假 则不执行大括号里面的语句 则执行 if 语句后面的代码

if else 语法结构

if(条件表达式){
	//执行语句1
}else {
	//执行语句2
}

if else if else 多分支语句

多分支语句,就是利用多个条件来选择不同的语句执行 多选 1 的过程

if (条件表达式1){
	//执行语句1
} else if (条件表达式2){
	//执行语句2
} else if (条件表达式3){
	//执行语句3
    ...
} else {
	//上述条件都不成立执行的代码
}

小小案例1(if)

弹出一个输入框,要求用户输入年龄,如果年龄大于等于 18 ,允许进网吧

var age = +prompt('请输入您的年龄');
if(age >= 18){
	alert('年龄符合,可以进入网吧!!');
}else{
	alert('您的年龄不符合!')
}

小小案例2 (if else)

接收用户输入的年份 如果是闰年就弹出闰年,否则弹出平年

var year = +prompt('请输入一个年份');
if( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
    alert(year + '是闰年');
} else {
    alert(year + '是平年');
}

小小案例3 (if else)

接收用户输入的姓名,来判断是否中将,如果输入的是刘德华,则提示中了5块钱,否则提示没中奖

var yName = prompt('请输入您的年龄');
if( yName == '刘德华'){
    alert('恭喜您中奖5元!!');
} else {
    alert('好遗憾,您没有中奖');
}

小小案例4 (if else if else)

接收用户输入的分数,根据分数输出对应的等级字母 A、B、C、D、E、其中:90分(含)以上,输出 A,80分(含)—90分(不含),输出B 70分(含)—80(不含)输出C,60分(含)—70(不含),输出D,60分(不含)一下,输出E.

var score = +prompt('请输入分数');
if (score >= 90){
    alert('A');
} else if (score >= 80){
    alert('B');
} else if (score >= 70){
    alert('C');
} else if(score >= 60){
    alert('D');
} else {
    alert('E');
}

三元表达式

由三元运算符组成的式子我们称为三元表达式 ? :

语法结构: 条件表达式 ? 表达式1 : 表达式2

var num = 10;
num > 5 ?'是的' : '不是的';

小小案例5 ( ? : )

用户输入数字,如果数字小于10,则在前面补0,如01,09,如果数字大于10,就输出数字本身

var num = +prompt('请输入0-59之间的数字');
var result = num < 10 ? '0' + num :num;
alert(result);

switch

switch 也是多分支语句,也可以实现多选 1。当腰争对变量设定一系列特定值的选项时,就可以使用stitch

switch(表达式){
	case value1:
		执行语句1;
		break;
	case value2:
		执行语句3;
		break;
	case value3:
		执行语句3;
		break;
	case value4:
		执行语句4;
		break;
	...
	default:
		最后执行的语句;
}

小小案例 ( 查询水果)

用户在输入框输入一个水果,如果有就弹出该水果的价格,如果没有该水果就输出“没有此水果”。

var fruit = prompt('请输入您需要的水果');
switch(fruit){
    case '苹果':
        alert('3.5元一斤');
        break;
    case '榴莲':
        alert('35元一斤');
        break;
    case '香蕉':
        alert('4元/斤');
        break;
    default:
        alert('没有该水果');
        break;
}

switch语句和 if else if语句的区别

  • 一般情况下,它们两个语句可以相互替换

  • switch.case语句通常处理case为比较确定值的情况,而i.else…语句更加灵活,常用于范围判断(大于、
    等于某个范围

  • switch语句进行条件判断后直接执行到程序的条件语句,效率更高。而f.else语句有几种条件,就得判断多
    少次

  • 当分支比较少时,f.else语句的执行效率比 switch语句高。

  • 当分支比较多时, switch语句的执行效率比较高,而且结构更清晰。

分支结构练习:

  1. 判断时间阶段。比如用户输入12点弹出中午好用户输入18点弹出傍晩好用户输入23点弹出深夜好
//有bug 如果数字数字大于23还是深夜好,而深夜是23~3
var time = +prompt('请输入时间');
if(time >= 23){ 
    alert('深夜好');
} else if(time >= 19){  
    alert('晚上好');
} else if(time >= 17){  
    alert('傍晚好');
} else if(time >= 13){ 
    alert('下午好');
} else if(time >= 11){  
    alert('中午好');
} else if(time >= 8){  
    alert('上午好');

} else if(time >= 6){  
    alert('早晨好');

}else if(time >= 3){   
    alert('凌晨好');
}
  1. 比较两个数的最大值(用户依次输入2个值,最后弹单出最大的那个值)
var num1 = +prompt('请输入数字1');
var num2 = +prompt('请输入数字2');
var max = num1 > num2 ? num1 : num2;
alert('最大数:'+max);
  1. 用户输入一个数,来判断是奇数还是偶数
var num = +prompt('请输入数字为您判断奇偶数');
num % 2 == 0 ? alert('您输入的数是偶数') : alert('您输入的数字是奇数'); 
  1. 根据用户输入的数值(数字1到数字7),返回星期几
var num = +prompt('请输入数字1~7');
switch(num){
    case 1:
        alert('星期一');
        break;
    case 2:
        alert('星期二');
        break; 
    case 3:
        alert('星期三');
        break;
    case 4:
        alert('星期四');
        break;
    case 5:
        alert('星期五');
        break;
    case 6:
        alert('星期六');
        break;
    case 7:
        alert('星期日');
        break;
    default:
        alert('输入有误!');
        break;
}
  1. 接收班长口袋里的钱数?若大于等于2000,请大家吃西餐。若小于2000,大于等于1500,请大家吃快餐。
    若小于1500,大于等于1000,请大家喝饮料。若小于1000,大于等于500,请大家吃棒棒糖。否则提醒班长
    下次把钱带够
var money = +prompt('请输入班长的钱数');
if(money >= 2000){
    alert('班上请大家吃西餐');
} else if(money >= 1500){
    alert('班上请大家吃快餐');
} else if(money >= 1000){
    alert('班长请大家喝饮料');
} else if(money >= 500){
    alert('班长请大家吃棒棒糖');
} else {
    alert('班长,大家提醒你下次把钱带够!');
}
  1. 分数转换,给个分数,判定等级。大于等于90A,大于等于80小于90B,大于等于70小于80C,大于
    等于60小于70D,小于60E
//有bug 只要数字大于90,就是A。那200呢?1000呢?不符合常理
var score = +prompt('请输入分数');
if (score >= 90){
    alert('A');
} else if (score >= 80){
    alert('B');
} else if (score >= 70){
    alert('C');
} else if(score >= 60){
    alert('D');
} else {
    alert('E');
}

循环结构

循环目的

在实际问题中,有许多 具有规律性的重复操作 ,因此在程序中需要完成这类操作就需要 : 重复执行某些语句

for循环

语法结构

for(初始化变量;条件表达式;操作表达式){
	//循环体
}
  • 初始化变量:就是用var 声明的一个普通变量 ,通常用于作为计数器使用
  • 条件表达式:就是用来决定每一次循环是否继续执行,就是终止的条件
  • 操作表达式:是每次循环最后执行的代码,经常用于我们计数器变量进行更新(递增 或 递减)

执行过程

for(var i = 1;i <= 100;i++){
	console,log('HOW are you?');
}

循环嵌套

循环嵌套 是值在一个循环语句中在定义一个循环语句的语法结构,例如for循环语句中,可以在嵌套一个for循环,这样的 for 循环语句我们称之为 双重for循环

语法结构:

for(外层的初始化变量;外层的条件表达式;外层的操作表达式){
	for(内层的初始化变量;内层的条件表达式;内层的操作表达式){
	
}

for循环练习

  1. 求 1~ 100之间所有数的平均值

    var sum = 0;
    for(var i = 1;i <= 100;i++){
        sum += i;
    }
    var mean = sum / 100;
    console.log('1~100之间的平均数是:'+mean);
    
  2. 求 1~100之间所有偶数和奇数的和

    var even = 0;
    var odd = 0;
    for(var i = 1;i <= 100;i++){
        if(i % 2 == 0){
            even += i
        }else {
            odd += i;
        }
    }
    console.log('1~100之间的偶数是:'+even);
    console.log('1~100之间的奇数是:'+odd);
    
  3. 求 1~100 之间所有能被3整除的数字和

    var sum = 0;
    for(var i = 1;i <= 100;i++){
        if(i % 3 == 0){
            sum += i;
        }
    }
    console.log('1~100之间能被3整除的数字和是:'+sum);
    
  4. 用户输入班级人数,之后依次输入每个学生的成绩,最后打印出班级总人数以及平均成绩

    var people = +prompt('请输入学生人数:');  //people 总的学生人数
    var score = 0;      //分数求和变量
    var average = 0;    //求平均值的变量
    for(var i = 1;i <= people;i++){
        score +=  +prompt('请输入第'+i+'个学生的分数');
    }
    average = score / people;
    console.log('这个班总人数是:'+people);
    console.log('平均成绩是:'+average);
    
    
  5. 一行打印五个星星在这里插入图片描述

    var str = '';
    for (var i = 1; i <= 5; i++) {
        str += '⭐'
    }
    console.log(str);
    
  6. 打印方形

    var rows = prompt('请输入要打印星星的行数:');
    var cols = prompt('请输入要打印星星的列数:');
    var str = '';
    for (var i = 1; i <= rows; i++) {
        for(var j = 1;j <= cols;j++){
            str +='⭐';
        }
        str += '\n';
    }
    console.log(str);
    
  7. 打印倒三角形

    var rows = prompt('请输入要打印星星的行数:');
    var cols = prompt('请输入要打印星星的列数:');
    var str = '';
    for (var i = 1; i <= rows; i++) {
        for(var j = i;j <= cols;j++){
            str +='⭐';
        }
        str += '\n';
    }
    console.log(str);
    
  8. 打印九九乘法表

    var str = '';
    for (var i = 1; i <= 9; i++) {
        for(var j = 1;j <= i;j++){
            // str += j + i = ;
            str += (j +'×'+ i +'='+ j * i)+'\t';
        }
        str += '\n';
    }
    console.log(str);
    
  9. 简易ATM

    • 里面现存有100元
    • 如果存钱,就用输入钱数加上之前的余额,之后显示弹出余额提示框
    • 如果取钱,就减去取出的钱数,之后弹出显示余额提示框
    • 点击显示余额,就显示当前余额
    • 如果退出,显示退出信息提示框
    var price = 100;
    while (true) {
        var str = +prompt('请输入您要的操作:\n 1.存钱 \n 2. 取钱 \n 3. 显示余额 \n 4. 退出');
        if (str == 1) {
            price += +prompt('请输入存入金额:');
            alert('余额是:' + price);
        } else if (str == 2) {
            var money = +prompt('请输入取出额度:');
            if (money <= price) {
                price -= money;
                alert('余额是:' + price);
            } else {
                alert('输入有误,余额不足!!');
            }
        } else if (str == 3) {
            alert('余额是:' + price);
        } else if (str == 4) {
            alert('退出中...');
            break;
        } else {
            alert('输入有误!!');
        }
        console.log(price);
    }
    

while 循环

whie语句可以在条件表达式为真的的提下,循环执行指定的段代码,直到达式不为真时结束循环
whie语句的语法结构如下

while(条件表达式){
	//循环体代码
}

执行思路:
① 先执行条件表达式,如果结果为true,则执行循环体代码;如果为 false,则退出循环,执行后面代码
② 执行循环体代码
③ 循环体代码执行完毕后,程序会继判断执行条件表达式,如条件仍为te,则会继续行循环体,直劉循
环条件为 false时,整个循环过程才会结束

while循环案例:

  1. 打印人的一生,从1岁到100岁

    var people = 1;
    while(people <= 100){
        console.log(people);
        people++;   
    }
    
  2. 计算 1~100之间所有整数的和

    var num = 1;
    var sum = 0;
    while(num <= 100){
        sum += num;
        num++;   
    }
    console.log(sum);
    
  3. 弹出一个提示框,你爱我吗?如果输入我爱你,就提示结束,否则就一直循环

    var message = prompt('你爱我吗?');
    while(message != '我爱你'){
        message = prompt('你爱我吗?');
    }
    alert('嘻嘻,我不爱你!!!');
    

do while 循环

do. while语句其实是whle语句的一个变体。该循环会执行次代码块,然后对条件表达式进行判断,如
果条件为真,就会重复执行循环体,否则退出循环。

do-whle语句的语法结构如下:

do {
	//循环体代码-条件表达式为true时重复执行循环体代码
}while(条件表达式);

执行思路:
①先执行次循环体代码
②再执行条件表达式,如果结果为true,则继续执行循环体代码,如果为 false,则退出循环,继续执行后面

注意:先再执行循环体,再判断,我们会发现do…while循环语句至少会执行一次循环体代码

  1. 1~100之间的和

    var j = 1;
    var sum = 0;
    do {
        sum += j;
        j++;
    }while(j <= 100);
    console.log(sum);
    
  2. 弹出一个提示框,你爱我吗?如果输入我爱你,就提示结束,否则就一直循环

    do {
        var message = prompt('你爱我吗?');
    }while(message != '我爱你');
    alert('我也爱你...');
    
  3. 判断用户名密码,若用户名为 “admin”,密码为 123456,则提示用户登录成功,否则一直让用户输入用户名和密码

    do {
        var user = prompt('请输入用户名');
        var pwd = +prompt('请输入密码');
    } while(user !='admin' && pwd != '123456');
    alert('输入正确,进入系统中...');
    

continue&bleak

continue关键字: 用于立即 跳出本次循环,继续下一次循环(本次循环体中continue之后的代码就会少执行一次)。

  1. 求 1~100 之间,除了能被7整除之外的整数和

    var sum = 0;
    for(var i = 1;i <= 100;i++){
        if(i %7 === 0){
            continue;
        }
        sum += i;
    }
    console.log(sum);
    

break关键字: 用于立即 跳出整个循环(循环结束)。

// break 推出整个循环
for(var i = 1;i <= 100;i++){
    if(i === 3){
        break;
    }
    console.log('我正在吃第'+i+'个包子');
}

JS命名规范

标识符命名规范

  • 变量、函数的命名必须要有意义
  • 变量 的名称一般用 名词
  • 函数 的名称一般用 动词

操作符命名规范

  • 操作符左右两侧各保留一个空格
  • // 单行注释前面注意有个空格

其它规范

在这里插入图片描述

JS数组

简介

数组是指 一组数据的集合 其中每个数据被称作 元素 ,在数组中可以存放 任意类型的元素。 数组是一种将 一组数据存储在单个变量名下的 优雅方式。

//普通变量一次只能存储一个值
var num = 10;

//数组一次可以存储多个值
var arr = [1,2,3,4];

数组创建方式

js创建数组有两种方式:

  • 利用 new 关键字创建数组
var 数组名 = new Array();
var arr = new Array(); //创建一个新的空数组
  • 利用数组字面量创建数组
// 1.使用数组字面量方式创建数组
var 数组名 = [];
// 2. 使用数组字面量方式创建带初始值的数组
var 数组名 = ['小白','小黑','大黄','瑞奇'];

获取数组元素

索引(下标): 用来访问数组元素的序号(数组下标从 0 开始)

数组可以通过 索引: 来访问,设置,修改对应的数组元素,可以通过 数组名[索引] 的形式来获取数组中的元素。

数组遍历

遍历: 就是把数组中的每个元素从头到尾都访问一次

var arr = ['关羽','张飞','马超','赵云','黄忠','刘备','姜维'];
for(var i = 0;i < arr.length;i++){
    console.log(arr[i]);
}

伪数组

  • 具有 length属性

  • ·按索引方式储存数据

  • 不具有数组的push,pop等方法

数组小练习

  1. 数组求和及平均值 ➡️:​ 求数组[2,6,1,7,4] 里面所有元素的和 以及平均值
var arr = [2,6,1,7,4];
var sum = 0;
var average = 0;
for(var i = 0;i < arr.length;i++){
    sum += arr[i];
}
average = sum / arr.length;
console.log('数组所有元素和是:'+ sum);
console.log('数组所有元素平均值是:'+ average);
  1. 求数组中的最大值 ➡️ : 其数值[2,6,1,77,52,25,7];
var arr = [2,6,1,77,52,25,7];
var maxValue = -Infinity;
var minValue = Infinity;
for(var i = 0;i < arr.length;i++){
    if(arr[i] > maxValue) {
        maxValue = arr[i];
    }
    if(arr[i] < minValue) {
        minValue = arr[i];
    }
}
console.log(arr);
console.log('arr数组最大值是:'+maxValue);
console.log('arr数组最小值是:'+minValue);
  1. 数组转化为分割字符串:

将数组[‘red’, ‘green’, ‘blue’, ‘pink’] 转换为字符串,并且用 | 或其它符号分割!

输出:’red | green | blue | pink|‘;

var arr = ['red','green','blue','pink'];
var str = '';
for(var i = 0;i < arr.length;i++){
    str += arr[i] + '|';
}
console.log(str);
  1. 新建一个数组,里面存放10个整数(1~10)
var arr = [];
for(var i = 0;i < 10;i++){
    arr[i] = i + 1;
}
console.log(arr);
  1. 筛选数组: → 将数组[2,0,3,1,77,52,0,25,7]中大于大于 10的元素选出来。存入新数组
var arr = [2,0,3,1,77,52,0,25,7];
var newArr = [];
var j = 0;
for(var i = 0;i < arr.length;i++){
    if(arr[i] >= 10){
        newArr[j] = arr[i];
        j++;
    }
}
console.log(newArr);
  1. [‘red’, ‘blue’, ‘red’, ‘green’, ‘pink’, ‘red’] 求red 出现的次数及位置
// 核心算法:先查找第一个。出现的位置
// 然后只要 indexof返回的结果不是-1就继续往后查找
// 因为 indexof只能查找到第一个,所以后面的查找,一定是当前索引加1,从而继续查找

var arr = ['red', 'blue', 'red', 'green', 'pink', 'red'];
var index = arr.indexOf('red');
var count = 0;  // 统计次数
while(index !== -1){
    console.log(index);
    count++;
    index = arr.indexOf('red',index + 1);
}
console.log('red一共出现了:'+ count + '次');
  1. 数组去重
//indexOf() → 1
var arr = [20,10,20,10,30,50];
var newArr = [];
for(var i = 0;i < arr.length;i++){
    if(arr.indexOf(arr[i]) == i){
        newArr[newArr.length] = arr[i];
    }
}
console.log(newArr);

//indexOf() → 2
var arr = [20,10,20,10,30,50];
var newArr = [];
for(var i = 0;i < arr.length;i++){
    if(newArr.indexOf(arr[i]) == -1){
        newArr[newArr.length] = arr[i];
    }
}
console.log(newArr);

//开关思想
var arr = [20,10,20,10,30,50];
var newArr = [];
for(var i = 0;i < arr.length;i++){
    var flag = true;
    for(var j = 0;j < newArr.length;j++){
        if(arr[i] == arr[j]) {
            flag = false;
            break;
        }
    }
    if(flag) {
        newArr[newArr.length] = arr[i];
    }
}
console.log(newArr);
  1. 翻转数组

    将数组[‘red’, ‘green’ , ‘blue’, ‘pink’ ,‘purole’] 的内容反过来存放

    输入:[‘purple’, ‘pink’, ‘blue’, ‘green’, ‘red’]

var arr = ['red', 'green' , 'blue', 'pink' ,'purole'];
for(var i = 0;i < arr.length / 2;i++) {
    var temp = arr[i];
    arr[i] = arr[arr.length -1 -i];
    arr[arr.length -1 -i] = temp;
}
console.log(arr);
  1. 冒泡排序
var arr = [20,2,10,5,30,10,80];
for(var i = 0;i <arr.length-1;i++){
    for(var j = 0;j < arr.length-1-i;j++){
        if(arr[j] > arr[j + 1]){
            var temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
console.log(arr);
  1. 选择排序
var arr = [20,2,10,5,30,10,80];
for(var i = 0;i <arr.length-1;i++){
    for(var j = i +1;j < arr.length;j++){
        if(arr[i] > arr[j]){
            var temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
}
console.log(arr);

JS 函数

简介

  • 在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量复使用
  • 虽然for循环语句也能实现些简单的重复操作,但是比较具有局限性,此时我们就可以使用 JS中的函数
  • 函数 :就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。

函数使用

声明函数

//声明函数
function 函数名(){
	//函数体代码
}
  • function 是声明函数的关键字,必须小写
  • 由于函数一般是为了实现某个功能才定义的,通常我们将 函数名 命名为 动词 ,比如getSum

调用函数

//调用函数
函数名();  //通过调用函数名来执行函数体代码
  • 调用的时候**不能忘记加小括号**
  • 函数不调用,自己不执行

【注】:声明函数本身并不会执行代码,只有调用函数是才会执行函数体代码

function getSum(num1,num2){
        var sum = 0;
        for(var i = num1;i <= num2;i++){
            sum += i;
        }
        console.log(sum);
        
     }
     getSum(1,100);

函数的参数

声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为 形参,

而在调用该函数时,同样也需要传递相应的参数,这些参数被称为 实参

参数说明
形参形式上的参数函数定义的时候传递的参数当前并不知道是什么
实参实际上的参数函数调用的时候传递的参数实参是传递给形参的

参数的作用:在函数内部某些值不能固定,我们可以通过参数在函数调用时传递不同的值进去!

函数的返回值

  • return 语句之后的代码不会被执行
  • return 只能返回一个值, 如果以逗号隔开,以最后一个值为准
  • 函数都是有返回值的,如果有 return 则返回return 后面的值
  • 如果没有return ,则返回 undefined
function add(num1,num2){
	//函数体
	return num1 + num2;   //return后面的语句不会被执行
	alert('我不会执行,因为前面又return')
}
var resNum = add(20,6);
alert(resNum);  //26

arguments的使用

当我们不确定有多少个参数传递的时候,可以用 arguments 来获取。在 JavaScript中, arguments实际上

它是当前函数的一个 内置对象 。所有函数都内置了一个 arguments对象,

arguments对象中存储了传递的
所有实参

arguments展示形式是一个伪数组, 因此可以进行遍历。伪数组具有以下特点

  • 具有 length属性
  • ·按索引方式储存数据
  • 不具有数组的push,pop等方法

立即执行函数

立即执行函数:不需要调用,立马就能执行的函数

(function(){}()); //1. W3C建议这种<
(function(){})(); //2
+function(){}() ; //3
-function(){}() //4
!function(){}() //5.
var demo = function(){}() //6
+function test(){ console.log(‘a’); }()

立即执行函数最大的作用就是 独立创立了一个作用域。里面所有的变量都是局部变量,不会有命名冲突的情况

函数练习

1. 任意两个数的和

function getResult(num1,num2){
	return[num1 + num2,num1 - num2,num1 * num2,num1 / num2];
}

2… 利用函数求任意数的最大值

function getMax() {
    var maxValue = -Infinity;
    for(var i = 0;i < arguments.length;i++) {
        if(arguments[i] > maxValue) {
            maxValue = arguments[i];
        }
    }
    return maxValue;
}
var res = getMax(10,100,820,50);
console.log(res);

3… 输入—个数判断是否是素数,并弹出返回值(又叫质数,只能被1和自身整数的数)

function getFlag(num) {
    for(var i = 2;i < num;i++) {
        if(num % i == 0) {
            return false;
        }
    }
    if(num >= 2) {
        return true;
    } else {
        alert('输入错误');
    }
}
console.log( getFlag(9));

4. 翻转任意数组

function reverse(arr){
    for(var i = 0;i < arr.length / 2;i++){
        var temp = arr[i];
        arr[i] =  arr[arr.length-1-i];
        arr[arr.length-1-i] = temp;
    }
    return arr;
}
console.log(reverse([10,20,30,40]));
console.log(reverse([50,10,20,10,30,40]));

5. 输入年份,判断是否是闰年

function isRunYear(year) {
    var flag = false;
    if( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
        flag =  true;
    } else {
        flag =  false;
    }
    return flag;
}
console.log(isRunYear(2000));

5. 输入年份,判断当前2月份的天数(函数调用函数)

function isRunYear(year) {
    var flag = false;
    if( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
        flag =  true;
    } else {
        flag =  false;
    }
    return flag;
}
console.log(isRunYear(1995));

function backDay() {
    var year = prompt('请输入年份');
    if(isRunYear(year)) {
        console.log('当前是闰年二月份有29天');     
    } else {
        console.log('当前是平年二月份有28天');     

    }
}
backDay();

6.输入某年某月某日,判断这一天是这一年的第几天?

function tesTing(year,month,day){
    var count = 0;
    switch(month){
        case 12:
            count += 30;
        case 11:
            count += 31;
        case 10:
            count += 30;
        case 9:
            count += 31;
        case 8:
            count += 31;
        case 7:
            count += 30;
        case 6:
            count += 31;
        case 5:
            count += 30;
        case 4:
            count += 31;
        case 3:
            count += 28;
        case 2:
            count += 31;
        default:
            break;
    }
    count +=day;
    if((year % 4 == 0 && year %100 != 0) || year % 400 == 0 && month > 2){
        count++;
    }  
    return count;
}

var year = +window.prompt('请输入年份');
var month = +window.prompt('请输入月份');
var day = +window.prompt('请输入天数');

var days = tesTing(year,month,day);
console.log('这一天是'+year+'年的第'+days+'天');

在这里插入图片描述

======================//简易计算器===================================
//计算器函数
function getArithmetic(parameter) {
    var num1 = +prompt('请输入第一个数:');
    var num2 = +prompt('请输入第二个数:');
    var str = '';
    switch(parameter){
        case 'add':
            str = num1 +'+'+ num2 + '='+(num1 + num2);
            alert(str);
            break;
        case 'subtract':
            str = num1 +'-'+ num2 + '='+(num1 - num2);
            alert(str);
            break;
        case 'multiply':
            str = num1 +'*'+ num2 + '='+(num1 * num2);
            alert(str);
            break;
        case 'divide':
            str = num1 +'/'+ num2 + '='+(num1 / num2);
            alert(str);
            break;
        default :
            alert('请输入正确的选项:');
            // break;
    }
}

//循环
while(true) {
    var str = prompt('欢迎使用简易计算器:\n 1.加法运算: \n 2.减法运算:\n 3.乘法运算:\n 4.除法运算:\n 5.退出:\n 请输入您的选项:');

    if(str == 1) {
        getArithmetic('add');
    } else if(str == 2) {
        getArithmetic('subtract');
    } else if(str == 3) {
        getArithmetic('multiply');
    } else if(str == 4) {
        getArithmetic('divide');
    } else if(str == 5){
        var res = confirm('确认退出吗?');
        if(res == true) {
            break;
        }
    } else {
        getArithmetic('false');
    }
}

JS作用域

简介

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的 可用性的代码范围 就是这

个名字的 作用域。 作用域的使用提高了程序逻辑的同部性,增强了程序的可靠性,减少了名字冲突

  • js的作用域(es6)之前:分为全局作用域和局部作用域
  • 全局作用域: 整个javascript标签,或者是一个单独的js文件
  • 局部作用域(函数作用域): 在函数内部就是局部作用域,这个代码的名字只在函数内部起效果。

变量的作用域

js 运行三部曲 1 语法分析 → 2 预编译 → 3解释执行

在JavaScript中,根据作用域的不同,变量 分为:

  • 全局变量

  • 在全局作用域下声明的变量叫做全局变量,(在函数外部定义的变量)

    • 全局作用域的代码在任何位置都可以使用==(只有在浏览器关闭时才会销毁,比较占用内存)==
  • 特殊情况下,在函数内不使用 var 声明也是全局变量==(不建议使用)==

    • 一切声明的全局变量,全是window 的属性。 window 就是全局的域
  • 局部变量

    • 在局部作用域下声明的变量叫做局部变量,(在函数内部定义的变量)
    • 函数变量只能在函数内部使用
    • 函数的 形参 实际上就是局部变量
    • 当代码块运行结束后,就会被销毁,因此 更节省内存空间

作用域链

  • 只要是代码,就至少有一个作用域
  • 写在函数内部的局部作用域
  • 如果函数中还有函数, 那么在这个作用域中就又可以诞生一个作用域
  • 据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部数访问,就称
    作用域链。访问规则:就近原则

JS预解析

简介

JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的,JavaScript 解析器在运行 JavaScript 的代码的时候分为两步:预解析 和 代码执行

  • 预解析 js引擎会把 js 里面所有的 var 还有 function 提升到当前作用域的最前面
  • 预解析分为变量预解析(变量提升)和函数预解析(函数提升)
  • 变量提升就是把所有的变量声明提升到当前的作用域最前面不提升赋值操作
  • 函数提升就是把所有的函数声明提升到当前作用域的最前面不调用函教
  • 函数表达式只会提升 变量名,所以函数表达式调用必须写在函数下面
//例一
console.log(num);   //undefined
var num = 10;

//  解析器 会执行以下操作
var num;
console.log(num);
num = 10;

//例二
fn();    //控制台输出111
function fn(){
    console.log('111');
}

//  解析器 会执行以下操作
function fn(){
    console.log('111');
}
fn(); 

//例三
fun();   //报错     输出fun不会报错而是undefined,因为var fun 提升了 后面函数是赋值
var fun = function(){
    console.log('111');
}
//  解析器 会执行以下操作
var fun;
fun = function(){
    console.log('111');
}

案例

fn1();
console.log(c);
console.log(b);
console.log(a);
function fn1(){
    var a = b = c = 9;  //相对于 var a = 9 b = 9 c = 9;b c 在没有var 属于全局作用域
    console.log(a);
    console.log(b);
    console.log(c);
}
//执行步骤
function fn1(){
    var a;
    a = b = c = 9;
    console.log(a); 	//9
    console.log(b);		//9
    console.log(c);		//9
}
fn1();
console.log(c);			//9
console.log(b);			//9
console.log(a);			//报错。。未定义,未赋值就使用

JS对象

什么是对象?

现实生活中: 万物皆对象, 对象是 一个具体的事物, 看得见摸得着的实物。例如,一本书一辆汽车、一个人

可以是“对象”,个数据库一张网页、一个与远程服务器的连接也可以是“对象”。

在 JavaScript 中,对象是一组无序的相关 属性 和 方法 的集合,所有的事物都是对象,例如字符串,数值,数组,函数等。

  • 属性:事物的 特征, 在对象中用 属性来表示 (常用名词)
  • 方法:事物的 行为, 在对象中用 方法来表示 (常用动词)

对象是 一个具体的事物

明星			周星驰(星爷)		女朋友			迪丽热巴		班主任			咱们班班主任
×(不是对象)		√(是对象)		×(不是对象)		√(是对象)		×(不是对象)		√(是对象)

苹果			这个苹果			手机	    pink老师的小米手机 	游戏			刺激战场
×(不是对象)		√(是对象)		×(不是对象)		√(是对象)			×(不是对象)		√(是对象)

为什么需要对象

保存一个值时,可以使用 变量, 保存多个值(一组值)时, 可以使用 数组。 如果要保存一个人的完整信息呢?

例如,将“张三疯”的个人的信息保存在数组中的方式为:

var arr=['张三疯','男',128,154];   
//前两个值还能看出来指的是什么,可是128,154能看出来指的什么吗
普通表示对象代码表示
张三疯.姓名= ‘张三疯’;person.name = ’张三疯’
张三疯.性别 = ‘男’;person.sex = ‘男’
张三疯.年龄 = 128person age= 128
张三疯.身高 = 154person. height= 154

创建对象方式

在JavaScript 中,现阶段 (es5) 我们可以采用三种方式创建对象

1. 利用 字面量 创建对象

var obj = {
	uname:'张三疯',
	age:18,
	sex:'男',
	sayHi:function(){
		console.log('hi~');
	}
}
  • 属性方法采取键值对的方式: 键 属性名 :指 属性值
  • 方法冒号后面跟着 匿名函数
  • 调用对象的属性,采取 对象名.属性值
  • 另外一种对象调用方法, 对象名[‘属性名’]
  • 调用对象的函数,对象名.方法名 obj.sayHi();

2.利用 new Object 创建对象

var obj = new Object();  //创建了一个空对象
obj.uname:'张三疯';
obj.age:18;
obj.sex:'男';
obj.sayHi:function(){
		console.log('hi~');
}
  • 利用 等号 = 赋值 的方法 添加 对象的属性和方法
  • 每个属性和方法之间用 分号结束
var mr = new Object();
mr.name = '鸣人';
mr.sex = '男';
mr.age = 19;
mr.skill = function {
	console.log('技能:影分身术');
}

3.利用 构造函数 创建对象

构造函数: 是一种特殊的函数,主要用来初始化对象,就是为对象变量赋初始值,它总是与 new 运算符 一起使用,我们可以把对象中一些公有的属性和方法抽取出来 ,然后封装到这个函数里面。

  • 构造函数名字首字母要大写

  • 构造函数不需要 return,就可以返回结果

  • 调用构造函数,必须使用 new

  • 前面两种方式一次只能创建一个对象。

  • 可以利用函数的方法,重复这些相同的代码,我们就把这个函数称为 构造函数

  • 利用构造函数创建对象的过程 我们 称为对象的实例化

  • 构造函数抽取了对象的公共部分,封装到了函数里面,它泛指某一大类(class);

 function Star(uname,age,sex) {
 		this.uname = uname;
 		this.age = age;
 		this.sex = sex;
 }
 var ldh = new Star('张凯','16','男'); 
 console.log(ldh.uname);   // 调用对象属性  打印张凯

var zxy = new Star('张学友','18','男');
console.log(zxy['age'])		//另外一种调用方式 必须要有引号 打印 18

变量、属性、函数、方法总结

  • 变量:单独声明赋值,单独存在
  • 属性:对象里面的变量称为 属性 ,不需要声明,用来描述该对象的特诊
  • 函数:单独存在的,通过 “函数名()” 的方式就可以调用
  • 方法:对象里面的函数称为方法,方法不需要声明,使用 “对象.方法名()” 的方式可以调用,方法用来描述该对象的行为和功能

new 关键字

new 关键字执行过程

  1. new 构造函数在内存中创建了一个空对象

  2. this 指向刚才创建的空对象

  3. 执行函数里面的代码,给这个空对象添加属性和方法

  4. 返回这个对象

遍历对象属性

for…in语句用于对数组或者对象的属性进行循环操作

//语法
for(变量 in 对象){
	
}

for(var k in obj){
	console.log(k);     // k是变量,打印出来的是属性名
	console.log(obj[k]); //输出打印属性值
}

对象练习

1.//创建电脑对象,该对象有颜色、重量、品牌、型号、可以看电影、听音乐、打游戏和敲代码

var computer = {
    color:'silvery',
    weight:'2千克',
    brand:'Legion',
    look:function(){
        console.log('see a movie');
    },
    hear:function(){
        console.log('listen to music');
    },
    play:function(){
        console.log('Playing games');
    },
    knock:function(){
        console.log('Knock code');
    }
}

2.//创建一个按钮对象,该对象中需要包含宽、高、背景颜色和点击行为。

var button = new Object();
button.width = '100';
button.height = '100';
button.background = 'pink';
button.click = function(){
    console.log('点击你是pink');

}
console.log( button['height']);
button.click();

3./创建一个车的对象,该对象有重量、颜色、牌子,可以载人、拉货和耕田

function Car(weight,color,brand){
    this.weight = weight;
    this.color = color;
    this.brand = brand;
    this.action = function(manned){
        console.log(manned);
    }
}
var BWM = new Car(4000,'red','BWM','载人');
var tuo = new Car(5900,'block','拖拉机','拉货');
//遍历BWM
for(var k in BWM){
    console.log(k + ':'+BWM[k]);
}

JS内置对象

JavaScript 中的对象分为三种:**自定义对象、内置对象、**浏览器对象。 自定义对象和内置对象都属于ECMAScript

内置对象: 就是指 JS 语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本二必要的功能(属性 和 方法)。

JavaScript 提供了多个内置对象:Math、Date、Array、String等

查询文档: MDN

Math对象

Math 对象不是构造函数,它具有数学常数和函数的属性和方法,跟数学相关的运算(求最大值、取整、最大值等) 可以使用Math中的成员

Math 对象功能
Math.PI圆周率
Math.max()函数返回一组数中的最大值。
Math.floor()向下取整
Math.ceil()向上取整
Math.random()随机数
Math.round()四舍五入 就近取整 注意点:-3.5 结果是-3
Math.abs()绝对值
Math.max()
Math.min()
求最大值和最小值

利用对象封装自己的对象,里面有PI,最大值和最小值

var myMath  = {
    PI:3.141592653,
    Max:function(){
        var maxValue = -Infinity;
        for(var i = 0;i < arguments.length;i++){
            if(arguments[i] > maxValue){
                maxValue = arguments[i];
            }
        }
        return maxValue;
    },
    Min:function(){
        var minValue = Infinity;
        for(var i = 0;i < arguments.length;i++){
            if(arguments[i] < maxValue){
                minValue = arguments[i];
            }
        }
        return minValue;
    }
}
console.log(myMath.PI);
console.log(myMath.Max(10,2,50,80,10));

利用**Math.random() **内置随机函数猜数字

function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;  //生成两个数之间的随机数,包含最大数和最小数。
}
var ran = getRandom(1, 10);
var count = 0;
while (true) {
    var num = +prompt('请输入1~10之间的数字');
    if (num > ran) {
        alert('猜大了');
    } else if (num < ran) {
        alert('猜小了');
    } else if (num == ran) {
        alert('恭喜你,猜对了!!!');
        break;
    } else {
        alert('请输入正确的数字');
    }
    count++;
    if (count == 3) {
        alert('机会用尽');
        break;
    }

}

日期对象

  • Date 对象不一样,他是一个**构造函数,所以需要实例化才能使用**
  • Date 实例用来处理**日期 和 时间**

1. 获取当前时间

var now = new Date();
console.log(now);

2. Date()构造函数的参数

如果括号里面有时间,就返回参数里面的时间,例如日期格式字符串 ’2019-5-1‘,可以写成 new Date(‘2019-5-1’),或者new Date(‘2019/5/1’)

3. 日期格式化:

如果我们想要 2020-2-2 2:2:2 格式的日期可以下面的格式指定

方法说明代码
getFullYear();获取当前年dObj.getFullYear();
getMonth();获取当月(0-11)dObj.getMonth();
getDate();获取当天日期dObj.getDate();
getDay();获取星期几(周日 0 到周六 6)dObj.getDay();
getHours();获取当前小数dObj.getHours();
getMinutes();获取当前分钟dObj.getMinutes();
getSeconds();获取当前月dObj.getSeconds();
getTime()
valueOf()
获取从1970年1月1日到现在的时间过了多少毫秒dObj.getTime()
dObj.valueOf()
//格式化年、月、日 输出 2020年2月28日 星期五
var d = new Date();  //实例化日期对象
var year = d.getFullYear();
var month = d.getMonth() + 1;   //getMonth()返回的月份小一个月,因此需要+1;
var dates = d.getDate();
var hours = d.getHours();
var minutes = d.getMinutes();
var day = d.getDay();
var week = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
console.log('今天是:'+year + '年'+month+'月'+dates +'日 '+hours+'时'+minutes+'分 '+week[day]);
// 封装获取时分秒的方法
function getTimer() {
    var time = new Date();
    var h = time.getDate();
    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( getTimer());

获取1970年月1日到现在的毫秒数

var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
//简单的写法
var date1 = +new Date();

//H5新增的获取总的毫秒数
console.log(Date.now());      //也叫时间戳,过去了就没有了

小案例(倒计时):

① 核心算法:输入的时间减去现在的时间就是剩余的时间,即倒时,但是不能拿着时分秒 相减,

​ 比如05分减去25分,结果会是负数的。

② 用时间戳来做。用户输入时间总的亳秒数减去现在时间的总的亳秒数,得到的就是剩佘时间的亳秒数。

③ 把剩余时间总的毫秒数转换为天、时、分、秒(时间戳转换为时分秒)
转换公式如下

d= parseInt(总秒数/60/60/24); //计算天数
h= parseInt(总秒数/60/60%24) //计算小时

m= parcent(总秒数/60%60) //计算分数
s= parseInt(总秒数%60) //计算秒数

function countDodn(time){
    var now = +new Date();          //当前时间总的毫秒数
    var inputTimes = +new Date(time);   //输入总的毫秒数
    var times = (inputTimes - now) / 1000;   //剩余的总秒数
    var d = parseInt(times / 60 / 60 / 24);    //剩余天数;
    var h = parseInt(times / 60 / 60 % 24);    //剩余小时数
    var m = parseInt(times / 60 % 60);          //剩余分钟
    var s = parseInt(times % 60);               //剩余秒数

    //天、时、分、秒小于10前面补0
    d = d < 10 ? '0' + d :d;
    h = h < 10 ? '0' + h :h;
    m = m < 10 ? '0' + m :m;
    s = s < 10 ? '0' + s :s;
    return d + '天' + h +'小数' + m +'分钟' + s +'秒';
}
console.log(countDodn('2020-2-30 12:00:00'));
var date = new Date();
console.log(date);

数组对象

检测是否为数组

(1) instanceof 运算符 用来检测是否为数组

var arr = [];
console.log(arr instanceof Array);		//如果是数组返回 true,否则为false

(2) Array.isArray(参数) H5新增方法(存在兼容性问题) 如果值是 Array,则为true; 否则为false。

var arr = [];
var obj = {}
console.log(Array.isArray(arr));		//如果是数组返回 true,否则为false
console.log(Array.isArray(obj));		//false

数组API

方法名说明返回值
push(参数1….)末尾添加一个或多个元素,会修改原数组返回新的数组长度
pop()删除数组最后一个元素,把数组长度减 1 ,无参数,修改原数组返回删除的元素值
unshift(参数1….)向数组的开头添加一个或多个元素,会修改原数组返回新的数组长度
shift()删除数组第一个元素,数组长度减 1 ,无参数,修改原数组返回第一个元素的值

数组排序API

方法名说明是否修改原数组
reverse()颠倒数组中元素的顺序,无参数修改原数组,返回新数组
sort()对数组元素进行排序修改原数组,返回新数组

数组 索引 API

方法名说明返回值
indexOf()数组中查找给定元素的第一个索引如果存在返回索引号,不存在,则返回 -1
lastIndexOf()数组中查找给定元素最后一个索引如果存在返回索引号,不存在,则返回 -1

数组转换字符串 API

方法名说明返回值
toString()把数组转换成字符串,逗号分隔每一项返回一个字符串
join(‘分隔符’)方法用于把数组中的每个元素都转换成字符串返回一个字符串

数组其它 API

方法名说明返回值
concat()连接两个或多个数组,不影响原数组返回一个新数组
slice()数组截取slice(begin,end)返回截取项目的新数组
splice()数组删除splice(第几个开始,要删除的个数)返回被删除的新数组,改变原数组

从第 2 位开始删除 1 个元素,插入“trumpet”

var myFish = ['angel', 'clown', 'drum', 'sturgeon'];
var removed = myFish.splice(2, 1, "trumpet");

从第 3 位开始删除 1 个元素

var myFish = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
var removed = myFish.splice(3, 1);

从倒数第 2 位开始删除 1 个元素

var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(-2, 1);

字符串对象

为了方便操作基本数据类型, JavaScript 还提供了三个特殊的引用类型: String、 Number和 Boolean

基本包装类型 就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法

// 下面代码有什么问题? 
var str ='andy';
console. log(str length);

按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为js会把

基本数据类型包装为复杂数据类型,其执行过程如下:

// 1.生成临时变量,把简单类型包装为复杂数据类型
var temp= new string('andy)i
// 2.赋值给我们声明的字符变量
str temp;
// 3.销毁临时变量
temp = null;                    

字符串的不可变性

指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。

var str="abc’;
str ='hello'
// 当重新给str赋值的时候,常量"abc不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str = '';
for(vari = 0;i < 100000; i++) {
    str + i;
}
console.1og(str);	//这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间

根据字符返回位置API

方法名说明返回
indexOf(‘要查找的字符串’,[开始位置])返回指定内容在元字符串中的位置新的字符串
lastIndexOf()从后往前找,只找第一个匹配的新的字符串
var str = '改革春风吹满地,春天来了';
consoloe.log(str.indexOf('春'));	// 返回第一个春的索引 2
consoloe.log(str.indexOf('春'3));	// 从索引3的位置往后找 8

练习题(查找某一个字符串出现的次数及位置):

// 查找字符串" abcoefoxyozzopp'"中所有o出现的位置以及次数
// 核心算法:先查找第一个。出现的位置
// 然后只要 indexof返回的结果不是-1就继续往后查找
// 因为 indexof只能查找到第一个,所以后面的查找,一定是当前索引加1,从而继续查找
var str = 'abcoefoxyozzopp';
var index = str.indexOf('o');
var count = 0;  // 统计次数
while(index !== -1){
    console.log(index);
    count++;
    index = str.indexOf('o',index + 1);
}
console.log('o一共出现了:'+ count + '次');

根据位置返回字符API

方法名说明使用
charAt(index)返回指定位置的字符(index字符串的索引号)str.charAt(0)
charCodeAt(index)返回指定位置处字符的ASCII码(index的索引号)
作用:可以知道用户按下键盘哪个键
str.charCodeAt(0)
str[index]获取指定位置处字符H5新增,有兼容性问题str[0]

1.遍历字符

var str = '改革春风吹满地';
for(var i = 0;i < str.length;i++) {
    console.log(str.charAt(i));
}

2.统计 ‘abcoefoxyozzopp’ 这个字符串中出现最多的字符及次数

// 核心算法: 
// 1.利用 charAt() 遍历这个字符
// 2.把每个字符都存储给对象,如果对象没有该属性,就为1,有则 +1;
// 3.遍历对象,得到最大值和该字符
var str = 'abcoefoxyozzopp';
var obj = {}
for(var i = 0;i < str.length; i++) {
    var chars = str.charAt(i);
    if(obj[chars]){
        obj[chars]++;
    } else {
        obj[chars] = 1;
    }
}
var max = -Infinity; //存储出现最多的次数
var ch = '';         //存储出现最多次数的字符
// 遍历对象
for(var k in obj) {
    if(obj[k] > max) {
        max = obj[k];
        ch = k;
    }
}
console.log('出现次数最多的字符是:' + ch+',出现了:'+max+'次');

字符串操作方法

方法名说明
concatlstr1, str2, str3…)concat() 方法用于连接两个或多个字符串拼接字符串,等效于+, +更常用
substr(start, length)从 start位置开始(索引号), length取的个数,重点记住这个
slice(start, end)从star位置开始,截取到end位置,end取不到(他们俩都是索引号)
substring(start, end)从 start位置开始,截取到end位置,end取不到 基本和 slice相同 但是不接受负值
replace(‘被替换的字符’,‘替换为的字符’)替换字符(只能替换第一个字符)
split(‘分隔符’)字符串转换为数组
toUpperCase()转换大写
toLowerCase()转换小写

有一个字符串 ‘abcoefoxyozzopp’ 要求把所有的 ’o‘ 替换为 ’ * ‘

var str = 'abcoefoxyozzopp';
while( str .indexOf('o') !== -1) {
    str = str.replace('o','*');
}
console.log(str);

练习作业

//给定一个字符串,如:" abaasdffggghhijkkgfddsssss344343”,问题下
    //1、字符串的长度
    //2、取出指定位置的字符,如:035等
    //3、查找指定字符是否在以上字符串中存在,如:i,C,b等
    //4、替换指定的字符,如:9替换为22 s替换为b等操作方法
    //5、截取指定开始位置到结束位置的字符串,如:取得15的字符串
    //6、找出以上字符串中出现次数最多的字符和出现的次数 

var str = 'abaasdffggghhijkkgfddsssss344343';
console.log('原字符:'+str);
console.log('1.字符串的长度:'+ str.length);
console.log('2.取出指定位置的字符:'+ str.charAt(3));
console.log('3..查找指定字符是否存在:'+ str.indexOf('C'));

//4.替换字符串

while(str.indexOf('s') !== -1) {
    str = str.replace('s','b');
}
console.log('4.替换后现字符:'+ str);
console.log('5.截取指定开始到结束位置字符串:'+ str.substr(0,5));

// 6、找出以上字符串中出现次数最多的字符和出现的次数 
//(第一步取出每个字符,定义一个空对象放字符)
var obj = {}
for(var i = 0;i < str.length;i ++){
    var chars = str.charAt(i);
    //(第二步把取出的字符放入对象中并判断对象中是否有这个字符,有就+1)
    if( obj[chars] ) {
        obj[chars]++;
    } else {
        obj[chars] = 1;
    }
}
//(第三部 遍历对象,找出出现次数最多的值和次数)
var max = -Infinity;
var st = '';
for(var k in obj) {
    if(obj[k] > max) {
        max = obj[k];
        st = k;
    }
}
console.log('出现次数最多的字符是:' + st+',出现了:'+max+'次');

JS高级(ECMAScript)

构造函数和原型

概述

  • 在典型的OP的语言中(如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,
    JS中并没用引入类的概念
  • ES6,全称 ECMAScript6.0,2015.06发版。但是目前浏览器的 JavaScript是ES5版本,大多数高版本的浏
    览器也支持ES6,不过只实现了ES6的部分特性和功能。
  • 在ES6之前,对象不是基于类创建的,而是用一种称为 构建函数 的特殊函数来定义缘和它们的特征
  • 创建对象可以通过以下三种方式:
    • 1.对象字面量
    • 2.new Object()
    • 3.自定义构造函数

构造函数

构造函数 是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值。它总是与 new 一起使用,我们可以把对象中的一些公共的属性和方法抽取出来,然后封装到这个函数里面。

在 JS 中,使用构造函数是应该注意以下两点:

  • 1.构造函数用于创建某一大类,因为和js 函数没什么区别,为方便开发。我们一般把首字母大写,这也是开发规范
  • 2.构造函数要和 new 关键字一起使用才有意义

使用构造函数实例化对象 的 new 在执行时会做的四点事情

​ ① 在内存中创建一个新的空对象

​ ② 让this指向这个空对象

​ ③ 指向构造函数里面的代码,给这个新对象添加属性和方法

​ ④ 返回这个新对象(所以构造函数里面不需要return)

构造函数的静态成员和实例成员

JavaScript 的构造函数中可以添加一些成员,可以在构造函数本身添加,也可以在构造函数内部的 this 上添加,通过这两种方式添加的成员,就分别称为 静态成员 和 实例成员

  • 静态成员: 在构造函数上添加的成员称为 静态成员 只能由构造函数本身来访问!
  • 实例成员: 在构造函数内部创建的对象称为 实例成员 只能由实例化的对象来访问!

如下 静态 、实例成员代码演示

function Demo(uname,age) {
    this.uname = uname;
    this.age = age;
}
var demo = new Demo('张三',18);
console.log(Demo.uname);    // 构造函数访问实例成员值为undefined 
//静态成员,在构造函数本身上添加的成员 sex 就是静态成员
Demo.sex = '男';
console.log(Demo.sex);      //静态成员只能通过构造函数来访问
console.log(demo.sex);      //值为undefined 因为实例化对象不可以访问静态成员

构造函数的问题

构造函数方法很好用,但是 存在浪费内存的问题

如下:

function Star(uname,age) {
	this.uname = uname;
	this.age = age;
	this.sing = function() {
		concole.log('我会唱歌');
	}
}
var zs = new Star('张三',18);
var lisi = new Star('李四',20);

在这里插入图片描述

unameage 都是 基本数据类型,直接赋值就可以了,但是里面有方法,就是一个函数,因为函数是复杂数据类型,每一个实例对象都会创建一个新的内存 用来存放函数,可是这些对象实例访问的是同一个函数,这样就很浪费内存,如果一两个对象还好,可是对象多了呢?那么每个实例对象都会去创建一个空间存放函数,而开辟空间也需要时间,实例对象一多就会程序运行也会变得缓慢!

代码验证:

function Demo(uname,age) {
    this.uname = uname;
    this.age = age;
    this.sing = function() {
        console.log('我会唱歌');
    }
}
var zs = new Demo('张三',18);
var ls = new Demo('李四',18);
console.log(zs.sing === ls.sing);
//显示结果为false,因为这两个比较的地址,他们的方法都来自同一个构造函数,
//但是实例的对象方法地址却不一样。

这种结果显然不是我们想要的,我们想要所有的对象都使用同一个函数,这样就比较节省内存,那么怎么做呢?

就用到接下来的原型知识点

构造函数原型

构造函数通过 原型(prototype) 分配 的函数是所有对象所共享的。

JavaScript 规定 每一个构造函数都有一个prototype属性, 指向另一个对象,注意这里的 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

我们随便创建一个构造函数来查看一下是否存在prototype 属性

function Test() {

}
console.dir(Test);

显示结果:

在这里插入图片描述

原型作用: 我们可以把那些不变的方法直接定义在prototype对象上。这样所有的实例都可以共享这些方法

我们再用代码验证一下原型是否可以让实例访问同一个内存地址中共享的方法:

function Demo(uname,age) {
    this.uname = uname;
    this.age = age;
}
Demo.prototype.sing = function() {
    console.log('我会唱歌');

}
var zs = new Demo('张三',18);
var ls = new Demo('李四',18);
console.log(zs.sing === ls.sing);
//显示结果为true,因为这两个实例访问的都是构造函数原型对象上的方法!
//一般情况下,我们把公共属性定义到构造函数里面
//公共的方法,我们放在原型对象上

看完思考:

  • 1.原型是什么?

    一个对象,我们也称 prototype 为 原型对象

  • 原型的作用?(就三个字)

    共享方法

到这里小伙伴们可能有些迷惑,为什么实例对象可以访问构造函数创建出来的prototype对象的方法和属性呢?

那是因为实例对象 有一个对象原型 _proto_

对象原型_proto_

对象都有一个属性__proto__ 指向构造函数的prototype 原型对象 ,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有 __proto__原型的存在。

  • __proto__ 对象原型和原型对象prototype是等价的
function Demo(uname,age) {
    this.uname = uname;
    this.age = age;
}
Demo.prototype.sing = function() {
    console.log('我会唱歌');

}
var zs = new Demo('张三',18);
//测试 zs 的 __proto__ 是否等于 Demop的prototype
console.log(zs.__proto__ === Demo.prototype);  //结果为true,说明__proto__ 和构造函数原型对象是等价的
  • __proto__ 对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但它是一个非标准的属性(或者说隐式的属性),因此在实际开发中,不可以使用这个属性,他只是内部指向原型对象prototype

  • 构造函数、对象实例、原型对象prototype 三者提供一个如下图的关系:

在这里插入图片描述

原型中的constructor

对象原型(__proto__)构造函数(prototype)原型对象 里面都有一个constructor 属性,constructor 我们称为构造函数,因为它只会构造函数本身。

constructor 主要记录该对象引用与哪个构造函数,它可以让原型对象重新指会原来的构造函数

很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数,为什么这么说呢?如下:

function Star(uname,age) {
    this.uname = uname;
    this.age = age;
}
//这种方式是向对象添加方法,但是对我们方法比较多的时候呢,这样每次都要写Star.prototype多麻烦,
//反正都是对象,我们何不采取另一种方法添加方法呢?

// Star.prototype.sing = function() {
//     console.log('我会唱歌');
// }
Star.prototype = {
    //让原型对象可以重新构造函数
    constructor:Star,
    sing:function() {
        console.log('唱歌');   
    },
    movie:function() {
        console.log('演电影');
    }
}
var ldh = new Star('刘德华',20);
var gtl = new Star('古天乐',18);
//打印一下 gtl 的对象原型 constructor
console.log(gtl.__proto__.constructor);
/* 
     控制台输出为:ƒ Object() { [native code] }  
     我们之前看到过这三者的关系图知道,
     gtl原本应该指向Star构造函数的  
     之前我们写的是采取点的形式添加方法
     而 Star.prototype = {} 这种方式是重新赋值,就是 = 后面的会覆盖原有的对象
     这时候我们就需要手动添加 constructor 让原型对象重新指回原来的构造函数
*/

构造函数、实例、原型对象三者之间关系

在这里插入图片描述

原型链

我们通过前面的知识知道,只要是对象就会有__proto__ 指向原型对象,那我们的Star 原型对象prototype指向谁呢?这就涉及到原型链的知识点了,如下图:

在这里插入图片描述

JavaScript 成员查找机制 (规则)

① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身 有没有该属性

② 如果没有就查找它的原型(也就是__proto__ 指向的 prototype原型对象

③ 如果还没有就查找原型对象的原型 (Object的原型对象

④ 以此类推一直找到 Object为止(null

⑤ __proto__ 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一个路线

原型对象小应用(扩展内置对象)

//this指向调用者(实例成员)
Array.prototype.sum = function() {
    var sum = 0;
    for(var i = 0; i < this.length;i++) {
        sum += this[i];
    }
    return sum;
}
var arr = [10,20,30];
console.log(arr.sum()); //输出结果为60,成功添加
//我们再看看 Array的原型对象上的方法
console.dir(Array.prototype); //可以看到我们自己添加的求和方法

在这里插入图片描述

继承

ES6之前并没有给我们提供 extends 继承。我们可以通过 构造函数 + 原型对象 模拟实现继承,被称为 组合继承

使用 call() 实现继承

调用这个函数,并且修改函数运行时的this指向

//语法
fun.call(thisArg,arg1,arg2,...);
  • thisArg:当前调用函数this 的指向对象
  • arg1, arg2 :传递的其它参数

call()用法:

//call()用法
function fn(x,y) {
    console.log('hello world');
    console.log(this);    
    console.log(x + y);

}
var o = {
    name:'张三'
}
//用法一:调用函数
fn.call();   //这时候this指向window
//用法二:改变this指向
fn.call(o); //此时this就改变了,this指向了o这个对象
//用法三:传递参数
fn.call(o,10,20);  //改变了this指向。显示运算结果

借用构造函数继承父类型属性

核心原理: 通过 call() 把父类的this指向子类型的this,这样就可以实现子类型继承父类型的属性。

//借用父构造函数继承属性
//1)父构造函数
function Father(uname,age) {
    this.uname = uname;
    this.age = age;
}
//2)子构造函数
function Son(uname,age) {
    Father.call(this,uname,age);
}
var gtl = new Son('古天乐',18);   //实现了继承父类的属性,自己也可以添加属性
console.log(gtl);

在这里插入图片描述

借用构造函数继承父类型方法

//借用父构造函数继承属性
//父构造函数原型对象里面的公有方法
Father.prototype.money = function() {
    console.log(1000000);
}
//父构造函数
function Father(uname,age) {
    this.uname = uname;
    this.age = age;
}

//子构造函数继承父类的方法
Son.prototype = new Father();
Son.prototype.constructor = Son;
//2)子构造函数
function Son(uname,age) {
    Father.call(this,uname,age);
}
var gtl = new Son('古天乐',18);   //实现了继承父类的属性,自己也可以添加属性
console.log(gtl);
console.log(gtl.money()); //已经实现了访问父类上的方法。
console.log(gtl.__proto__.constructor); 
      
/* 
   但是通过 = 的赋值方式会把Son.prototype原型对象覆盖, 
   而 Son.prototype 原型对象上的constructor 本该指向Son的构造函数,
   现在却指向了Father 构造函数,所以我们需要在Son.prototype原型对象上手动添加
   constructor 指向Son。现在就实现了继承父类方法
 */

详细图解继承父类方法:

在这里插入图片描述

ES5新增的一些方法

ES5给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:

  • 数组方法
  • 字符串方法
  • 对象方法

数组方法

迭代(遍历)方法:forEach()、map()、filter()some()、every

forEach

// forEach 语法
array.forEach(function(currentValue,index,arr){});
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组 对象 本身

栗子:

var arr = [10,20,30];
arr.forEach(function(value,index,arr) {
    console.log('每个数组元素:'+value);
    console.log('每个数组元素索引:'+index);
    console.log('数组元素本身:'+arr);
});

在这里插入图片描述

filter()

// filter 语法
array.filter(function(currentValue,index,arr){});
  • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用来筛选数组
  • 注意点:直接返回一个新的数组
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身

栗子:

// filter()筛选数组
var arr = [10,3,5,20,55];
//返回新数组,需要变量接收
var newArr = arr.filter(function(value,index,arr) {
    // return value >= 10; 
    return value % 2 === 0;   //也可以筛选偶数
});
// console.log(newArr);  // 筛选结果:(3) [10, 20, 55]
console.log(newArr);

some()

// some() 语法
array.some(function(currentValue,index,arr){})
  • some() 方法用于检测数组中的元素是否满足指定条件,通俗点,查找数组中是否有满足条件的元素。
  • 注意点:返回值是布尔值,如果查找到这个元素就返回true,如果找不到就返回false
  • 如果找到第一个满足条件的元素,则终止循环,不在继续查找
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身

举个栗子:

// some 查找数组中是否具有满足条件的元素
var arr = ['red','blue','lightblue','orange'];
//返回新数组,用变量接收
var newArr = arr.some(function(value) {
    return value === 'orange';
});
console.log(newArr);  //返回结果为true,说明找到了 'orange'

filter() 和 some() 的区别

  • filter 也是查找满足条件的元素,返回的是一个数组,而且是把所有满足条件的元素返回回来
  • some 也是查找满足条件的元素是否存在,返回的是一个布尔值,如果查找到第一个满足条件的元素就终止循环

根据这些新增方法的小案例:

在这里插入图片描述

// ==============html====================
<div class="box">
    <div class="query-bar">
        <label>按照价格查询:<input type="text" name="min-price" id="min-price" size="5"> - <input type="text" name="max-price"
                                                                                            id="max-price" size="5"> <input type="button" value="搜索" id="search"></label>
        <label>按照商品名称查询:<input type="text" name="product-name" id="product-name" size="5"> <input type="button" value="查询"
                                                                                                  id="find"></label>
    </div>
    <table>
        <thead>
            <tr>
                <th>id</th>
                <th>产品名称</th>
                <th>价格</th>
            </tr>
        </thead>
        <tbody>
            <!-- 动态加载数据 -->
        </tbody>
    </table>
</div>
// ============== Css ====================
.box {
width: 600px;
margin: 30px auto;
}

table {
width: 500px;
margin: 20px auto;
border-collapse: collapse;
border: 1px solid #000;
text-align: center;
}

th,
td {
border: 1px solid #666;
}

// ==============JS====================
//表格数据
var data = [{
    id: 1,
    proname: '小米',
    price: 3999
}, {
    id: 2,
    proname: 'oppo',
    price: 999
}, {
    id: 3,
    proname: '荣耀',
    price: 1299
}, {
    id: 4,
    proname: '华为',
    price: 1999
}];

//1)获取元素
var tbody = document.querySelector('tbody');
var min_price = document.getElementById('min-price');
var max_price = document.getElementById('max-price');
var search = document.getElementById('search');
var product_name = document.getElementById('product-name');
var find = document.getElementById('find');

//动态插入数据
getData(data);
function getData(ele) {
    tbody.innerHTML = '';  //先清除表格的数据再添加
    ele.forEach(function (value) {
        var tr = document.createElement('tr');
        tr.innerHTML = '<td>' + value.id + '</td><td>' + value.proname + '</td><td>' + value.price + '</td>'
        tbody.appendChild(tr);
    });
}
//按照价格查询
search.addEventListener('click',function(){
    var newData =  data.filter(function(value) {
        return value.price >= min_price.value && value.price <= max_price.value
    });
    //把查询的结果插入到表格中
    getData(newData);
});

//根据名称查询
find.addEventListener('click',function() {
    var arr = [];
    data.some(function(value) {
        if(value.proname === product_name.value.trim()) {
            arr.push(value);
            return true;
        }
    });
    getData(arr);
})

字符串方法

trim() 方法会从一个字符串的两端删除空白字符

str.trim();

trim() 方法并不会影响原字符串本身,它返回的是一个新的字符串。

举个栗子:

//未使用trim()方法 var str = ' hello '; console.log(str);

在这里插入图片描述

//使用了 trim()方法 var str = ' hello '; console.log(str); console.log(str.trim());

在这里插入图片描述

对象方法

Object.keys()

Object.keys() 用于获取对象自身所有的属性

//语法
Object.kays(obj);
  • 效果类似:for … in
  • 返回一个有属性名组成的数组

小栗子:

var obj = {
    name:'张三',
    age: 18,
    sex: '男'
}
var arr = Object.keys(obj);
console.log(arr);
//Object.keys() 可以直接把属性名存在数组,而 for..in 还需要自己定义一个数组,
//然会把属性名取出来存入数组

Object.definePropety()

Object.definePropety() 定义对象中新属性或修改原有的属性

// 语法
Object.definePropety(obj,prop,descriptor);
  • obj :必须。目标对象
  • prop : 必须。需定义或修改的属性的名字
  • descriptor:必须。目标属性所拥有的特性【以对象的形式书写】
    • value:设置属性的值。默认为undefined
    • writable:值是否可以重写。true | false 默认为false
    • enumerable:目标属性是否可以被枚举(简单理解是否可以被遍历)。true | false 默认为false
    • configurable:目标属性是否可以被删除或者 是否可以再次修改特性。true | false 默认为false
// ===============第三个参数是对象属性是value
var obj = {
    name:'张三',
    age: 18,
    sex: '男'
}
//以前的对象添加和修改属性的方式
// obj.money = 100;
// obj.age = 22;
// console.log(obj);
//Object.definePropety() 定义对象中新属性或修改原有的属性 
Object.defineProperty(obj,'money',{
    value:999      //obj目标对象,'money'对象属性名,第三个参数是一个对象 value 是'monry'的属性值
});              //如果obj对象存在'money'这个属性名那就表示再对象中修改'money'这个属性名的值
console.log(obj);  
// ===============第三个参数对象属性是writable
var obj = {
    id:1,
    name:'张三',
    age: 18,
    sex: '男'
}
//如果我对象有些属性不希望被更改,那就可以使用writable
Object.defineProperty(obj,'id',{
    writable:false   //writable 不允许某些属性被修改。默认值是false不允许修改
});
obj.id = 2;       //当我们对id进行了更改限制,再修改输出的时候我们发现id 已经更改不了了
console.log(obj);
// ===============第三个参数对象属性是enumerable
var obj = {
    id:1,
    name:'张三',
    age: 18,
    sex: '男'
}
//如果我对象有些属性不希望被更改,那就可以使用writable
Object.defineProperty(obj,'id',{
    enumerable:false      //设置id不允许被枚举(遍历)
});

console.log(Object.keys(obj));  //控制台结果:(3) ["name", "age", "sex"] 这时候id就不可以被遍历出来
// ===============第三个参数对象属性是configurable
var obj = {
    id:1,
    name:'张三',
    age: 18,
    sex: '男'
}
Object.defineProperty(obj,'id',{
    configurable:false      //设置id不允许被删除或者修改 true允许,false禁止,默认false
});
//我们尝试删除id
delete obj.id;
console.log(obj);  //结果没有被删除

//另一个特性 id之前设置 configurable:false  那就不允许再做修改
Object.defineProperty(obj,'id',{
    configurable:true    //当我们再次修改id的 configurable:true 时就会显示报错
});
console.log(obj);

JS this指向问题

这些 this 的指向 是当我们调用函数的时候确定的,调用方法的不同决定了this的指向不同。但是这个this也不是不可变的,可以通过 call()、apply()、bind() 修改,下面先看this指向

一般this指向我们的调用者

调用方法this指向
普通函数调用window
构造函数调用实例对象,原型对象里面的方法也指向实例对象
对象方法调用该方法所属对象
事件绑定调用绑定事件对象
定时器函数window
立即执行函数window

实例演示:

// 1.普通函数this
function fn() {
    console.log('普通函数的this:'+this);

}
fn();        //结果为window
// 2. 构造函数this
function Star() {
    console.log('构造函数的this:'+this);

}
var zs = new Star();
console.log(zs); //指向zs这个实例对象

// 2.1 构造函数原型this
Star.prototype.movie = function() {
    console.log('原型对象this:'+this);
}
console.log(zs.movie()); //原型对象上的this也指向实例对象

// 3 对象方法this
var obj = {
    name:'张三',
    age: 20,
    sing:function() {
        console.log('对象方法的this:'+this);
    }
}
obj.sing() //对象方法的this指向这个对象 obj

// 4 事件绑定this
var btn = document.querySelector('button');
btn.onclick = function() {
    console.log('事件绑定的this:'+this);  //结果是这个调用者,就是button按钮
}
// 5 定时器函数this
setTimeout(function() {
    console.log('定时器的this:'+this);

},1000);        //结果为window

// 6 立即执行函数this
(function(){
    console.log('立即执行函数里面的this:'+this);  //也是指向window
}())

修改this指向

JavaScript 为我们专门提供l1一些函数方法来帮助我们更优雅的处理函数内部this的指向问题,常用的又bind()、

call()、apply() 三种方法。

1.call方法

call() 方法 调用 一个对象,简单理解为调用函数的方法,但是它可以改变函数的 this指向。

// 语法
fun.call(thisArg,arg1,arg2,...);

举个栗子:

var obj = {
    name:'张三'
}
function fn() {
    console.log('我的this指向为:'+this);

}
fn.call();  //如果什么参数都不写,相对于调用函数执行。此时函数内this 还是指向window 

那要怎么改变这个函数内的 this 指向呢?这就用到了 call方法的参数

var obj = {
    name:'张三'
}
function fn(x,y) {
    console.log('我的this指向为:'+this);
    console.log(x + y);


}
fn.call();  //如果什么参数都不写,相对于调用函数执行。此时函数内this 还是指向window 
fn.call(obj); //我们把对象obj传进去,此时 函数内的this就指向了 obj 对象
fn.call(obj,10,20); //这个 10 20 就是我们之前普通函数的实参,可以传到 函数内进行运行等操作

当然了 call 还有一个很重要的作用就是实现继承

function Father(uname,age) {
    this.uname = uname;
    this.age = age;
}
function Son(uname,age) {
    //把Father调用过来,并且把Father的this 修改为Son的this
    Father.call(this,uname,age);
}
var wwu = new Son('王五',23);
console.log(wwu); //此时Son就继承了 Father 的属性

2.apply() 方法

apply 方法 调用 一个对象,简单理解为调用函数的方法,但是它可以改变函数的 this指向。 这个方法跟 call() 非常相似,但是有一点不同大家记住就行了,那就是apply的参数采用数组形式

// 语法跟call() 差不多
fn.apply(thisArg,[argsArray]);
  • thisArg:再 函数运行时指定的this值
  • argsArray:传递的值,必须包含再 数组 里面
  • 返回值就是函数的返回值,因为apply() 也会调用函数

代码实例:

var obj = {
    name:'Hello'
}
function fn() {
    console.log('我的this指向:'+this);

}
fn.apply();  //经过前面的栗子我们知道不传递参数就相对于调用函数,此时this指向window
fn.apply(obj);  //这时候我们把obj对象传入 apply方法内 this就指向了 obj对象

在这里插入图片描述

call() 的只有作用可以实现继承,那apply呢,它的作用就是可以让我们借助于js内置对象最数组运算,因为它的参数就是数组或者伪数组

var arr = [11,2,50,34,20];
var max = Math.max.apply(Math,arr); //这个不需要改变this指向所以还是让这个参数指向Math对象
var min = Math.min.apply(Math,arr);
console.log(max,min);    //结果为50,很方便的找到了最大值,最小值

bind() 方法

bind() 方法不会调用函数,但是也可以改变函数内 this 的指向

// 语法
fun.bind(thisArg,arg1,arg2,..);
  • thisArg:再fun函数运行时指定的 this
  • arg1,arg2:传递的实参
  • 返回由指定的this值和初始化参数改造的 原函数拷贝

代码实例:

var obj = {
    name:'Hello'
}
function fn(x,y) {
    console.log('我的this指向:'+this);
    console.log(x + y);


}
var f = fn.bind(obj,10,20);
f();
/* 
   1. 不会调用原函数,但是可以修改原函数的 this指向
   2. 返回的是原函数改变了this之后产生的新函数,所以需要一个变量去接收
   3. 使用情况:如果有的函数不需要我们立即调用,但是又想改变这个函数内部的 this 用bind最合适
*/

bing() 的使用场景代码实例:

  • 现在有几个按钮,当我们点击了了之后,就禁用这个按钮,3秒之后自动开启这个按钮。因为这个场景不是立即调用函数, 但是有需要改变定时器的 this 的指向
<button>按钮一</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
    var btn = document.querySelectorAll('button');
for(var i = 0; i < btn.length; i++) {
    btn[i].onclick = function() {
        this.disabled = true;  //点击这个按钮这个按钮禁用
        //设置定时器 3 秒之后开启这个按钮
        setTimeout(function() {
            //大家想想,这里可以使用this吗。 文章刚开始就介绍了this指向,再定时器里面 this指向window
            //可能有些忍想到了btn[i].disabled = false. 但是在这里有个问题,等定时器过了3 秒之后
            // i 的值已经是 3了, 而button有没有对应的按钮,此时就会报错
            // 这时候就需要 bing来修改定时器里面的this的指向
            this.disabled = false;
        }.bind(this),3000)
    }
}

在这里插入图片描述

call()、apply() 、bind() 总结

相同点:

都可以改变函数内 this 指向

区别点:

1.call 和 apply 会调用函数,并且改变函数内部 this 指向

2.call 和 apply 传递的参数不一样,call 传递参数 arg1,arg2形式,apply 必须 [args]形式

3.bind 不会调用函数,但是可以改变函数内部 this 指向

三者应用场景区别:

1.call 经常用于继承

2.apply 经常跟数组有关,比如借助数学对象实现数组求最大值最小值

3.bind 不调用函数,但是还想改变 this 指向。比如改变定时器内部的 this 指向

JS 严格模式

什么是严格模式

  • JavaScript除了提供正常模式外,还提供了 严格模式( strict mode)。 ES5的严格模式是采用具有限制性
  • JavaScript变体的一种方式,即在严格的条件下运行S代码
  • 严格模式在10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
  • 严格模式对正常的 JavaScript语义做了一些更改:
    • 1.消除了 Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为。
    • 2.消除代码运行的些不安全之处,保证代码运行的安全。
    • 3.提高编译器效率,增加运行速度。
    • 4.禁用了在 ECMAScript的末来版本中可能会定义的些语法,为未来新版本的 Javascript 做好铺垫。比
      如一些保留字如: class. enum, export, extends, import, super 不能做变量名

开启严格模式

严格模式可以应用于 整个脚本个别函数中,因此再使用时,我们可以将严格模式分为 为脚本开启严格模式为函数开启严格模式 两张情况

1.为脚本开启严格模式:

为所有脚本开启严格模式,需要在所有语句之前放一个特定语句 “use strict;”

<script>
	"use strict";
    console.log('这是严格模式');
</script>

因为 “use strict" 加了引号,所以老版本的浏览器会把它当作一行普通的字符而忽略

2.为函数开启严格模式

有的 script 脚本是严格模式,有的 script 脚本 是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行函数之中,这样独立创建一个作用域而不影响其它 script 文件

<script>
	(function(){
    	"use strict";
    	var num = 10;
    	function fn() {}
	}());

</script>

严格模式中的变化

严格模式对 JavaScript 的语法和行为,都做了一写改变。

变量规定:

  • 在正常模式中,如果一个变量未经声明就赋值,默认是全局变量,严格模式禁用这种写法,严格模式下 变量都必须先用 var 声明 然后再使用
  • 严格模式 禁止删除已经声明变量,例如:delete x 语法是错误的

函数规定:

  • 函数不能有重名的参数

  • 函数必须声明再顶层,新版本的 JavaScript 会引入 ”块级作用域“(ES6中已引入),为了与新版本接轨,不允许在非函数的代码块内声明函数

    // 比如if
    if() {
    	function fn() {}}
    
    // 比如 for
    for(var i = 0;i < 10; i++) {
    	function fn2() {}}
    

严格模式下 this 指向问题:

  • 以前在全局作用域函数中的ths指向 window对象。
  • 严格模式下全局作用域中函数中的this是 undefined
  • 以前构造函数时不加 new 也可以调用当普通函数,ths指向全局对象
  • 严格模式下如果构造函数不加new调用this会报错
  • new实例化的构造函数指向创建的对象实例
  • 定时器ths还是指向 window。
  • 事件,对象的 this 还是指向调用者

高阶函数(回调)

高阶函数 是对其它函数进行操作的函数,它 接收函数作为参数或 将函数作为返回值输出

// 如:
function fn(x,y,callback) {
    console.log(x + y);
    callback && callback();
}
fn(10,20,function() {alert('Hello')});

闭包

闭包(closure) 值有权访问 另一个函数作用域中变量 的 函数 — 出自 JavaScript 高阶程序设计

简单理解就是,一个作用域可以访问另外一个函数内部的局部变量

闭包是什么?

闭包是一个函数,(一个作用域可以访问另外一个函数内部的局部变量)

闭包的作用是什么?

延伸变量的作用范围

递归

概述

如果一个函数在内部可以调用其本身, 那么这个函数就是 递归函数

简单理解:函数内部自己调用自己,这个函数就是递归函数

递归函数的作用和循环效果一样

由于递归很容易发生 “栈溢出” 错误(stack overflow),所以 必须要加退出条件 return。

以下案例:

//递归函数 : 函数内部自己调用自己,这个函数就是递归函数
var num = 1;
function fn() {
    console.log('我要打印六遍');
    //退出条件
    if(num === 6) {
        return;
    }
    num++;
    fn(); //递归
} 
fn();

递归小应用

// 求阶乘
function fn(n) {
    //退出条件
    if(n == 1) {
        return n;
    }
    return n * fn(n - 1); 	//递归
}
console.log(fn(5));
// 求斐波那契数列
function fn(n) {
    if(n == 1 || n == 2) {
        return 1;
    }
    return fn(n -1) + fn(n - 2);
}
console.log(fn(5))
// 根据id 返回对应的数据对象 
var data = [{
    id:1,
    name:'家电',
    goods:[{
        id:11,
        name:'冰箱'
    },{
        id:22,
        name:'洗衣机'
    }]
},{
    id:2,
    name:'服装'
}]

//我们想要输入 id 数,就可以返回对应的数据对象
function getId(json,id) {
    var o = {};  //定义一个对象接收符合要求的对象
    //第一步:遍历数据
    json.forEach(function(item) {
        if(item.id == id) {
            o =  item;
        }
        //但是这种方式只能执行一次,如果我们需要id为11的数据怎么办呢?
        //在遍历一遍数据吗?那是不是太麻烦了。这时候就可以用我们的递归了
        //里面应该有goods 这个数组,并且这个数组长度要大于0
        else if(item.goods && item.goods.length > 0) {
            o = getId(item.goods,id);
            //没有退出条件的原因:因为数据是有限的,forEach遍历完了就不会执行了,完美结束条件
        }

    });
    return o;
}
console.log( getId(data,1));
console.log( getId(data,2));  
console.log( getId(data,11));
console.log( getId(data,22));

浅拷贝和深拷贝

1.浅拷贝只是拷贝一层,更深层次对象级别的只拷贝印用。

2.深拷贝拷贝多层,每一级别的数据都会被拷贝

3.Object.assign(target,….sources) es6新增方法可以浅拷贝

代码演示:

//待定 明日写(明日复明日,明日何其多!!)

JS正则表达式

概述

正则表达式(Regular Expression) 是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线,昵称输入框中可以输入中文(匹配) 。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分 (提取)等。

正则特点

  1. 灵活性、逻辑性和功能性非常的强。
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。比如:^\w+([- + ]w+)*@\W +([-.]\w+)*\ .\w+([-.]\w+)*$
  4. 实际开发,一般都是直接复制写好的正则表达式,但是要求会使用正则表达式并且根据实际情况修改正则表达式,比如用户名:/1(3,16)$/

创建正则表达式

在JavaScript 中,可以通过两种方式创建一个正则表达式

1.通过调用RegExp对象的构造函数创建

var 变量名 = new RegExp(/表达式/);

2.通过字面量创建

var 变量名 = /表达式/;

//注释中间方正则表达式就在正则字面量

正则表达式的组成

一个正则表达式 可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如/ab*c/。其中特殊字符也被称为 元字符,在正则表达式中是具有特殊意义 的专用符号,如^、$、+等。
特殊字符非常多,可以参考:

  • MDN
  • jQuery手册:正则表达式部分
  • 正则测试工具:链接

边界符

正则表达式中的边界符(位置符)用来 提示字符所处的位置,主要有两个字符

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)
// 代码演示
//1) 边界符 ^ $
var rg = /abc/; //表示只要包含有abc这个字符串就返回 true
console.log(rg.test('abc'));    //true
console.log(rg.test('abcd'));   //true
console.log(rg.test('aabc'));   //true

console.log('-------------------------');

var reg1 = /^abc/;  //表示以abc开头的字符
console.log(reg1.test('abc'));  //true
console.log(reg1.test('bca'));  //false
console.log(reg1.test('cab'));  //false
console.log(reg1.test('abcabc'));   //true

console.log('--------------------------');

var reg2 = /^abc$/; //表示精确匹配,只能是abc
console.log(reg2.test('abcd')); //false
console.log(reg2.test('abc'));  //true
console.log(reg2.test('abbc')); //false
console.log(reg2.test('ccabc'));    //false

字符类

字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的都放在方括号内

[-] 方括号内部 范围符 -

// 代码演示
//2) 字符类 [] 表示有一系列字符可供选择吗,只要匹配其中一个就可以了;
var reg3 = /[abc]/; //表示包含a 或者 b 或者c 都返回true  跟/abc/不同的是 /abc/ 字符串必须包含这三个字母。
console.log(reg3.test('abc'));  //true 包含a | b | c
console.log(reg3.test('b'));    //true 有b
console.log(reg3.test('c'));    //true  有c
console.log(reg3.test('ddd'));  //false 没有a b c任意一个字符

[^] 方括号内部 ^ 表示取反符

// 代码演示
var reg5 = /^[a-zA-Z0-9]$/;    //表示26个英文字母任意大小写都可以,或者0-9数字其中一个
console.log(reg5.test('aB'));  //false 含有两个字母
console.log(reg5.test('1'));    //true
console.log(reg5.test('A'));    //true
console.log(reg5.test('a'));    //true

console.log('--------------------');

var reg6 = /^[^a-zA-Z0-9]$/;  //如果^边界符在[] 括号内 表示取反
console.log(reg6.test('a'));    //false
console.log(reg6.test('A'));    //false
console.log(reg6.test('8'));    //false
console.log(reg6.test('-'));    //true

量词类

量词用来 设定某个模式出现的次数

量词说明
*重复0次或更多次
+重复一次或更多次
重复0次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n 到 m 次
//======= * =======
// 量词符 : 就是用来限定某个模式出现的次数
//简单理解:就是让下面的这个a 重复出现的次数
//var reg = /^a$/;
// * 相对于>=0 可以出现0次和很多次 出现任意次
var reg = /^a*$/;
console.log(reg.test(''));    //true
console.log(reg.test('a')); //true
console.log(reg.test('aa')); //true
// ========= + ============
//+相当于 >=1 可以出现1次或者很多次
var reg=/^a+$/;
console.log(reg.test(""));  //false
console.log(reg.test('a')); //true
console.log(reg.test('aaaa')); //true
// ========== ? ============
// ? 相当于 1|| 0 
var reg=/^a$/;
console.log(reg.test(''));      //false 
console.log(reg.test('a'));     //true 
console.log(reg.test('aaaa'));  //true
// {3 }就是重复3次
var reg10 = /^a{3}$/;
console.log(reg10.test(''));  //false
console.log(reg10.test('aaa')); //true
console.log(reg10.test('aaaa')); //false
console.log(reg10.test('aa'));  //false
//{3,}大于等于3
var reg11 = /^a{3,}$/;
console.log(reg11.test('aaa'));     //true
console.log(reg11.test('aaaa'));    //true
console.log(reg11.test('a'));       //false
console.log(reg11.test('aa'));      //false
//{3,16}大于等于3并且小于等于16
var reg12 = /^a{3,6}$/; 
console.log(reg12.test('aaa'));     //true
console.log(reg12.test('aaaaaa')); //true
console.log(reg12.test('aa'));      //false
console.log(reg12.test('aaaaaaa')); //false

预定义类

预定义类指的是 ,某些常见模式的简写方式

预定类说明
\d匹配 0 -9 之间的任一数字 相对于[0-9]
\D匹配所有0-9 之外的字符,相对于 [^0-9]
\w匹配任意的字母、数字、下划线,相对于[a-zA-Z0-9_]
\W除所有的字母、数字、下划线以外的字符,相对于[^a-zA-Z0-9_]
\s匹配空格(包括换行符、制表符、空格符等),相对于[\t\r\n\v\f]
\S匹配非空格字符,相对于[^\t\r\n\v\f]

replace 替换

replace() 方法可以实现替换字符串操作,原来替换的参数可以是一个字符串或者一个正则表达式。

stringObject.replace(regexp/substr/replacement);
// 第一个参数:被替换的参数,或者 正则表达式
// 第二个参数:替换为的字符串
// 第三个参数:返回值是一个替换完毕的新字符串

代码演示:

// replace 替换
var regStr = /[激情|黄色|sb]/g;
var str = '很有激情,很黄很暴力';
console.log( str.replace(regStr,'*'));

正则参数

/表达式/[switch]
  • switch(也称为修饰符)按照什么样的模式来匹配 有三种值:
  • g : 全局匹配
  • i : 忽略大小写
  • gi :全局匹配 + 忽略大小写

ES 6

简介

ES 的全称 ECMAScript,他是由ECMA国际标准化组织,指定的 一项脚本语言的标准花规范

年份版本
2015年6月ES2015
2016年6月ES2016
2017年6月ES2017
2018年6月ES2018

ES6 实际上是一个泛指,泛指ES2015的后续版本

每一次标准的诞生都意味着语言的完善,功能的加强, JavaScript语言本身也有一些令人不满意的地方

  • 变量提升特性增加了程序运行时的不可预测性。
  • 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码

ES6 let

ES6 新增的用于声明变量的关键字。

特性一:

  • let 声明的变量只在所处于的块级有效(防止变量全局污染)

    if(true) {
    	let a = 10;
    }
    console.log(a);  //a is not defined  使用var可以访问到
    

    【注意点:】 使用let 关键字声明的变量才具有块级作用域,使用var 声明的变量不具有会计作用域特性

  • 防止循环变量变成全局变量

     for(var i = 0; i < 3; i++) {}
    console.log(i);     //结果为 3 这个i变成了全局变量
    
    for(let i = 0; i < 3; i++) {}
    console.log(i);     //报错 找不到 i这个变量
    
  • 不存在变量提升

    console.log(a); undefined 变量提升未赋值状态
    var a = 10;
    
    console.log(b);	//let 声明不存在变量提升 报错 没有b变量
    let b = 20;
    
  • 形成暂时性死区

    var num = 10;
    if(true) {
        num = 20;
        console.log(num);   //结果20 意味着可以对外部变量进行访问操作
    }
    
    if(true) {
        console.log(num);  //报错 没有找到变量 
        //使用let关键字 再 {} 内使用会形成暂时性死区,形成了独立块级作用域
        let num = 20;
    }
    

let、const、var 区别

  • 使用 var 声明的变量,其作用域为:该语句所在的函数内,且存在变量提升现象
  • 使用 let声明的变量,其作用域为:该语句所在的代码块,不存在变量提升
  • 使用 const声明的是常量,在后面出现的代码:不能再修改该常量的值
varletconst
函数级作用域块级作用域块级作用域
变量提升不存在变量提升不存在变量提升
值可更改值可更改值不可更改

JS阶段二 (APIS)

API 和 WEBAPI

API

API( Application Programming Interface应用程序编程接口) 是些预先定义的函数,目的是提供应用程序

与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节

简单理解: API 是给程序员提供的一种工具,以便能更轻松的实现想要亮成的功能。

比如手机充电的接口:

我们要实现充电这个功能:

  • 我们不关心手机内部变压器,内部怎么存储电等
  • 我们不关心这个充电线怎么制作的
  • 我们只知道,我们拿着充电线插进充电接口就可以充电
  • 这个充电接口就是一个API

WEB API

Web API是浏览器 提供的套操作 浏览器功能和页面元素的API (BOM和DOM)

Mdn详情:MDN Api

什么是DOM

文档对象模型( Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言(HML
或者XML)的标准 编程接口
W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式

DOM树

在这里插入图片描述

  • 文档:一个页面就是一个文档,DOM 中使用 document 表示类名、ID、元素

  • 元素 : 页面中的所有标签都是元素,MOM 中使用element表示

  • 节点 : 网页中所有的内容都是节点(标签、属性、文本、注释等)

    DOM 把以上内容都看作是对象

获取页面元素

方法说明返回值
document.getElementById(‘id’);通过ID获取元素一个元素对象
document.getElementsByTagName(’’)通过元素名获获取过来元素对象的集合(伪数组)
document.getElementsByClassName(’’)通过类名获取类名获得某些元素集合
document.getElementsByName通过name属性获取常用于表单,获取某些元素的集合
document.querySelector(’’)类名、ID、元素返回指定选择器的第一个元素对象
document.querySelectorAll(’’)类名、元素返回指定选择器的所有元素对象集合
document.body;获取body元素
document.documentElement;获取html元素

鼠标事件

概述

JavaScript使我们有能力创建动态页面,而事件是可以被 JavaScript侦测到的行为。

简单理解: 触发–响应机制

网页中的每个元素都可以产生某些可以触发 JavaScript的事件,例如,我们同以在用户点击某按钮时产生一个

事件,然后去执行某些操作。

事件是由三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素

  • 事件源 事件被触发的对象 谁
  • 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下 绑定事件 注册事件
  • 事件处理程序 通过一个函数赋值的方式 完成
<button id = 'one'>点击事件</button>
//获取页面元素
var id = document.getelementbyId('one');
//鼠标点击事件操作元素
id.onclick = function(){
	alert('Hello');
}

常见的鼠标事件

鼠标事件触发条件
onclick鼠标点击左键触发
onmouseover鼠标经过触发
onmouseout鼠标离开触发
onmousemove鼠标移到触发
onmouseup鼠标弹起触发
onmousedown鼠标按下触发
onfocus获得鼠标焦点触发
onblur失去鼠标焦点触发

mouseenter 和 mouseover 区别

  • 当鼠标移动到元素上就会触发 mouseenter 时间
  • mouseover 鼠标经过自身盒子会触发、经过子盒子还会触发,mouseenter 只有经过自身盒子会触发
  • 之所以这样,就是因为mouseenter 不会冒泡
  • 跟mouseenter 搭配的 鼠标离开mouseleave 同样不会冒泡

操作元素

JavaScript 的DOM 操作可以改变网页内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。

element innerHTML  // w3c标准的 识别html标签 
element innerText // 非标准,不识别html标签,空格换行都会去掉。只能给元素添加文字,不能插入标签

常用元素属性操作

1. innerText、innerHTML 改变元素内容
2. src、href
3. id、alt、title

表单元素属性操作

利用DOM可以操作如下表单元素属性

type、 value、 checked、 selected、 disabled

作业练习(密码显示与隐藏):

<style>
        .box {
            position: relative;
            width: 300px;
            margin: 100px auto;
            border-bottom: 1px solid #ccc;
        }
        .box input {
            width: 80%;
            height: 30px;
            outline: none;
            border: none;
        }
        .move {
            display: none;
            position: absolute;
            top: 0px;
            right: 30px;
            font-size: 24px;
            color: #999;
        }
        label img {
            position: absolute;
            top: 4px;
            right: 3px;
            width: 24px;
        }
<//style>
<div class="box">
        <label>
            <img src="img/close.png" alt="">
            <span class="move">×</span>
            <input type="password" name="pwd" id="pwd">
        </label>
    </div>
    
    <script>
        var input = document.querySelector('input');
        var img = document.querySelector('img');
        var span = document.querySelector('span');

        var flag = true;
        img.onclick = function () {
            if (flag) {
                flag = false;
                input.type = 'text';
                this.src = 'img/open.png';
            } else {
                flag = true;
                input.type = 'password';
                this.src = 'img/close.png';
            }
        }
        input.oninput = function () {
            span.style.display = 'block';
            span.onclick = function () {
                input.value = '';
                this.style.display = 'none';
            }
            if (this.value == '') {
                span.style.display = 'none';
            }
        }
<//script>

样式属性操作

可以通过 JS 修改元素的大小、颜色、位置等样式

1. element.style    	//行内样式操作
2. element.className 	//类名样式操作

【注意】

JS 里面的样式采用驼峰命名法, 比如 fontSize、backgroundColor等

JS 修改css 样式操作,产生的是行内样式,css权重比较高

  • 如果样式修改较多,可以采用操作类名方式更改元素样式
  • class 因为是个保留字,因此使用className来操作元素类名属性
  • className 会直接更改元素的类名,会覆盖原先的类名

作业练习(淘宝关闭二维码):

<script>
    var btn = document.querySelector('.close-btn');
var box = document.querySelector('.box');
btn.onclick = function(){
    box.style.display = 'none';
}
</script>

作业练习(遍历淘宝精灵图插入元素中):

<script>
    var li = document.querySelectorAll('li');
	for(var i = 0;i < li.length; i++) {
    var index = i * -44;
    	li[i].style.backgroundPosition = '0 '+index+'px';
	}
</script>

排他思想

如果有同一组元素,我们想要某—个元素实现某种样式,需要用到循环的排他思想算法

在这里插入图片描述

  • 所有元素全部清除样式(干掉其他人)
  • 给当前元素设置样式(留下我自己)
  • 注意顺序不能颠倒,首先干掉其他人,再设置自己

自定义属性操作

自定义属性目的:是为了保存并使用数据,有些数据可以保存在网页中而不用存在数据库

为了避免有些自定义属性引起歧义,不容易判断该属性是元素内置还是自定义属性

H5给我们新增了自定义属性:

H5 规定自定义属性 date-开头作为属性名并且赋值

比如: <div date-index = '1'><div>

或者使用 JS 设置 element.setAttribute('date-index',2)

  • 兼容性获取属性 element.getAttribute(‘date-index’);
  • H5 新增 element.dataset.index 或者 element.dataset[‘index’] ie11以上版本才支持

示例

<div data-index = "2" data-list-name = "andy"></div>

var div = document.querySelector('div');
//  H5新增的获取自定义属性的方法只能获取 data- 开头的
//  dataset 是一个集合,里面存放了所有以 data- 开头的自定义属性
console.log(div.dataset);
console.log(div.dataset.index);
console.log(div.dataset['index']);
//  如果自定义属性里面有多个 - 连接的单词【data-list-name】
//  获取的时候采取驼峰命名法
console.log(div.dataset.listName);
console.log(div.dataset['listName']);

1.获取属性值

  • element.属性 获取属性值

  • element.getAttribute(‘属性值’);

区别

  • element.属性 获取内置属性值(元素本身自带的属性)
  • element.getAttribute(‘属性’); 主要获取自定义的属性(标准)程序员自己定义的属性

2.设置属性值

  • element.属性 = ‘值’ 设置内置属性
  • element.setAttribute(‘属性’,‘值’);

区别

  • element.属性 设置内置属性
  • element.setAttribute(‘属性’,‘值’); 主要设置自定义属性(标准)

3.移除属性

  • element.removeAttribute(‘属性’);

4.判断元素是否含有某个属性

  • obj.hasAttrinute(“attr”);

练习(选项卡切换)

//想法: 1.先给头部的每个li循环注册点击事件。
//		2.给头部的每个li绑定一个自定义属性
//		3.遍历所有内容的li,先让所有li隐藏
//		4.获取头部点击的li自定义属性索引,显示出索引对应的内容区li

var listli = document.getElementsByClassName('tab_list')[0].getElementsByTagName('li');
var conli = document.getElementsByClassName('tab_con')[0].getElementsByTagName('li');
var listLen = listli.length;
var conLen = conli.length;

//头部选项切换
for(var i = 0; i < listLen;i++) {
    //给自身添加自定义属性对应内容索引
    listli[i].setAttribute('data-index',i);
    listli[i].onclick = function() {
        for(var i = 0;i < listLen; i++) {
            listli[i].className = '';
        }
        this.className = 'current';
        //下面的内容显示模块
        var index = this.getAttribute('data-index');
        for(var j = 0;j < conLen; j++) {
            conli[j].style.display = 'none';
        }
        conli[index].style.display = 'block';
    }
}

节点获取增删改查

跟获取页面元素类型,不过节点操作是 利用节点层级关系获取元素

  • 利用父子兄弟节点关系获取元素
  • 逻辑性强,但是兼容性稍差
  • 一般的,节点至少拥有 nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点名称) 这三个基本属性

节点类型

节点类型说明
元素节点每一个HTML标签都是一个元素节点,如 <div> 、 <p>、<ul>等1
属性节点元素节点(HTML标签)的属性,如 id 、class 、name 等。2
文本节点元素节点或属性节点中的文本内容。3
注释节点表示文档注释,形式为 。8
文档节点表示整个文档(DOM 树的根节点,即 document )9
  • 实际开发中,节点操作主要操作的是元素节点

获取节点(层级)

利用 DOM 树可以把节点划分为不同类型的层级关系,常见的是 父子兄层级关系

1.父级节点

node.parentNode
  • parentNode 属性可返回某节点的父节点,注意是 最近的一个父节点
  • 如果指定的节点没有父节点则返回 null

2.子节点

1. parentNode.childNodes	//标准

parentNode, childNodes返回包含指定节点的子节点的集合,该集合为即时更新的集合。

注意: 回值里面包含了所有的子节点,包括元素节点,文本节点等

如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用 child Nodes

//利用nodeType 取出元素节点方法
var ul = document.querySelector ('ul');
for(var i= 0; i < ul.childNodeslength;i++) {
    if (ul.childNodes[i].nodeType === 1)(
    // u1. childNodes[i]是元素节点
    console. log(ul.childNodes[i]);
}

[重点掌握这个] 获取所有子节点

parentNode.children		//非标准

parentNode.children 是一个只读属性,返回 所有的 子元素节点。【重点】

虽然 children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用

3.第一个与最后一个节点

第一种方式 返回第一个节点

1. parentNode.firstChild

返回最后一个子节点

2. parentNode.lastChild
  • 这两种返回节点方式找不到返回null ,找到的话都会返回所有子节点,包含文本,注释节点等…
  • 实际开发中,我们并不想要全部节点,只想要元素节点,怎么办呢?看下面

第二种方式 返回第一个元素节点

3. parentNode.firstElementChild

parentNode.firstElementChild 返回第一个子元素节点,找不到则返回 null

4. parentNode.lastElementChild

parentNode.lastElementChild 返回最后一个子元素节点,找不到则返回null

【但是】:这两种方式都存在兼容性问题,IE9以上才支持,万恶的IE,别担心,我们还有第三种方式👇

第三种方式 获取第一个最后一个元素节点兼容性写法

实际开发的写法:既没有兼容性问题又能返回想要的元素节点

5. parentNode.children[0];  //取得第一个元素节点
6. parentNode.children[parentNode.children.length - 1]; //取得最后一个元素

子节点小练习(新浪导航栏)

//获取ul盒子
var nav = document.querySelector('.nav');
//获取显示的4个小li
var lis = nav.children;
//使用一个遍历存储li的长度,免得每次for循环都要获取
var Len = lis.length;

//for循环注册鼠标事件
for(var i = 0; i < Len; i++) {
    lis[i].onmouseover = function(){
        //a标签是li的第一个元素,li里面的ul是li的第二个元素
        this.children[0].className = 'currentA';
        this.children[1].style.display = 'block';
    }
    lis[i].onmouseout = function(){
        this.children[0].className = '';
        this.children[1].style.display = 'none';
    }
}

4.兄弟节点

第一种方式

1. node.nextSibling

nextSibling 返回当前元素的下一个兄弟节点,找不到返回 null。同样,也是包含所有节点

2. node.previousSibling

previousSibling 返回当前元素上一个兄弟节点,找不到返回 null。同样,也是包含所有节点

这俩傻缺方法跟前面获取第一个与最后一个元素方法的第一种方法一样,,让人无语

第二种方式

3. node.nextElementSibling

nextElementSibling 返回当前元素的下一个兄弟 元素 节点 ,找不到则返回null

4. node.previousElementSibling

nextElementSibling 返回当前元素的上一个兄弟 元素 节点 ,找不到则返回null

同样跟前面获取 第一个与最后一个元素方法 的第二种方法一样,存在兼容性问题。IE9以上版本才支持

创建节点

document.createElemet('tagName')
  • document.createElement () 方法创建由 tagName 指定的 HTML 元素,因为这些元素原本不存在,是根据我们需求动态生成的,所有我们也称为 动态创建元素节点

插入节点

node.appendChild(child)
  • node.appendChild() 方法将一个节点添加到指定父节点的子节点列表 末尾 类似于css里面的 ::after 伪元素

  • 缺点: 如果元素里面的内容较多,需要innerHTML赋值然后在 appendChild()追加到父元素里面 ,比较麻烦

  • 高级语法: 利用insertAdjacentHTML() 可以直接将字符串格式元素添加到父元素中

node.insertBefore(child,指定元素)
  • node.insertBefore() 方法将一个节点添加到指定父节点的指定 子节点 前面 类似于css里面的 ::before伪元素
node.insertAdjacentHTML("位置",text);

删除节点

1. 元素.remove();				//也可以删除
2. node.removeChild(child);	//删除父节内元素 node父节点
  • node.removeChild() 方法从 DOM 中删除一个子节点,返回删除的节点

案例:(简易留言板)

//最外层盒子(通过这个元素获取它的子元素))
var div = document.getElementsByClassName('box')[0];
//文本框
var txt = div.children[0];
//发布按钮
var btn = div.children[1];
//ul 获取这个元素添加li标签
var ul = div.lastElementChild;
//点击事件
btn.onclick = function() {
    if(txt.value == '') {
        alert('您还没有输入内容..');
        return false;
    } else {
        var li = document.createElement('li');
        li.innerHTML = txt.value +"<a href = 'javascript:;'>删除</a>";
        ul.insertBefore(li,ul.firstChild);
        txt.value = '';

        //删除元素
        var armove = li.firstElementChild;
        armove.onclick = function() {
            ul.removeChild(this.parentNode);
        }
    }
}

复制节点(克隆节点)

node.colneNode();
  • node.cloneNode() 方法返回调用该方法节点的一个副本,也称为克隆节点 / 拷贝节点

  • 【注意】 1.如果括号 参数为空或者为false ,则是 浅拷贝,即只克隆节点本身,不克隆元素里面的子节点

  • ​ 2.如果括号 ==参数为true ,则是 深度拷贝,会复制节点本身以及里面所有的子节点

练习(动态生成表格)

//模拟准备学生数据
var datas = [{   
    name:'魏璎珞',
    subject:'JavaScript',
    score:100
},{				
    name:'弘历',
    subject:'JavaScript',
    score:98
},{				
    name:'傅恒',
    subject:'JavaScript',
    score:99
},{
    name:'明玉',
    subject:'JavaScript',
    score:88
},{
    name:'大猪蹄子',
    subject:'JavaScript',
    score:0
}];
var tb = document.querySelector('tbody');
var len = datas.length;
//遍历数组创建元素
for(var i = 0; i < len; i++) {
    //创建行
    var tr = document.createElement('tr');
    tb.appendChild(tr);
    //行里面创建单元格 td 单元格的数量取决于对象的属性个数,所以需要遍历对象 datas[i]
    for(var k in datas[i]) {
        //创建单元格
        var td = document.createElement('td');
        //属性值插入单元格
        td.innerHTML = (datas[i][k]);
        //插入行
        tr.appendChild(td);
    }
    //创建删除单元格
    var td = document.createElement('td');
    td.innerHTML = "<a href = 'javascript:;' title = '点击删除'>删除</a>";
    tr.appendChild(td);
}
//最后一步 点击删除按钮删除数据
var close = document.getElementsByTagName('a');
var cloLen = close.length;
for(var j = 0; j <cloLen; j++) {
    close[j].onclick = function() {
        tb.removeChild(this.parentNode.parentNode);
    }
}

三种动态创建区别

  • document.write()
document.write(<div>ss</div>);
  • element.innerHTML
document.body.innerHTML(); 	//有一百行内没什么 ,但是超过100行就非常影响性能
  • document.createElement()
document.createElement();	//官方推荐

区别:

  1. document.write() 是直接将内容写入页面的内流,但是文档流执行完毕,则会导致页面全部重绘【非常严重哦,原有页面会丢失】

  2. innerHTML 是将内容写入某个DOM 节点,不会导致页面全部重绘

  3. innerHTML 创建多个元素效率更高==(不要拼接字符串,采取数组形式拼接,字符串的不可变性,拼接字符串还会导致效率很慢)==,结构稍微复杂

  4. createElement() 创建多个元素效率稍微低一点点,但是结构更加清晰 (先创建元素,再把内容追加到元素中)

【总结:】不同浏览器下,innerHTML效率要比createElement高(不拼接字符串前提下)

下面是 innerHTML 拼接字符串 ,使用数组和 createElement 效率对比方法

//	 1.innerHTML拼接字符测试(不同电脑硬件计算效率不同,我这里是【联想拯救者Y7000】执行时间约为:
//		1秒)

<button>点击添加</button>
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
    consoloe.time();
for (var i = 0; i < 1000; i++) {
    document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
}
consoloe.timeEnd();
});


function fn() {
    var d1 = +new Date();

    var str = '';
    for (var i = 0; i < 1000; i++) {
        document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
    }
    var d2 = +new Date();
    console.log(d2 - d1);
}
fn();
//	 2.innerHTML数组方式测试(不同电脑硬件计算效率不同,我这里是【联想拯救者Y7000】执行时间约为:
//		4毫秒,时间大大缩短)
function fn() {
    var d1 = +new Date();
    var array = [];
    for (var i = 0; i < 1000; i++) {
        array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');
    }
    document.body.innerHTML = array.join('');
    var d2 = +new Date();
    console.log(d2 - d1);
}
fn();
//	 2.createElement数组方式测试(不同电脑硬件计算效率不同,我这里是【联想拯救者Y7000】执行时间约为:
//		13毫秒)
function fn() {
    var d1 = +new Date();

    for (var i = 0; i < 1000; i++) {
        var div = document.createElement('div');
        div.style.width = '100px';
        div.style.height = '2px';
        div.style.border = '1px solid red';
        document.body.appendChild(div);
    }
    var d2 = +new Date();
    console.log(d2 - d1);
}
fn();

节点获取兼容性写法

高级浏览器使用能够获取 元素,文本(包括内容,空格,换行),注释。
低版本浏览器使用能够获取 元素,文本(不包含空格,换行),注释。

获取节点和元素节点的方法

节点 / 元素节点说明
node.parentNode最近一个父节点
parentNode.childNodes所有的子节点
parentNode.children所有的子元素节点 【重点可兼容】
parentNode.firstChild第一个子节点
parentNode…lastChild最后一个子节点
parentNode.firstElementChild第一个子元素节点【IE678不兼容】
parentNode.lastElementChild最后一个子元素节点【IE678不兼容】
node.nextSibling下一个兄弟节点
node.previousSibling上一个兄弟节点
node.nextElementSibling下一个兄弟元素节点【IE678不兼容】
node.previousSibling上一个兄弟元素节点【IE678不兼容】

获取第一个元素节点兼容

var ul = document.getElementsByTagName('ul')[0];
// 简洁方法
ul.children[0].style.background = 'orange';

//	①.兼容性封装获取第一个子元素:
function getFirstElementChild(ele) {
    if (ele.firstElementChild) {
        return ele.firstElementChild;
    } else {
        var node = ele.firstChild;
        while(node != null && node.nodeType != 1) {
            node =  node.nextSibling;
        }
        // node == null  或者  node.nodeType == 1
        return node;
    }
}
var demo = getFirstElementChild(ul);
console.log(demo);
demo.style.background = 'pink';

获取最后一个元素节点兼容

var ul = document.getElementsByTagName('ul')[0];
//简洁写法
ul.children[ul.children.length-1].style.background = 'lightblue'

// 函数封装写法
function getLastElementChild(ele) {
    if (ele.lastElementChild) {
        return ele.lastElementChild;
    } else {
        var nodeLast = ele.lastChild;
        while (nodeLast != null && nodeLast.nodeType != 1) {
            nodeLast = nodeLast.previousSibling;
        }
        return nodeLast;
    }
}
getLastElementChild(ul).style.background = '#008c8c';

获取上一个元素节点兼容

// ele.previousElementSibling 在IE678显示undefined

var li2 = document.getElementById('#li2');
function getPreviousElement(ele) {
    // 能力检测
    if(ele.previousElementSibling)  {  // 谷歌火狐
        return ele.previousElementSibling;
    } else {  // IE8
        // 获取上一个节点  :  null  元素  文本  注释
        var node = ele.previousSibling;

        // 循环次数不确定
        // 1. node必须存在, 不是null,  2. node不是元素节点
        while(node != null && node.nodeType != 1) {
            node =  node.previousSibling
        }
        // node == null  或者  node.nodeType == 1
        return node;
    }
}
var li = getPreviousElement(li2);
li.style.background = 'pink';

获取下一个元素节点兼容

var li2 = document.getElementById('#li2');

//兼容性封装获取上一个兄弟元素
function getNextElement(ele) {
    if (ele.nextElementSibling) {
        return ele.nextElementSibling;
    } else {
        var nodeNext = ele.nextSibling;
        while (nodeNext != null && nodeNext.nodeType != 1) {
            nodeNext = nodeNext.nextSibling;
        }
        return nodeNext;
    }
}
getNextElement(li2).style.background = 'pink';

DOM基础重点核心

文档对象模型

  • 文档对象模型( Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记己语言
    (HTM或者XML)的标准编程接口
  • W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式
    • ① 对于 JavaScript,为了能够使va5crp操作HM, JavaScrip就有了一套自己的dom编程接口
    • ② 对于HML,dom使得hm形成一棵dom树.包含文档、元素、节点

我们获取过来的DOM元素是
一个对象( object), 所以称为 文档对象模型

创建

关于 DOM 我们主要针对元素 的操作。主要有 创建属性操作时间操作

  • document.write
  • innerHTML
  • createElement

关于 DOM 我们主要针对元素 的操作。主要有 创建属性操作时间操作

  • appendChild
  • insertBefore

关于 DOM 我们主要针对元素 的操作。主要有 创建属性操作时间操作

  • removeChild

关于 DOM 我们主要针对元素 的操作。主要有 创建属性操作时间操作

主要修改 DOM 的元素属性、DOM的元素内容、属性、表单值等

  • 修改元素属性: src、href 、 title等
  • 修改普通元素内容:innerHTML 、 innerText
  • 修改表单元素:value、type、disabled等
  • 修改元素样式:style、textCss、className

关于 DOM 我们主要针对元素 的操作。主要有 创建属性操作时间操作

只要查询 DOM 的元素:

  • DOM提供的AP方法: getElementByld、 getElementsByTagName古老用法不太推荐
  • H5提供的新方法 querySelector.、 querysSelectorAl提倡
  • 利用节点操作获取元素: 父(parentNode)、子( children)、兄 (previousElementsibling
    nextElementSibling)提倡

属性操作

关于 DOM 我们主要针对元素 的操作。主要有 创建属性操作时间操作

注意针对自定义属性:

  • setAttribute : 设置 DOM 的属性值
  • getAttribute : 获取 DOM 的属性值
  • removeAttribute : 移除属性

事件操作

给元素注册事件、采取 事件源 👉 事件类型 👉 事件处理程序

鼠标事件触发条件
onclick鼠标点击左键触发
onmouseover鼠标经过触发
onmouseout鼠标离开触发
onmousemove鼠标移到触发
onmouseup鼠标弹起触发
onmousedown鼠标按下触发
onfocus获得鼠标焦点触发
onblur失去鼠标焦点触

事件高级 👇

## 注册事件

注册事件概述

给元素添加事件,称为 注册事件 或者 绑定事件

注册事件有两种方式: 传统方式方法监听注册方式

传统注册方式:

  • 利用on开头的事件 onclick
  • <button onclick = "alert(hi-)”>
  • btn.onclick = function(){}
  • 特点:注册事件的 唯一性
  • 同一个元素同个事件只能设置一个处理函数,最
    后注册的处理函数将会覆盖前面注册的处理函数
<button></button>
<script>
	var btn = document.querySelector('button');
    btn[0].onclick = function() {
        alert('He~');
    }
     btn[0].onclick = function() {
        alert('He~he');
    }
    //结果 只会执行一次后面的 因为前面的被覆盖了 传统注册事件的唯一性
</script>

方法监听注册事件

  • W3C 推荐的 标准方式
  • addEventlistener() 它是一个方法
  • IE9 之前不支持该方法,可使用 attachEvent() 代替
  • 特点:同一个元素同一个事件可以注册多个监听器
  • 按照注册顺序依次执行

语法:

//eventTarget 元素
eventTarget.addEventListener(type, listener[, useCapturel);

eventrarget.addEventListener() 方法将指定的监听器注册到 eventrarget(目标对象)上,当对
象触发指定的事件时,就会执行事件处理函数。

该方法接收三个参数:

  • type: 事件类型字符串,比如 click、 mouseover,注意这里不要带on
  • listener: 事件处理函数,事件发生时,会调用该监听数
  • useCapture:: 可选参数,是一个布尔值,默认是 false.学完DOM事件流后,我们再进步学习

IE(冷门事件)

attachEvent 事件监听方式:

eventTarget.attachEvent(eventNameWithon,callback)

eventTarget.attachEvent()方法将指定的监听器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,指定的回调函数就会被执行。

该方法接收两个参数:

  • eventNameWithOn: 事件类型字符串,比如onclick、onmouseover,这里要带on
  • callback: 事件处理函数,当目标触发事件时回调函数被调用

注册事件兼容性解决方案

function addEventListener(element, eventName, fn) {
    //判断当前浏览器是否支持 addEventlistener方法
if (element.addEventListener){
	element.addEventListener( eventName,fn);//第三个参数默认是 false
} else if (element. attachEvent){
    element. attachEvent ('on'+ eventName, fn);
} else {
        // 相当于 element.οnclick=fn;
         element['on'+ eventName]= fn;
	}
}

删除事件

1.传统事件删除方式

enentTarget.onclick = null;
如 div.onclick = null;

2.方法监听删除方式

enentTarget.removeEventLister(type,listener[,useCapture]);
// 如果需要删除事件,那么绑定方式将发生变化
div.addEventListener('click',fn); //里面的fn不需要加小括号,因为这样调用是把整个函数赋给调用者

function fn() {
    alert('aaa');
    //在这里面移除事件
    div.removeEventLister('click',fn);
}

3.IE9监听事件删除方式

enentTarget.datachEvent(eventNameWitOn,callback);

// 如果需要删除事件,那么绑定方式将发生变化
div.attachEvent('click',fn); //里面的fn不需要加小括号,因为这样调用是把整个函数赋给调用者
function fn() {
    alert('aaa');
    //在这里面移除事件
    div.datachEvent('click',fn);
}

4.删除事件兼容性封装函数

function removeEventListener(element, eventName, fn) {
    // 判断当前浏览器是否支持 removeEventlistener方法
    if (element. removeEventListener){
        element. removeEventListener( eventName,fn);//第三个参数默认是 false
    } else if (element. detachEvent) {
    	element. detachEvent('on'+ eventName, fn);
    } else {
         element['on'+ eventName] = null;
    }
   
}

DOM事件流

事件流 描述的是从页面中接收事件的顺序。

事件 发生时会在元素节点之间按照 特定的顺序传播, 这个传播 过程 即DoM事件流

比如我们给一个 div注册了点击事件:

DOM事件流分为3个阶段:

  1. 捕获阶段
  2. 当前目标阶段
  3. 冒泡阶段

在这里插入图片描述

事件冒泡: IE最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到DOM最顶层节点的过程。

事件捕获: 网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程

如:

我们向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具
体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点(最具体元素)之后漂浮到水面上,这个过
程相当于事件冒泡。

在这里插入图片描述

DOM 事件流三个阶段:

  • JS 代码中只能执行捕获 或者 冒泡 其中一个阶段

  • onclickattachEvent(IE) 只能得到 冒泡阶段

  • 捕获阶段,如果 addEventListener 第三个参数是 true 那么则处于捕获阶段

    document → html → body → father → son

【捕获阶段】点击子元素 但是有捕获阶段,所以会先执行父级的事件 document → html → body → father → son

<div class="father">
    <div class="son"></div>
</div>

<script>
    var son = document.querySelector('.son');
    son.addEventListener('click',function(){
        alert('son');
    },true);
    var father = document.querySelector('.father');
    father.addEventListener('click',function(){
        alert('father');
    },true);
</script>

在这里插入图片描述

【冒泡阶段】如果 addEventListener 第三个参数是 false 或者省略,那么处于冒泡阶段。点击子元素 但是有冒泡阶段,所以会先执行自身的事件,然后往上查找事件 步骤: son → father → body → html → document

<div class="father">
    <div class="son"></div>
</div>

<script>
    var son = document.querySelector('.son');
    son.addEventListener('click',function(){
        alert('son');
    },false);
    var father = document.querySelector('.father');
    father.addEventListener('click',function(){
        alert('father');
    },false);
</script>

在这里插入图片描述

总结

事件发生时会在元素节点之冋按照特定的顺序传播,这个传播过程即DOM事件流

注意

  • Js代码中只能执行捕获或者冒泡其中的一个阶段。
  • onclick和 attachEment只能得到冒泡阶段
  • addeventlistener(type, listener, usecapture])第三个参数如果是 true,表示在事件捕
    获阶段调用事件处理程序; 如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理
  • 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
  • 有些事件是没有冒泡的, 比如 onblur、 on focus、 onmouseerer、 onmouseleave
  • 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件

事件对象

事件对象是?

eventTarget.onclick = function (event)()
eventTarget.addEventListener('click', function(event)()
// 这个 event就是事件对象,我们还喜欢的写成e或者evt

官方解释: event对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钿的状态。

简单理解: 事件发生后,跟 事件相关的一系列信息数据的集合 都放到这个对象里面,这个对象就是 事件对象event, 它有很多属性和方法。

比如:

  1. 谁绑定了这个事件。
  2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
  3. 键盘触发事件的话,会得到腱盘的相关信息,如按了哪个键

使用语法

eventTarget.onclick = function(event) {
    // 这个 event就是事件对象,我们还喜欢的写成e或者evt
}

eventTarget.addEventListener = function('click, function(event){
	// 这个 event就是事件对象,我们还喜欢的写成e或者evt    
}

这个 event是个形参,系统帮我们设定为事件戏象,不需要传递实参过去。

当我们注册事件时, event对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。

【例:】

<div class="box">div标签</div>
<script>
    var div = document.querySelector('.box');
    div.addEventListener('click',function(e) {
        console.dir(e);
        //或者写成(window.event)IE写法
        console.log(window.event);
        //或者写成(兼容IE))
        e = e || window.event;
    });
</script>

在这里插入图片描述

1. event就是一个事件对象写到我们侦听函数的小括号里面当形参来看。
2. 事件对象只有在有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
3. 事件对象是我们事件的一系列相关数据的集合跟事件相关的比如鼠标点击里面就包含了鼠标的相关信 息,鼠标坐标啊,如果是键盘事件里面就包含的键盘事件的信息比如判断用户按下了那个键
5. 这个事件对象我们可以自己命名比如 event、evt、e
6. 事件对象也有兼容性问题 ie678 通过 window, event 兼容性的写法 e=e|| window.event;

常见属性和方法

事件对象的常见属性方法
e.target返回触发 事件的对象 标准
e.srcElement返回触发 事件的对象 非标准 IE 678使用
e.type返回事件的类型比如 click mouseover 不带on
e.cancelBubble该属性阻止冒泡非标准 IE 678使用
e.returnvalue该属性阻止默认事件(默认行为) 非标准IE 678使用比如不让链接跳转
e.preventDefault该方法阻止默认事件(默认行为)标准比如不让链接跳转
e.stopPropagation阻止冒泡 标准

[示例 1.](返回触发事件对象)

// 1. e.target 返回的是触发事件的对象(元素)  this 返回的是绑定事件的对象(元素)

// 区别 : e.target 点击了那个元素,就返回那个元素 this 那个元素绑定了这个点击事件,那么就返回谁

var ul = document.querySelector('ul');
ul.addEventListener('click',function(e) {
    console.log(this);
    console.log(e.target);

    //e.currentTarget 跟this 非常相似的属性 但是IE 678不认识
    console.log(e.currentTarget); 
})

// 了解兼容性
// ul.onclick = function(e) {
//     e = e || window.event;
//     var target = e.target || e.srcElement;
//     console.log(target);

[示例 2.](返回事件类型)

<div>123</div>
var dic = document.querySelector('div');
div.addEventListerence = function('click',fn);
div.addEventListerence = function('mouseover',fn);
div.addEventListerence = function('mouseout',fn);
function fn(e) {
    console.log(e.type);
}

[示例 3.](阻止事件默认行为)

<div>123</div>
    <a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
     <input type="submit" value="提交" name="sub">
</form>
// 2. 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
    e.preventDefault(); //  dom 标准写法
})

// 3. 传统的注册方式
a.onclick = function(e) {
    // 普通浏览器 e.preventDefault();  方法
    // e.preventDefault();
    // 低版本浏览器 ie678  returnValue  属性
    // e.returnValue;
    // 我们可以利用return false 也能阻止默认行为 没有兼容性问题 
    // 特点: return 后面的代码不执行了, 而且只限于传统的注册方式
    return false;
    alert(11);
}

阻止事件冒泡(面试问题⭐)

阻止事件冒泡的两种方式

事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到刭DOM最顶层节点。

事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要们灵活掌握。

阻止方式:

  • 标准写法:利用事件对象里面的 stopPropagation()方法
e.stopPropagation();
  • 非标准写法: IE 678利用事件对象 cancelBubble属性
window.event.cancelBubble= true;  //非标准 cancel取消 bubble 泡泡

[示例 4.](阻止事件冒泡)

// 常见事件对象的属性和方法
// 阻止冒泡  dom 推荐的标准 stopPropagation() 
var son = document.querySelector('.son');
son.addEventListener('click', function(e) {
    alert('son');
    e.stopPropagation(); 	// stop 停止  Propagation 传播
    e.cancelBubble = true; 	// 非标准 cancel 取消 bubble 泡泡
}, false);
// father 没有阻止事件冒泡 ,点击father还是会一级一级向上冒泡
var father = document.querySelector('.father');
father.addEventListener('click', function() {
    alert('father');
}, false);
document.addEventListener('click', function() {
    alert('document');
})

阻止事件冒泡兼容性解决方案【函数封装】

if(e.stopPropagation) {
	e.stopPropagation();
} else {
	window.event.cancelBubble = true;
}

事件委托(代理、委派)

事件委托也称为事件代理, 在 jQuery里面称为事件委派。

事件委托原理

不用给每个子节点单独设置事件监听器, 而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

有如下案例: 给 ul 注册点击事件,然后利用事件对象的 target来找到当前点击的 li ,因为点击 li ,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器。

事件委托作用

我们只操作了一次 DOM,提高了程序的性能。

【加深理解】

生活中有如下场景
咱们班有100个学生,快递员有100个快递,如果个个的送花费时间较长。同时每个学生领取的时候,也需
要排队领取,也花费时间较长,何如?

解决方案: 快递员把100个快递,委托给班主任,班主任把这些快递放到办公室,同学们下课自行领取即可。

优势: 快递员省事,委托给班主任就可以走了。同学们领取也方便,因为相信班主任

【事件冒泡案例】

事件冒泡本身的特性, 会带来的坏处,也会 带来的好处, 需要我们灵活掌握。程序中也有如此场景:

<ul>
    <li>我是1</li>
    <li>我是2</li>
    <li>我是3</li>
    <li>我是4</li>
    <li>我是5</li>
    <li>我是6</li>
</ul>

点击每个都会弹出对话框,以前需要给每个注册事件, 这是非常辛苦的, 而目访问DOM的次数越多,这就

会延长整个页面的交互就绪时间。

 // 利用事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('mousemove',function(e) {
    e.target.style.background = 'pink';
});
ul.addEventListener('mouseout',function(e) {
    e.target.style.background = '';
});

在这里插入图片描述

e.preventDefault 阻止鼠标事件

1.禁止鼠标右键菜单

contextmenu 主要控制应该何时显示上下文菜单,主要用于程序取消默认的上下文菜单

// 阻止鼠标右键
document.addEventListener('contextmenu',function(e) {
    e.preventDefault();
})

2.禁止鼠标选中(selectstart 开始选中)

//阻止鼠标选中
document.addEventListener('selectstart',function(e) {
    e.preventDefault();
})

鼠标事件对象

event 对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用 鼠标事件对象
Mouseevent
键盘事件对象 KeyboardEvent

鼠标事件对象说明
e.clientX返回鼠标相对于浏览器窗口可视区的X坐标
e.clientY返回鼠标相对于浏览器窗口可视区的Y坐标
e.pageX返回鼠标相对于文档页面的X坐标 IE9+支持
e.pageY返回鼠标相对于文档页面的Y坐标 IE9+支持
e.screenX返回鼠标相对于电脑屏幕的X坐标
e.screenY返回鼠标相对于电脑屏幕的Y坐标

【案例:根据鼠标移动天使】

//案例分析
//	①鼠标不断的移动,使用鼠标移动事件: mousemove
//	②在页面中移动,给 document册事件
//	③图片要移动距离,而且不占位置,我们使用绝对定位即可
//④核心原理:每次鼠标移动,我们都会获得最新的鼠标坐标,把这个X和y坐标做为图片的top和let值就可以移动图片

<img src="img/angel.gif" alt="" style = 'position:absolute'>
     var pic = document.querySelector('img');
document.addEventListener('mousemove',function(e) {
    var x = e.pageX;
    var y = e.pageY;
    pic.style.left = x - 30 + 'px';
    pic.style.top = y  - 40 + 'px'; 
})

常用键盘事件

事件除了使用鼠标触发,还可以使用键盘触发。

键盘事件触发条件
onkeyup某个键盘按键被松开时触发
onkeydown某个键盘按键被按下时触发
onkeypress某个键盘按键被按下时触发但是 它不识别功能键比如 ctrl shift箭头等

【 注:】

  • 如果使用 add EventListener不需要加on
  • onkeypress 和前面2个的区别是,它不识别功能键,比如 左右箭头,shift 等
  • 三个事件的执行顺序是: keydown —> keypress —> keyup

键盘事件对象

键盘事件对象属性说明
keyCode返回该键的ASCII值

【注:】

  • onkedown 和 onkeyup 不区分字母大小写(按下a 或者按下A),onkeypress 区分字母大小写
  • 在我们实际开发中,我们更多的使用 onkeydown 和 onkeyup 它能识别所有的键(包括功能键)
  • keypress 不识别功能键,但是keyCode 属性可以区分大小写,返回不同的ASCII值
// 用户按下快捷键,输入框自动获得焦点
<input type="text">
    var ipt = document.querySelector('input');
document.addEventListener('keyup',function(e) {
    if(e.keyCode === 83) {
        ipt.focus();
    }
})
// 案例二: 输入框输入,上方显示大号字
<div class="search">
    <div class="con"></div>
	<input type="text" placeholder="请输入单号">
</div>
var con = document.querySelector('.con');
var text = document.querySelector('input');
//输入框输入隐藏内容显示并把输入框内容赋值给隐藏的盒子
text.addEventListener('keyup',function() {
    if(this.value == ''){
        con.style.display = 'none';
    } else {
        con.style.display = 'block';
        con.innerHTML = this.value;
    }
});
//失去焦点就隐藏盒子
text.addEventListener('blur',function() {
    con.style.display = 'none';
})

//获得焦点就显示盒子
text.addEventListener('focus',function() {
    if(this.value != '') {
        con.style.display = 'block';
    }
})

JS阶段三(BOM)

概述

什么是BOM

  • BOM( Browser Object Model)即 浏览器对象模型, 它提供了独立于内容而 与浏览器窗口进行交互的对象, 其核心对象是 window。
  • BOM由一系列相关的对象构成,并目每个对象都提供了很多方法与属性。
  • BOM缺乏标准, JavaScript语法的标准化组织是ECMA,DOM的标准化组织是WC,BOM最初是 Netscape浏
    览器标准的部分。

DOM

  • 文档对象模型
  • DOM就是把「文档」当做一个「对象」来看待
  • DOM的顶级对象是 document
  • DOM主要学习的是操作页面素
  • DOM是W3C标准规范

BOM

  • BOM的顶级对象是 window
  • BOM学习的是浏览器窗口交互的一些对象
  • BOM学习的是浏览器窗口交互的一些对象
  • BOM是浏览器厂商在各自浏览器上定义的,兼容性较差

BOM 的构成

BOM 比 DOM 更大,它包含DOM

在这里插入图片描述

Window 对象是浏览器的顶级对象 ,它具有双重角色

  • 它是 JS 访问浏览器窗口的一个接口
  • 它是一个全局对象,定义在全局作用域的变量、函数都会变成 window 对象的属性和方法
  • 在调用的时候可以省略 window,前面学习的对话框属于 window 对象的方法,如 alert()、prompt()等

Window 下有个特殊的属性 window.name

window 对象事件

窗口加载事件1

window,onload = function(){}
// 或者
window.addEventListener('load',function(){});

window. onload是窗口 (页面) 加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS
文件等就调用的处理函数

【注意:】

  • 有了 window.onload 就可以把 JS代码写到页面元素的上方,因为 onload是等贞面内容全部加载完毕,
    再去执行处理函数。
  • window.onload 传统注册事件方式只能写一次, 如果有多个,会以最后一个 window. onload 为准。
  • 如果使用 addEventlistener则没有限制

窗口加载事件2

document.addEventlistener('DOMContentLoaded',function(){})
  • DOMContentloaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。IE9以上才支持
  • 如果页面的图片很多的话从用户访问到 onload触发可能需要较长的时间交互效果就不能实现,必然影响用
    户的体验,此时用 DOMContentloaded事件比较合适。

【这两种方式区别:】1. onload 是页面文档(图像、css等)加载完毕后才会执行JS代码,如果页面图片太多,就会造成加载时间长,js加载期间无法使用。 2.而(DOMContentLoaded)是在DO(不包含 css、图片、flash插件等)加载完就可以执行,不过需要 IE9 以上支持

调整窗口大小事件

window.onresize = function(){}
window.addEventListerener('resize',function(){});

window.onresize 是调整窗口大小的加载事件,当触发是就调用处理函数

【注意:】

  • 只要窗口发生像素变化,就会触发这个事件
  • 我们可以可以这个事件完成响应式布局,window.innerWidth 监测当前屏幕的宽度
// 当屏幕像素小于等于500px时隐藏盒子
<div class="box"></div>
var div = document.querySelector('.box');
window.addEventListener('resize',function() {
    if(this.innerWidth <= 500) {
        div.style.display = 'none';
    } else {
        div.style.display = 'block';
    }
})

在这里插入图片描述

定时器

setTimout()定时器

window.setTimeout(调用函数,[延迟毫秒数]);

setTimout() 方法用于设置一个定时器 ,这个调用函数我们也称为 回调函数 callback

普通函数是按照代码顺序直接调用,而这个函数,需要等待时间,时间到了才去调用这个函数,我们称为 回调函数

简单理解:回调,就是回头调用的意思,上一件事情做完,在回头调用这个函数

element.onclick = function(){}
// 或者
element.addEventListence('click',function(){})  //里面的参数也是回调函数

【注意:】

  • window可以省略
  • 这个调用函数可以直接写函数,或者写函数名,或者写字符串 ‘函数名()’ 三种方法,第三种不推荐
  • 延迟的毫秒数可以省略,省略默认是 0 .如果写,必须是毫秒数
  • 因为定时器可能有很多,我们经常给定时器赋值一个标识符

setInterval()定时器

window.setInterval(回调函数,[间隔的毫秒数]);

setInterval() 方法重复调用一个函数,每隔一段时间(自己设置的),就去调用一次回调函数

【注意:】

  • window可以省略
  • 这个调用函数可以直接写函数,或者写函数名,或者写字符串 ‘函数名()’ 三种方法,第三种不推荐
  • 延迟的毫秒数可以省略,省略默认是 0 .如果写,必须是毫秒数
  • 因为定时器可能有很多,我们经常给定时器赋值一个标识符

停止定时器

window.clearTimeout(timeoutID);  //里面传入设置定时器的标识符

clearTimeout() 取消了先前通过调用 setTimeout 建立的定时器

【注意:】里面的参数就是定时器的标识符

【案例】(发送验证码)

// 每隔一分钟才能发送一次验证码
<input type="text" placeholder="短信验证码">
<button>获取验证码</button>

var btn = document.querySelector('button');
var count = 60;
btn.addEventListener('click',function() {
    this.disabled = true;
    this.style.backgroundColor = '#ccc';
    this.style.cursor = 'not-allowed';
    var clear = setInterval(function(){
        if(count !== 0) {
            btn.innerHTML = '重新发送('+count+')';
            count--;
        } else {
            clearInterval(clear);
            btn.innerHTML = '重新发送';
            count = 3;
            btn.disabled = false;
            btn.style.backgroundColor = 'orange';
            btn.style.cursor = 'pointer';
        }
    },1000);
})

JS执行机制

JS是单线程

Javascript 语言的一大特点 就是 单线程, 也就是说, 同一个时间只能做一件事。 这是因为 Javascript这门

脚本语言诞生的使命所致 —> JavaScript是为处理页面中用户的交互,以及操作DOM而诞生的。

比如我们对 某个DOM元素进行添加和删除操作,不能同时进行。应该先讲行添加,之后再删除

单线程就意味着, 所有任务需要排队,前一个任务结束, 才会执行后一个任务。这样所导致的问题是:如果

JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致面渲染加载阻塞的感觉。

一个问题:

// 一下代码执行结果是什么
console.log(1);
setTimeout(function() {
	console.log(3);
});
console.log(2);

同步和异步

为了解决 JS 单线程 可能造成的 页面渲染可能阻塞的问题,利用多核 CPU 核算能力,HTML5 提出 WEB Worker标准,允许 JavaScript 脚本创建多个线程,于是 ,JS出现了同步和异步

同步

前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做
法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

异步

你在做—件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做
饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜

他们的本质区别:这条流水线上各个流程的执行顺序不同

同步任务

同步任务都在主线程上执行,形成一个 执行栈

异步任务

JS 异步是通过回调函数实现的。一般而言,异步任务有以下三种类型

  • 普通事件,如 click、resize 等
  • 资源加载,如 load、error 等
  • 定时器,包括 setTimeout、setInterval 等

异步任务相关 回调函数 添加到 任务队列中 (任务队列也称为消息队列)

执行过程

  1. 先执行 执行栈中的同步任务
  2. 异步任务,(回调函数放入任务队列中)
  3. 、一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取 任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈中 ,开始执行,主线程会不断的重复去任务队列中获得任务,执行任务….

在这里插入图片描述

一张图解释执行过程

在这里插入图片描述

由于主线程不断重复地获取任务,执行任务,再获取任务,在执行,我们把这种机制称为【事件循环(eventloop)】

location对象

window 对象给我们提供了一个 location属性用于获取或设置窗体的 URL,并且可以用于 解析URL 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location对象

URL

统一资源定位符( Uniform resource locator,URL) 是互联网上标准资源的地址。互联网上的每个文件都有
一个唯的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

URL语法为:

protocol://host [:port]/path/[?query]#fragment
http://www.itcast.cn/index.html?name=andysage=18#link
组成说明
protocol通信协议常用的http, ftp,maito等
host主机(域名)www.xxx.com
port端口号可选,省略时使用方案的默认端口如 http 的默认端口为80
path路径由零或多个/符号隔开的字符串,一般用来表示主机上的一个目录或文件地址
query参数以键值对的形式通过&符号分隔开来
fragment片段 # 后面内容常见于链接锚点

location 对象属性

location对象属性返回值
location.href获取或者设置整个URL
location.host返回主机(域名)www.xxx.com
location.port返回端囗号如果未写返回空字符串
location.pathname返回路径
location.search返回参数
location.hash返回片段#后面内容常见于链接锚点

页面间传递参数

location对象方法

location对象方法返回值
location. assign()跟href一样,可以跳转页面( 也称为重定向页面)
location. replace()替换当前页面,因为不记录历史,所以不能后退页面
location. reload()重新加载页面,相当于刷新按钮或者f5如果参数为true强制刷新ctr+f5

navigater 对象

navigator 对象包含有关浏览器的信息,它 有很多属性,我们最常用的是 userAgent, 该属性可以返回由客
户机发送服务器的user- agent头部的值。

下面前端代码可以判断用户那个终端打开页面,实现跳转

if((navigator.userAgent.match(/(phone | pad | pod | iPhone | iPod | ios| iPad | Android | Mobile | BlackBerry | IEMobile | MoRBrowser | JUC | Fennec | WOSBrowser | BrowserNG | WeboS | Symbian | windows Phone)/i))) {
    window.location. href = ""; //手机
} else {
    window.location.href = "";  //电脑
}

history对象

window 对象给我们提供了一个 history对象, 与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)
访问过的URL。

history对象方法作用
back()可以后退功能
forward()前进功能
go(参数)前进后退功能参数如果是1前进1个页面如果是1后退1个页面

元素偏移量 offset

offset概述

offset翻译过来就是偏移量, 我们使用 offset 系列相关属性可以 动态的得到该元素的位置〔偏移)、大小等

  • 获得元素距离带有定位父元素的位置
  • 获得元素自身的大小(宽度高度)
  • 注意:返回的数值都不带单位

offset 常用属性

offset系列属性作用
element. offsetParent返回作为该元素带有定位的父级元素如果父级都没有定位则返回body
element. offsetTop返回元素相对带有定位父元素上方的偏移
element offsetLeft返回元素相对带有定位父元素左边框的偏移
element offsetwidth返回自身包括 padding、边框、内容区的宽度,返回数值不带单位
element offsetHeight返回自身包括 padding边框、内容区的高度,返回数值不带单位

offset 与 style区别

offset

  • offset可以得到任意样式表中的样式值
  • offset系列获得的数值是没有单位的
  • offset.Width包含 padding+ border+ width
  • offsetWidth 等属性是只读属性,只能获取不能赋值
  • 所以,我们想要获取元素大小位置,用 offset更合适

style

  • stye只能得到行内样式表中的样式值
  • style. width获得的是带有单位的字符串
  • style. width获得不包含 padding和 border的值
  • style. width是可读写属性,可以获取也可以赋值
  • 所以,我们想要给元素更改值,则需要用 style改变

案例

模态拖拽框

①我们在盒子内点击,想要得到鼠标距离盒子左右的距离

②首先得到鼠标在页面中的坐标( e pagex e pageY) 其次得到盒子在页面中的距离( box. offsetLeft, box. offsetTop)

④用鼠标距离页面的坐标减去盒子在页面中的距离,得到鼠标在盒子内的坐标

⑤如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动事件 mousemove

// 1.点击弹出层,会弹出模态框,并且显示灰色半透明的遮挡层。
// 2.点击关闭按钮,可以关模态框,并且同时关闭灰色半透明遮挡层。
// 3.鼠标放到模态框最上面行,可以按住鼠标拖拽模态框在页面中移动。
// 4.鼠标松开,可以停止拖动模态框移动。

//打开窗口
link.addEventListener('click',function() {
    login.style.display = 'block';
    bg.style.display = 'block';
});
//关闭窗口
closeBtn.addEventListener('click',function() {
    login.style.display = 'none';
    bg.style.display = 'none';
});
//鼠标按下,获取鼠标在盒子内的坐标
title.addEventListener('mousedown',function(e) {
    var x = e.pageX - login.offsetLeft;
    var y = e.pageY - login.offsetTop;
    //获得盒子在页面内的坐标
    document.addEventListener('mousemove',move);
    function move(e) {
        login.style.left = e.pageX - x +'px';
        login.style.top = e.pageY - y +'px';
    };
    //鼠标松开就停止拖动
    document.addEventListener('mouseup',function() {
        document.removeEventListener('mousemove',move);
    });
});

放大镜

//	①点击弹出层,模态框和遮挡层就会显示出来 display: block;
//	②点击关闭按钮,模态框和遮挡层就会隐藏起来 display: none;
//	③在页面中拖拽的原理:鼠标按下并且移动,之后松开鼠标
//	④触发事件是鼠标按下 mousedown,鼠标移动 mousemove鼠标松开 mouseup
//	⑤拖拽过程:鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框可以跟着鼠标走了
//	⑥鼠标按下触发的事件源是最上面一行,就是id为ttle
//	⑦鼠标的坐标减去鼠标在盒子内的坐标,才是模态框真正的位置。
//	8鼠标按下,我们要得到鼠标在盒子的坐标。
//	⑨鼠标移动,就让模态框的坐标设置为:鼠标坐标减去盒子坐标即可,注意移动事件写到按下事件里面。
//	⑩鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除

// 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
preview_img.addEventListener('mouseover', function() {
    mask.style.display = 'block';
    big.style.display = 'block';
})
preview_img.addEventListener('mouseout', function() {
    mask.style.display = 'none';
    big.style.display = 'none';
})
// 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
preview_img.addEventListener('mousemove', function(e) {
    // (1). 先计算出鼠标在盒子内的坐标
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
    // console.log(x, y);
    // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
    // (3) 我们mask 移动的距离
    var maskX = x - mask.offsetWidth / 2;
    var maskY = y - mask.offsetHeight / 2;
    // (4) 如果x 坐标小于了0 就让他停在0 的位置
    // 遮挡层的最大移动距离
    var maskMax = preview_img.offsetWidth - mask.offsetWidth;
    if (maskX <= 0) {
        maskX = 0;
    } else if (maskX >= maskMax) {
        maskX = maskMax;
    }
    if (maskY <= 0) {
        maskY = 0;
    } else if (maskY >= maskMax) {
        maskY = maskMax;
    }
    mask.style.left = maskX + 'px';
    mask.style.top = maskY + 'px';
    // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
    // 大图
    var bigIMg = document.querySelector('.bigImg');
    // 大图片最大移动距离
    var bigMax = bigIMg.offsetWidth - big.offsetWidth;
    // 大图片的移动距离 X Y
    var bigX = maskX * bigMax / maskMax;
    var bigY = maskY * bigMax / maskMax;
    bigIMg.style.left = -bigX + 'px';
    bigIMg.style.top = -bigY + 'px';

元素可视区 client

click :翻译过来就是客户端,我们使用 client系列的相关属性来获取元素可视区的相关信息。通过 client系列

的相关属性可以动态的得到亥元素的边框大小、元素大小等。

client系列属性作用
element. clientTop返回元素上边框的大小
element. clientLeft返回元素左边框的大小
element. clientWidth返回自身包括 padding、内容区的宽度,不含边框,返回数值不带单位
element. clientHeight返回自身包括 padding、内容区的高度,不含边框,返回数值不带单位

click 和 元素偏移量不同的只有一点:offsetWidth、offsetHeight 返会自身+padding +内容区宽高+边框。而clientWidth、clientHeight 返回不包含边框

元素滚动 scroll

scroll 翻译过来就是滚动的,我们使用scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等

scroll 系列属性作用
element.scrollTop返回被卷去的上侧距离,返回数值不带单位
element .scrollLeft返回被卷去的左侧距离,返回数值不带单位
element. scrollWidth返回自身实际的宽度,不含边框,返回数值不带单位
element. scrollHeight返回自身实际的高度,不含边框,返回数值不带单位

scorll 滚动事件:当我们滚动滚动条发生变化的事件

scroll 兼容性解决方案

需要注意的是,页面被卷去的头部, 有兼容性问题,因此被卷去的头部通常有如下几种写法:

  1. 声明了DTD(),使用 document. documentElement.scrollTop
  2. 未声明DTD, 使用 document. body.scrollTop
  3. 新方法 window. pageY.offset 和 window. pageX.offset, IE9开始支持
// 兼容写法函数
function getscroll() {
    return {
        left: window. pageX.offset || document.documentElement.scrollLeft || 						document. body. scrollLeft || 0,
		top: window. pageY.offset || document.documentElement.scrollTop || 
        		document.body. scrollTop || 0
    };
}

// 使用的时候 getscro11().Left

三大系列总结

三大系列作用作用
element.offsetWidth返回自身包括padding、边框、内容区宽度、返回数值不带单位
element.clientWidth返回自身包括padding、内容区宽度、不含边框、返回数值不带单位
element.scrollWidth返回自身实际的宽度、不含边框、返回数值不带单位

总结 他们的主要用法

  1. offset 系列 经常用于获得 元素位置 offsetLeft、offsetTop
  2. client 经常用于获取元素大小 clientWidth、clientHeight
  3. scroll 经常用于获取滚动距离 scrollTop、scrollLeft

【注意】 页面滚动的距离通过 window.pageXoffset 获得

本地存储

随着互联网的快速发展,基于网页的用于越来越普遍,同时也变得越来越复杂,为了满足各种各样的需求,会经常性的在本地存储大量的数据,HTML5提出了相关解决方案。

本地存储特性

  • 数据存储在用户浏览器中
  • 设置、读取非常方便、甚至页面刷新也不会丢失数据
  • 容量较大,sessionStorage约为5M,localStorage约为20M
  • 只能存储字符串,可以将JSON.stringify()编码后存储

sessionStorage

  • 生命周期为关闭浏览器窗口

  • 在一个窗口(页面)下数据可以共享

  • 以键值对的形式存储使用

存储数据

sessionStorage.setItem(key,value);

获取数据

sessionStorage.getItem(key);

删除数据

sessionStorage.removeItem(key);

删除所有数据

sessionStorage.clear();

演示:

 <form action="">
        <input type="text" name="" id="ipt">
        <input type="button" value="存储数据" class="set">
        <input type="button" value="获取数据" class="get">
        <input type="button" value="删除数据" class="remove">
        <input type="button" value="删除所有数据" class="removeall">
 <//form>

 <script>
        var ipt = document.getElementById('ipt');
        var setBtn = document.querySelector('.set');
        var getBtn = document.querySelector('.get');
        var removeBtn = document.querySelector('.remove');
        var removeallBtn = document.querySelector('.removeall');
        //存储数据
        setBtn.addEventListener('click',function() {
            var val = ipt.value;
            sessionStorage.setItem('uname',val);    //存储一个数据

            sessionStorage.setItem('pwd',val);      //存储多个数据

        });
        //获取数据
        getBtn.addEventListener('click',function() {
            alert(sessionStorage.getItem('uname'));
        });
        //删除数据
        removeBtn.addEventListener('click',function() {
            sessionStorage.removeItem('uname');
        });

        //删除多个数据
        removeallBtn.addEventListener('click',function() {
            sessionStorage.clear();
        });
</script>

localStorage

  • 生命周期永久有效,除非手动删除,否则关闭页面也会存在
  • 可以多窗口(页面)共享(同一浏览器可以共享)
  • 以键值对的形式存储使用

存储数据

localStorage.setItem(key,value);

获取数据

localStorage.getItem(key);

删除数据

localStorage.removeItem(key);

删除所有数据

localStorage.clear();

案例:记住用户名

//存在一个小bug,就是当我们先点击记住密码功能,再输入,就不会真正的记住密码
<form action="">
    <input type="text" name="" id="uesrname">
    <input type="checkbox" name="remember" id="btn">记住用户名
</form>

<script>
    var user = document.getElementById('uesrname');
    var remember = document.querySelector('#btn');
    if(localStorage.getItem('uesr')) {
        user.value = localStorage.getItem('uesr');
        remember.checked = true;
    }
    remember.addEventListener('click',function() {
        if(this.checked) {
            localStorage.setItem('uesr',user.value);
        } else {
            localStorage.removeItem('uesr');
        }
    })
</script>

  1. a-z0-9_- ↩︎

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页