🚀【JavaScript快速入门】从零开始,小白也能变大神!👩💻👨💻
🌟 前言
亲爱的朋友们,你是否对网页背后的魔法🧙♂️充满好奇?是否想要掌握JavaScript这门强大的编程语言?别担心,今天我们就从零开始,一起探索JavaScript的奥秘!🔍
📚 第1步:环境搭建
首先,我们需要一个文本编辑器和一个浏览器。推荐使用VSCode作为编辑器🖥️,然后用Chrome浏览器来查看我们的代码效果。🌐
🌈 第2步:Hello World!
让我们写下我们的第一行JavaScript代码吧!👇
console.log("Hello, World!");
在浏览器的控制台(按F12打开开发者工具,然后点击Console标签)中,你会看到输出的内容。👀
🚀 第3步:学习JavaScript
接下来,我们要学习JavaScript大部分常用知识。
JavaScript基础
原型&原型链
原型链机制:
要查找一个对象的属性或者方法,JavaScript会现先从当前对象的自身的属性开始找,若没有找到,就找当前对象的__proto__属性,若没有,就顺着__proto__指向位置一层一层往上查找。这种链式的结构就是原型链机制。
Object和object的区别:
首先Object是一个构造函数,是JavaScript内置的构造函数。作用就是用来创建对象的。
object就是一种普通的对象类型。
获取对象原型的方法:(ES5)
Object.getPrototypeOf()
还可检查是不是自身的属性,而不是原型链上的。
const person={
name:'小兰花',
age:18
}
console.log(person.hasOwnProperty('name'));true
Object是所有对象的原型,也是一个构造函数,所有用户创建的对象都是来自于Object。Object用于创建新的对象。
// 父类
class People{
constructor(name,age){
this.name=name;
this.age=age;
}
hai(){
console.log(`我叫${this.name},我今年${this.age}岁了。`)
}
}
// 继承于父类的子类
class Student extends People{
constructor(name,number){
super(name);// 使用父类属性
this.number=number;
}
xuehao(){
console.log(`我的学号是${this.number}。`)
}
}
const Tom=new Student('小兰花','2022412683');
console.log(Tom.name;)
Tom.hai()
Tom.xuehao()
注意:
- 类在被创造时候,会自动生成prototype属性,这个属性是一个对象,此类的方法会存放在prototype里面,但是属性会存放于实例对象里面。因为使用constructor来声明属性的时候是用的this.name。这里的this会把name存放到实例对象里面,而不是放在class里面。
- 这个Tom实例化对象会自动生成一个自带的__proto__属性,这个属性是一个对象,就像this一样会指向其实例化自身的类的prototype。实例化对象的属性会存放在这个对象自身
原型:
原型链
因为Student又是People类继承而来的,所以Student类的prototype属性里面的__proto__又会指向People类里面的prototype属性。
所有原型的最终原型。
People类是第一个创建的类,我没有再创建它的父类。
intanceof:可以用这个方法来判断是不是属于Object类型。
例如:
console.log(Tom intanceof Object)// 输出true。
// intanceof会从Tom的__proto__开始向上找,直到能够找到Object.prototype.__proto__为止。
// 若能找到就是Object类型返回true,否则返回false。
JavaScript数据类型以及判断数据类型的方法
数据类型
:简单数据类型和引用数据类型
简单数据类型:String、Number、Boolean、Undefined、Null、Symbol、BigInt
这里只简单介绍
Symbol:一种唯一、不可变的数据类型。
BigInt:大于2^53-1的整数,注意:BigInt类型的数据类型要特殊标识。
const num=BigInt(这里放一个超大的数)
let k = 1234567890123456789012345678901234567890n;
JavaScript里面,基本数据类型存放在栈中,而且基本数据类型的变量的值也会存放在栈中存放变量的位置。是存放在一起的。
栈:
先进后出,但基本数据类型被当作全局属性和局部属性的时候,被释放的时候是不一样的。
局部属性在栈中的释放是和函数的生命周期挂钩的,函数执行结束后,栈中的内存就会被自动释放。
全局属性在栈中释放时是和整个程序的生命周期挂钩的,只有程序执行完后才会在栈中清除内存。
基本数据类型存放在栈的优点:基本数据类型和基本数据类型的值会被存放在同一块栈的内存中。这个方式使得访问基本数据类型的值的时候性能十分高效,因为不再需要引用来找类型对应的值了。
堆:
是一个优先队列,可以按照优先级来排序,比如按大小来排列。
引用数据类型是存放在堆中的,而引用数据类型里面的变量的值是存放在栈中的,变量本身也存放在栈中。
数据类型检查
typeof----------每种数据类型都有一个类型标识,typeof就是通过这个标识来判断数据类型的。但是有一个历史遗留问题,typeof会把null类型判断成object类型。
instanceof---------工具根据原型链来向上查找,比如对象会找到最上面的Object
constructor--------这是JavaScript里面对象的一个内置的属性,这个属性是一个对象。并且每个实例对象的constructor属性对象都会指向其实例对象的构造函数。
const person=new Test('小兰花',18);
console.log(person.constructor===Test)// 输出是true
es6里面还有个obj.isArray()方法来判断是不是数组
包装类型
JavaScript里面存在一个可以将基本类型转换成对象的构造函数。这个构造函数就叫作包装类型。
JavaScript里面有一个机制:
虽然基本数据类型没有属性和方法,但是JavaScript为了方便管理,JavaScript会在后台悄悄的将基本数据类型的值转变成对象类型的值。
当然,悄悄地转换成对象类型后,也可以使用下面讲的valueof()方法,来获取悄悄转换成的对象类型的原始值,也就是转换之前的值。这里也可以顺便理解一下,声明叫原始值。
装箱:
装箱是一个过程:将基本数据类型自动转换成对象的过程。
拆箱:
将对象转换成基本数据类型。
JavaScript中的隐式类型转换
首先要介绍一个方法:ToPrimitive方法。这个方法是每个值隐式自带的方法。
什么叫隐式自带的方法?基本数据类型为什么会有方法?
接下来一句话一起解决。
此方法是JavaScript内部的方法,开发这不能直接调用和访问它,用于特定情况:需要将基本类型或引用类型转换成基本类型。
若+的其中一个操作数是字符串那么,就会执行字符串的拼接操作。
toString()方法
首先这个也是一个JavaScript里面对象的内置方法。功能就是将对象转换成字符串。
注意:对基本数据使用toString()方法和对引用数据类型使用toString()方法所返回的值不同。
对基本数据类型使用toString()方法:基本类型的值是多少就原封不动的转换成字符串即可。
对引用类型使用toString()方法:
数组:将整个数组的所有值当成一个字符串
const arr=[1,2,3];
console.log(arr.toString());// 返回"1,2,3"
函数:将整个函数的代码当作一个字符串返回
对象:默认都会返回一个默认值
consol.log(obj.toString())// 输出"[object Object]"
注意:对象使用的toString()方法可以自定义修改。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
return `Person(name: ${this.name}, age: ${this.age})`;
}
}
let person = new Person('kimi', 18);
console.log(person.toString()); // "Person(name: kimi, age: 18)"
valueof()方法
内置方法,用于返回原始值,即非对象的数据类型。
基本数据类型返回基本数据类型。
引用类型返回引用类型本身(以字符串的形式)
对象的valueof()可以自定义
判断对象是否为空
// 方法一
console.log(JSON.stringfy(obj)=={})// 实现的原理是,先将obj转换成JSON格式,因为空对象的JSON字符串
// 则值是{}, 利用此特性就可以实现判断字符串。
// 方法二
Object.keys(obj).length<0;// 原理是:此方法会返回一个有给定的对象的可枚举属性构成的数组,
// 所以,判断该返回数组的长度是不是0,若长度是0,那么就是没有内容。
算数运算符
在 JavaScript 中,算术运算符共有 6 种:加 +、减 -、乘 *、除 /、取余 %、求幂 **
加法运算符的功能,若两个相加的对象,有一个是算术运算符,那么无论另一个是什么,都会被当初算术运算符,然后两个拼接。
在JavaScript中也有算数运算符优先级。和显示中的一致。
=和==和===的区别以及Object.is()方法
=是赋值
==是判断数值相等。
判断两个类型的值,若两个类型不一致,则会进行强制置换,再比较值是否相等,相等就返回true
===是判断数值相等的同时类型也相等。-----------两种类型(值类型和引用类型)
判断两个类型的值,若两个类型不一致,则会直接返回false
Object.is()方法:JavaScript里面Object对象的内置方法。功能强大,可以判断一些特殊值。返回值一般和===的返回值一致。
Object.is(NaN,NaN);// 返回true
逻辑运算符
与:&&
或:||
非:!
左移运算符:<< 对二进制来说,左移一位就是乘以2,
右移运算符; >>右移一位就是除以2。
原码:一个二进制数。
反码:按位取反
补码:正数的补码不变,负数的补码就是符号位不变,其他按位取反,再加1。
条件运算符或者叫做三目运算符
条件表达式?表达式1:表达式2
age>=18?成年:未成年-----------------------若age的值大于18就是成年返回true,若没有18就是未成年,返回false。
javascript也有自增自减运算符
++
--
if语句和switch语句
for循环和while循环
do while循环是先执行一次循环再判断是否成立条件。
Object.assign和扩展运算符
Object.assign()是ES6里面的JavaScript内置方法,用于一个或者多个对象的可枚举属性浅拷贝到目标对象。
Object.assign(obj1,obj2)// obj1是目标对象,obj2是被拷贝的对象。
Object.asssign()方法会触发JavaScript里面的setter函数,
setter函数
setter函数可以理解成axios里面的请求/响应拦截器也有点类似于Proxy代理对象。可以定义对属性的赋值行为。
生效时自动的:当尝试给一个对象的属性赋值的时候,如果这个属性恰好设置的有关于setter的函数,那么此setter函数会自动生效。
扩展运算符
特点:
- 浅拷贝
- 不会修改原数组,会返回一个新数组
- 使用扩展运算符浅拷贝的时候,若遇到相同的属性,后面的会覆盖前面的。
let obj1={...obj2}
注意:当使用扩展运算符来进行数组的赋值。扩展运算符要放在最后一位。不然会报错
const [first,...content,last]=[1,2,3,4,5,6]// 报错
// 原因:扩展运算符会把first赋值后的元素都放在content里面,导致last没有可用的元素,导致报错。
还有种用法:扩展运算符用于形参上。
若一个函数要传入很多参数,可以使用扩展运算符将传入的众多参数合成一个数组。这样就可以不用写多个形参了。好方便啊。
function sum(...num){
let number=0;
for(let item of num){
number+=item;
}
console.log(`1 2 3 4 5 6的和是${number}`);
}
sum(1,2,3,4,5,6);
这里算出来是21。en ,就是这么用的,很方便。
函数定义有两种方式
函数声明:
function 函数名(参数) {
//、、、
return 返回值
}
函数声明就是经典的函数命名格式,用于独立的函数使用很好用
函数表达式:
当遇到需要回调的时候,函数表达式更合适。
若函数表达式里面function后面又名字就是有名字的函数表达式,没有名字就是匿名函数表达式。
对象是属性的集合,当然函数也可以当作属性。所以对量里的函数叫方法。
创建对象可以使用构造函数。
// 1.自定义构造函数
function Student(num,name,age){
this.num=num
this.name=name
this.age=age
// 定义方法
this.study=function (){
console.log("我已经"+this.age+"了。")
}
}
//创建对象
const stu1=new Student(1,"小米",18)
// 调用方法
stu1.study()
string对象
单引号,双引号都可以定义字符串,但不可混用。
act()方法----------根据下标来访问字符。
str.at(index)
从左到右第二个:act(3)
要返回的字符串字符的索引。如果为负数,则从字符串末尾向前倒数,最后一个字符的索引为 -1。
倒数第二个:act(-2)
charAct()方法------不可以使用负值
str.at(index)
上面两个方法都只是来找字符串中的单个字符,
slice()方法是用来找一个字符串中的一段字符串作为新字符串返回。
用法:str.slice(start,end)-----------satrt:开始的位置,end:结束的位置(选取的字符不包括结束的位置的字符。)。
还有一个截取字符串的方法。
str.substring(start,end)
上面是截取字符串的方法,有一个拼接字符串的方法。---------concat()
用法:str.concat(str1,str2,str3..........)--------------str字符串拼接str1,str2,str3等等。
分割字符串的方法:split()
split() 方法可以使用指定的分隔符将一个字符串分割成子字符串数组。
str.split(指定的分隔符,要分割的个数)
const str = 'Hello,Java,Script'console.log(str.split(',')) console.log(str.split(',', 2))
转大写,小写的方法。
str.ToLowerCase()
str.ToUpperCase()
还有个奇怪的方法:删除字符串两端的字符。
str.trim()------删除两端的字符串
str.trimStart()-----------删除开头字符串的空格
str.trimEnd()---------删除末尾的空格。
还有个更奇怪的方法:填充字符使其达到要求个数的字符串
str.padStart(要求的长度,要填充的字符串)--------填在开头
str.padEnd(要求的长度,要填充的字符串)---------填在末尾
多次返回字符串
str.repeat(要返回的次数)
替换一个字符串的一部分方法
str.replace(被替换的字符串,替换的字符串)--------只能替换第一个匹配到的字符串。
str.replaceAll(被替换的字符串,替换的字符串)
Array对象
数组有个length属性
Array.length会返回数组的元素数量。
寻找数组 的某个元素:
arr.act(下标)
arr.alice(start,end)---------和string一个用法。
数组也有concat()方法,和string一个用法。
join()------------------将数组的单个元素拼接成一个字符串方法
新循环方式---自己体会
for of 语句
循环遍历数组的所有值,每次以赋值给item,这样写十分的简单。
const str=["Brian","meg","peter"]
for(let item of str){
console.log("hello "+item)
}
还有另一个新循环---arr.forEach(callbackFn, thisArg)
callbackFn是对数组中的每个元素执行的回调函数,它可以接收三个参数:
-
element:当前元素的值index:当前元素的下标array:被该方法操作的数组
thisArg是执行回调函数时用作this的对象,它是一个可选参数
arr.pop()----------删除数组的最后一个元素。
arr.shift()----------删除数组的首元素
arr.push(要添加的元素)---------添加元素要数组的末尾
arr.unshift(要添加的元素)----------在数组的首部添加新元素
在JavaScript里面有一个内置的对象是Math,其中有许多的数学常数和数学函数。
还有一个Data对象。用于处理 日期和时间的。
正则表达式
---------用来验证字符串的模板,可用于手机验证、短信验证
创建正则表达式有两种:
常用的是const re = /abc/,斜杠式的
难点:
function strRender(str, data) {
const re = /${(\w+)}/g;
return str.replace(re, (match, key) => data[key]);
}
// 注意点:当replace()方法中使用了正则表达式re,那么替换函数会接受到多个参数(背后发生的)。
// 替换函数会把正则表达式匹配到的子字符串返回给match参数,并且会把子字符串里的捕获内容给key参数,
// (match, key) => data[key],data[key]会返回从data对象里面的符合匹配的键值的属性值。
还有一种是,调用正则表达式对象 RegExp 的构造函数-----当然这个函数只能用于匹配字符串
首先来介绍正则表达式中的方括号组,它可以匹配方括号中的任意一个字符。如果在两个字符间插入一个连字符,则指定了一个字符范围。如果在方括号内的开头添加一个 ^ 符号,则反转条件,匹配不是方括号中的字符。
下面是一些例子:
| 表达式 | 描述 |
|---|---|
[abc] | 匹配 [abc]之中的任意一个字符 |
[a-c] | 同上 |
[^abc] | 匹配 [abc]以外的任意一个字符 |
[A-Z] | 匹配任意大写字母 |
[a-z] | 匹配任意小写字母 |
[0-9] | 匹配任意数字 |
斜杠和一些字母组合起来具有特殊的含义,单独的 . 符号也同理,它们称为元字符,如下所示:
| 表达式 | 描述 |
|---|---|
. | 匹配除换行符之外的任何单个字符 |
\w | 匹配数字、字母、下划线 |
\W | 匹配非数字、字母、下划线 |
\d | 匹配数字 |
\D | 匹配非数字 |
\s | 匹配空白字符(空格、换行) |
\S | 匹配非空白字符 |
\n | 匹配换行符 |
注: 如果要匹配 `` 本身,应当使用 \。
正则表达式还能够匹配字符串的开头或结尾,如下所示:
| 表达式 | 描述 |
|---|---|
^abc | 匹配以 abc开头的字符串 |
abc$ | 匹配以 abc结尾的字符串 |
^abc$ | 完全匹配字符串 abc |
我们可以使用正则表达式的量词指定匹配的次数:
| 量词 | 描述 |
|---|---|
* | 匹配前面的子表达式零次或多次 |
+ | 匹配前面的子表达式一次或多次 |
? | 匹配前面的子表达式零次或一次 |
{n} | 匹配确定的 n次 |
{n,} | 至少匹配 n次 |
{n,m} | 最少匹配 n次且最多匹配 m次 |
我们可以使用正则表达式的标志启用一些特殊功能:
| 修饰符 | 描述 |
|---|---|
i | 执行对大小写不敏感的匹配 |
g | 执行全局匹配 |
m | 执行多行匹配 |
// 利用RegExp函数生成一个正则表达式,不区分大小写、多行匹配、
全局匹配(不只是匹配第一个hello而是要匹配所有的hello。)
let reg=new RegExp(`hello`,"img");
set对象
set对象允许存储任意对象的唯一值集合。相当于set里面存储的值都是唯一的,没有重复的。
使用方法:
let mySet=new Set()
mySet.add(要插入的值)
mySet.delete(要删除的值
forEach()的用法
const array = ['a', 'b', 'c'];
array.forEach(function(element) {
console.log(element);
});
// 输出:
// a
// b
// c
也可以 array.forEach(arr=>{
console.log(arr)
})
表达式:
let reg=RegExp(rule[i],"img")
RegExp()----------正则表达式的构造函数。
rule[i]-----------构造函数的第一个参数,例如会把"abc"转换成正则表达式的形式。不用自己写表达式。
“img”---------表示匹配的模式,
i-----------------不区分大小写。
m----------------全局匹配,不只是会匹配第一个。会匹配所有的。
g----------------表示多行匹配,它会影响 ^ 和 $ 这两个锚字符的行为,使它们能够匹配每一行的开始和结束,而不仅仅是整个字符串的开始和结束。
JavaScript进阶(ES6)
String对象的扩展
模板字面量:``
可以代替单引号和双引号来表示字符串。
typeof 识别的也是string 类型。
若字符串中有反撇号想要输出。可以使用斜杠来转义一下。
反撇号还可以应用与多行字符串。
let str = `Hello,
ECMAScript 6`console.log(str)
字符串占位符:
//以前用+号连接起来
console.log("a+b="+a+b);
//反撇号用法一
let str=`abcdefg`
console.log(`字母是${str}。`); // 输出是字母是abcdefg。
//反撇号用法二
let a=1;
let b=2;
console.log(`a+b=${a+b}。`); //输出是a+b=3。
//反撇号用法四
const dog = { name: '闷墩儿', breed: '柯基', age: '一岁' }
const introduce = `大家好!我叫${dog.name},我是一只可爱的小${dog.breed}。
今天是我${dog.age}的生日,欢迎朋友们给我送礼物哦!`
console.log(introduce)
indexof()方法查找,某个字符在字符串中出现的首个位置。若是不存在则返回-1,也可以查找数组的某个元素的下标。
let str=`hello world!`
// 要查找的字符串
let st=`o`
//查找o
const res=str.indexof(st)// 默认从第一个查找。
const re=str.indexof(st,6)// 从指定位置查找。
lastindexof()方法,查找某个字符在字符串中最后出现的位置。用法和上面的类似。
includes()方法,查找某个字符是否存在于字符串。存在返回true,不存在返回false。
let str=`hello world?`;
let st=`world`;
let s=`fg`
if(str.includes(st)){ //默认从第一个字符开始查找
console.log(存在world)
}
if(!str.includes(s,4)){
console.log(不存在)
}
startWith()方法、endsWith()方法-----判断字符串是否以指定字符开头或者结尾。
用法与上述一致。
创建数组可以直接创造。
let arr=[1,2,3]
也可以使用Array的内置函数,Array.of()方法。
let arr=Array.of(1,'abc',4);
Array.from()-----------可以将非数组的对象转化成数组。
let str=`hello world!`;
let arr=Array.from(str);
Array对象的扩展
find()方法,可以用于找到一个数组中想要返回符合条件的数组。
let arr=[2,3,4,6,3,4,5,9];
// 可以写普通函数的形式,
// 这里的element,index,array都分别是:
// find方法正在处理的元素、当正在处理的元素的索引、调用find方法的数组。
let ar=arr.find(function (element,index,array) {
return element>3
})
// 可以写箭头函数的形式
let q=arr.find(element=>element>4);
find()-------是从数组的头部开始找的,lastfind()方法是从数组的最后开始找的。
includes()方法-----关于数组的判断一个数组里面是否有想要查找的元素。用法和参数都和字符串的方法相同。
some()方法----------和find()方法类似,find()方法是返回一个符合条件的数组,而some()方法是符合条件返回true,不符合条件返回false。
用法和参数都和find()方法一样,只是返回不一样。
every()方法------ 若数组的所有元素都满足函数里面的条件,则返回true,否则返回false,用法和参数和find()和some()方法一样。
sort()方法---------给函数排序的方法
let arr=[3,2,1,1,4,6,5];
arr.sort((a,b)=>a-b);
// 若a-b是负数则a排在b前面,代表从小到大排列。
// 若a-b是正数,a排在b后面,代表从大到小排列。
// 若a-b是0,则不动
reverse()方法--------将数组逆序输出。
fill()方法---------将原数组的值用指定的元素替换。第一个参数是要替换的元素(必填),第二个参数:起始位置,第三个位置:结束位置。
let arr=[1,2,3,4,5];
arr.fill(6); // 将arr数组的所有的值替换成6
arr.fill(7,1,3) // 将arr数组从第二个元素开始到第四个元素结束的元素,都替换成7。
map()方法-------对每个数组元素使用回调函数并返回一个新数组。
let arr=[1,2,4,7];
let result=arr.map(function (element,index,array) {
return element*2;
},thisArg)
// 此处的thisArg用法还是不懂。
难点:
reduce()方法------对数组的每个元素执行用户设定的回调函数,并传入前一个返回值。对数组的所有元素执行回调函数的最终结果是单个值。从前往后开始。reduceRight() 方法从后王前开始。
let arr=[1,2,3,4];
let result=arr.reduce(function (accumulator,element,index,array) {
return accumulator+element;
})
// result的值是10.
// accumulator是累加器,element是正在处理的元素,index:当前元素的索引,
//array:使用了reduce方法的数组。
// 特别说明:当reduce执行第一个元素的时候,没有前一个返回值,所以,将第一个元素作为返回值,
// element从第二个元素开始遍历。
entries()、keys()、values() 是 ES6 中三种数组的遍历方法,三个方法返回的都是 Array Iterator 迭代器对象。
let arr=['a','b','c'];
console.log([...arr.entries()]);// 返回键值对,输出[0,'a'],[1,'b'],[2,'c']
console.log([...arr.keys()]); // 返回索引,[0,1,2]
console.log([...values()]); // 返回值,['a','b','c']
函数的拓展
剩余参数-----只能放在最后一个参数。否则会报错。
function fun(a,b,...rest){
console.log(rest);
}
fun(1,2,3,4,5) //只会输出3,4,5
对象的扩展
在Es6之前,判断两个值相等的方法是==或者===
但这个方法存在缺陷。
// 特殊情况一
console.log(-0==+0)// true
console.log(-0===+0)// true
// 特殊情况二
// NaN不等于任何值包括它自己
console.log(NaN==NaN)// false
console.log(NaN===NaN)// false
所以ES6以后,新增加了Object.is()来判断两个值是否相等。
soncole.log(Object.is9(-0,+0)) // flase
等等
Object.assign()方法-----合并对象。相同属性名会后者覆盖前者,不同的属性名会累加到一起。
let obj1={
name:'小兰花',
age:12,
food:'花菜'
}
let obj2={
sex:'男',
food:'apple'
}
console.log(obj1,obj2); // 输出{ name:'小兰花',age:12,sex:'男',food:'apple'}
面向对象编程
ES6之前是没有类的。
类是用来创建类的蓝图。---类是蓝图
类和函数都有两种表达形式
- 声明式形式(例如function和class关键字)
- 表达式形式(const A=class{})
JavaScript的继承和Java的继承关键字一样,都是extends
super关键字------------扩展继承的用法。保留父类的方法不动,扩展使用父类的方法。
class Dog extends Animal {
constructor(name, age, speed, species) {
super(name)
this.species = species
}
run() {
console.log(`${this.name}是一只奔跑的${this.species}`)
}
}
let dog = new Dog('闷墩儿', '一', 5, '狗')
dog.run()
静态属性和方法
静态属性就是属于类的属性,所有实例共享,不会实例的操作改变。通过类名调用。
静态方法就是属于类本身,不依赖具体实例,通过类名调用。不能通过实例对象调用,
使用方法就是类属性或者方法前面加上关键字static
重点:静态属性和方法不能通过实例对象调用,只能通过类调用。
静态属性和方法可以被继承,在实例化对象里面调用静态属性或方法就是继承。
私有、共有属性和方法
公有属性:在类的内部和外部都可以访问
私有属性:只能在类的内部访问,不能再类的外部访问
私有属性和方法的定义:使用#前缀
注意:
如果实例化对象后,直接通过实例化对象访问私有属性也是错误的。
要用实例化对象调用类的公有方法,然后公有方法里面再调用私有方法。调用私有属性也是一样。
Set对象和Map对象
Set对象不允许存放相同的元素,每个元素都是独一无二的。
Set对象要通过实例化创建。
let obj=new Set([1,2,3]);
console.log(obj.size);// 输出Set对象的元素个数。
Set对象添加元素方法
obj.add(4);
判断Set对象中是否有特定对象
obj.has(5);
从Set对象中删除指定元素。
obj.delete(3);
删除Set对象的所有元素
obj.clear() // 清空obj对象
Map对象-----注意区分map和Map是不一样的。
创建Map对象也是需要实例化的。
在ES6之前,是用对象来存放键值对的元素。但用对象的方式,键名只能是字符串。并且对象不可以使用forEach()方法来遍历。所以就新增加了一个Map对象用于存放键值对,而且其键值对可以是任意数据类型。
创建Map对象后,添加元素的方法。set(键,值)
let Map=new Map()
Map.set(1,'书');
Map.set(true,'笔');
Map.set('小兰花','花');
获取某个键的值-----get(键)
console.log(Map.get(true));
has()方法、delete()方法、clear()方法的用法和含义和Set对象一致。
了解:WeakSet和 WeakMap对象。
异步-Promise
Promise对象是用来解决回调地狱的问题。在Promise对象出现之前,存在回调地狱的问题。
回调地狱:现在有一个请求,需要通过接口一的返回值去获取接口二的返回值,再通过接口二的返回值去获取接口三的返回值,以此类推。不过这样的实现是通过一层一层的回调函数解决的。现在要是去修改某一层的回调函数,会牵一发而动全身。维护工作十分巨大。
function outnum(n, callback) {
setTimeout(() => {
console.log(n)
callback()
}, 1000)
}
outnum('1', () => {
outnum('2', () => {
outnum('3', () => {
console.log('0')
})
})
})
Promise的出现就是来简化或者解决回调地狱的。
Function renum(n) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log(n);
resolve()
},1000)
})
}
renum(1).then(()=>{
reurn renum(2);
}).then(()=>{
return renum(3);
})
// 输出1
// 输出2
// 输出3
Promise.all()用来管理多个Promise对象。当所有的Promise对象里面的东西都执行成功后,才会执行后面的then()方法中的成功回调函数resolve(),若存在一个Promise对象没有成功执行,那么就执行后面的的then()里面的失败回调函数。
function p(n) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if(n>3){
resolve(n)
} else {
reject(n)
}
},1000)
})
}
Promise.all([p(1),p(4),p(5)]).then(
(v)=>{
console.log(`${n}大于3`);
},
(e)=>{
console.log(`${e}小于3`)
}
)
Promise.race()方法,用于执行最快完成Promise对象任务的Promise对象。
function loadData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求成功')
}, 6000)
})
}
function timeOut() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求超时')
}, 5000)
})
}
Promise.race([loadData(), timeOut()]).then(
(v) => {
console.log(v)
},
(e) => {
console.log(e)
}
)
// 两个Promise对象,timeOut()对象先完成。所以timeOut()对象快,所以就执行timeOut()对象的
// reject给then()的(e) => {
// console.log(e)
// }
现在Promise对象的方式已经可以简洁地解决回调地狱问题。但是可以有更加简洁和高效地解决回调地狱问题。又称作为Promise对象的升级版。
async和await关键字
async本质上是Promise对象的语法糖写法,语法糖就是相当于成语的意思。
async function fn(){
return '123'
}
fn().then(
(v)=>{
console.log('成功!');
}
)
在一个函数前面加上关键字async,就可以让这个函数返回一个Promise对象。就可以在函数后面使用then()方法。
await关键字:就是async wait的简写,意味着等待异步完成,相当于将异步操作传化成同步操作顺序执行。必须要等待有await关键字的异步操作完成后才可以执行下一步操作。
因为有await关键字所以,以前的嵌套式的异步操作可以管道式的书写。
// 函数 p() 返回的是一个 Promise 对象,
// 延时 1 秒后执行成功回调函数,相当于模拟一次异步请求
function p(msg) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 将函数 p() 的实参值 msg 作为执行成功回调函数的返回值
resolve(msg)
}, 1000)
})
}
// 先进行p1的异步请求,要等待执行完成后,将p1的返回值给p2作为参数,等待p2执行成功后
// 将p2的返回值当作p3的参数执行。此写法避免了回调地狱的层层嵌套的局面。
async function fn() {
let p1 = await p('a')
let p2 = await p(p1 + 'b')
let p3 = await p(p2 + 'c')
console.log(p3)
console.log('登录成功!')
}
fn()
在此说明一点:Promise.all()的方法是并发式的异步请求。await是链式的异步请求。
显然,并发式的异步请求明显比链式的异步请求效率高。
Proxy对象
相当于一个代理商,或者一道门,可以通过代理商对目标对象进行操作,但是要遵循代理商设下的规则。
实例化Proxy对象需要传入两个参数。第一个参数是要操作的目标对象。第二个是指定拦截行为的对象。
第二个参数,例如,其对象里面定义了一些方法,这些方法可以阻止对目标对象的增删改查操作。
ReferenceError 是 JavaScript 中的一种内置错误类型,表示一个引用了一个未声明的变量或不存在的属性的错误。
想要使用ReferenceError类型,就要实例化 ,new ReferenceError('要抛出的错误信息');和throw关键字配合使用。
throw new ReferenceError('要跑出的错误信息!')
throw 用于报出错误。
现在学习一下Proxy对象的几个重要的方法。
get(target, propKey, receiver)
set(target, propKey, value, receiver)
has(target, propKey)
ownKeys(target)
Proxy对象的get()方法。用于定义访问目标对象属性行为的方法。 访问成功返回属性值,就代表访问成功。
例如当想要读取目标对象的键的值。若想要访问的键没有,则要抛出错误。
const q={name:'夸克'};
let proxy =new Proxy (q,{
get(target,propKey){
if(propKey in target){
return target[propKey];
} else {
throw new ReferenceError(propKey+'属性不存在!')
}
}
})
console.log(proxy.name);// 输出夸克
console.log(proxy.age);// 浏览器报错。输出age属性不存在!
set(target, propKey, value, receiver)方法,用于定义添加目标对象属性行为的方法。
Number.isInteger(value)// 用于判断value是否是整数。是就返回true,不是就返回false
set()方法如果添加属性成功就要return true
例如要插入空对象一个键值对是age:10;
const setting={
set(target,propKey,value){
if(propKey==='age') {
if(!Number.isInteger(value)){
thow new TypeError(value+'的类型错误!')
}
}
}
}
const dog=new Proxy({},setting) // 这里的目标对象是空对象
dog.age='12';
has(target, propKey) 方法,用于定义判断目标对象属性是否存在的行为的方法。
const people={name:'小兰花',age:18};
const have={
has(target,propKey) {
if(propKey==='age'&& target.age>16){
console.log(`${target.name}的年龄是大于16的!!!`);
return true; // 表示判断成功,存在。
}
}
}
const proxy=new Proxy(people,have)
console.log('age' in proxy)
ownKeys(target) 方法,用于拦截读取对象属性的行为的方法。
以for in 为例子
const dog={name:'小兰花',age:18,food:'fish'};
const own={
ownKeys(target){
return ['name','age'];
}
}
// 只给访问者看name和age键值,不给看food键值。
const proxy= new Proxy(dog,own)
// for in 遍历,本意是想proxy的所有键值。
for(let i in proxy) {
console.log(i)
}
注意:
为什么使用for in 语句后不能用点的形式来访问对象的属性。要用[]的方法来访问对象属性?
因为方括号[]k可以将任何字符串视作一个统一的整体来当作属性名。
因为对象的键值可以是任何字符串,
例子:
const obj1={
name:'高飞';
key.name:'小兰花';
key value:'吴悠';
}
for(let key in obj1){
console.log(obj1[key.name]);// 可以正确输出
console.log(obj1.key value);// 发生错误
}
ES6
let、const、var
块级作用域:
一句话就是:花括号{}里面常量和变量的作用范围。
变量提升
这个简单来说就是一种行为,某个作用域声明了var变量或常量。那么JavaScript就会把这个变量或者常量提升到此作用域的顶部。相当于放在此作用域第一行。
注意:此处说明一下什么是声明?什么是定义?什么是赋值?我觉得可能会有些小伙伴没注意此处:
var cat;// 声明
// 就是只声明了此变量而已。
var cat;
cat='咪咪';// 赋值
var cat='咪咪';// 定义。
// 定义就是声明加上赋值。嗯嗯,好理解
注意:let 和const没有变量声明,只能在声明后使用,否则会报错。会报出TypeError的错误。这个错误就是使用了没有声明的变量或者常量的时候会爆出的。(不是var,是let或者const)。
重复声明:
var可以重复声明,let和const不可以重复声明。后面重复声明的var会覆盖前面的重名的var。
全局对象属性:
JavaScript里面规范:在全局作用域里面的var声明的变量会被自动添加到全局作用域对象的属性,也就是window对象。
暂时性死区:
上面刚刚说过,使用了未声明的let和const,就会爆出TypeError的错误,因为这里有一个暂时性死区。
暂时性死区:简单来说,就是未声明let、const变量就使用就会报错。这个错误在语法上就是暂时性死区。
初始值规定:
var和let都可以只声明不赋值,但const必须声明的时候也要赋值,也就是必须定义,而不是声明。
更改指针指向:
就是let可以重新赋值,const不可以重新赋值。
let是变量,const是常量。
嗯,这时候就有一个问题了:既然它有这个优点,为什么在ES6中的const和let没有变量提升的特点?是他们忘了吗?
答案不是的,是开发者有意为之,变量提升是将var变量提上至当前的执行上下文的顶部。但是var变量的赋值又不会提升。这个在很多时候都会产生难以发现的错误。所以干脆放弃变量提升,这也是为了开发者自己要注意变量声明的执行上下文的位置。
箭头函数
ES6新语法,没有自己的this(但是可以有继承来的this)、super等绑定,语法简洁、书写简洁。更重要的是箭头函数没有prototype属性,但有__proto__属性。
箭头函数无法作为构造函数来new一个实例对象的要点:
- 构造函数实例化对象的时候,需要创建自己的this来指向实例对象的属性上,前面原型里面说过,构造函数初始化实例化对象的时候,是把属性存放在实例对象里面,构造函数里面的prototype属性只存放关于属性的方法。但是箭头函数没有自己的this。
- 构造函数实例化对象后,实例对象的__proto__属性需要指向其构造函数的prototype属性。但是,箭头函数没有prototype属性。
箭头函数继承而来的指针的值永远不会变。因为箭头函数只是调用上层继承而来的this值。
执行上下文
执行上下文就是代码的运行环境。执行上下文可以简单分成两个:全局执行上下文和函数执行上下文。
全局执行上下文:就是代码程序的最外层环境。是整个程序执行的环境。在这个环境,也就是全局执行上下文,this会指向浏览器的window对象。node.js是global
函数执行上下文:函数内部代码的执行环境,每当函数被调用的时候,自动被创建。
作用域链:这是JavaScript的一种机制。作用域链是JavaScript在查找变量和函数的时候使用的一种链表结构。
确保变量和函数的正确解析。
那么此处的作用域链的工作机制是怎么样的?
作用域链在查找一个变量和函数的时候会先找当前的执行上下文再找外层的执行上下文,最后找全局的执行上下文。若都没找到就抛出ReferenceError错误,表示引用了未声明的变量。
那么用来查询指定函数的this值的方法有什么呢?
call()方法、apply()方法、bind()方法
执行上下文栈:
用于管理执行上下文。管理方式和栈的运行机制类似。
对象与数组的解构
解构就是ES6提供的一种模式,用于从对象和数组里面针对性的拿数据。
现在是第一种:数组的解构
const [a,b,c]=[1,2,3,4]// 这样可以把数组的前三个按顺序分别赋值给a,b,c.
// 这样看好像就是把前三个一起取出来而已嘛。还可以用于另一种情况
const [a,,c]=[1,2,3,4,5]// 取出来1和3。这样是不是就可以体现出来可以取任意想要的数据了。
现在是非常常用的:对象的解构
这里的解构说的是没有嵌套的对象。就一个对象
const stu={
name:'小兰花';
age:18,
sex:'male'
}
const {name,age}=stu;// 可以只拿出对象里面的想要的属性。方法就是通过键来取值。
// 这种方式比数组的解构更加强大。哪里强大?不用管里面的属性的顺序,
// 只需要以属性的键值来定位。意思是:
const {age,name}=stu;// 这样也是没问题的。
当出现一种嵌套对象的时候,再用上面的方法是无法成功解构的。
const School={
ClassName:{
Student:{
name:'小兰花';
}
}
}
这里我再说明一下:为什么School对象用的是等于号=,而里面的ClassName对象和Student对象是用冒号:表示?
School对象用等于号=是因为这里是定义一个对象。而定义包括声明和赋值,=就是赋值。
里面的对象用冒号表示是因为ClassName对象是作为School对象的属性。这里我可以再说明一下,对象可以被当作属性,就像原型一样。prototype被对象的属性,其实这个prototype属性也是一个对象。嗯,这里又复习了prototype属性。
应对这种嵌套式的对象,其方法是:逐层解构
const {ClassName:{Student:{name}}}=School;
// 先取School对象的ClassName属性,再取ClassName对象的Student属性,再取Student对象的name属性。
前面扩展运算符那里使用了模板字符串,那说一下模板字符串
const str='hello world';
console.log(`我要说的话是:${str}?`);
说明:不用这个用字符连接符的话,会十分的麻烦且不好看。
const str='hello world?';
console.log('我要说的话是:'+str+'?');// 字符少还好,字符多了就眼花缭乱了。
// 当然了,到底好不好用,得自己试试看。
new操作符
new操作符使用得时候,发生的事情:
function re() {
this.name=name;
console.log(`我打印的是${this.name}!`)
}
const re = new re('小兰花');
- 创建一个空对象。
- 将此函数实例化出来的对象里面的__proto__属性指向此函数的prototype属性。为了构建好原型链。
- 让此函数的this指向该实例对象的对应的属性。(这个操作是因为前面原型那是讲过了,构造函数里面的prototype属性里面只存放构造函数的方法。而每个构造函数里面constructor里面创建的属性是用this来指向实例对象的属性,每次由构造函数实例出来的对象里面才真正放着属性。)又复习到了。
- 构造函数里面若return 有基本类型,则其实例对象会忽略这个返回。(为什么会忽略?:乌龟的屁股)。
若return 的是引用类型,那么实例对象也会返回此引用类型。实例对象也会返回引用类型是什么意思?
function re() {
this.name=name;
// console.log(`我打印的是${this.name}!`)
return {
age:18
}
}
const re = new re('小兰花');
console.log(re);
打印实例对象即可。
Arguments类数组对象
首先就是介绍,这个对象是一个类似于数组的对象,所以叫这个名字。那么为什么类似于数组呢?
因为Arguments是用来处理函数的参数的,是JavaScript规定的在函数上下文里面函数内置的对象。
ok,现在明白它是什么。就要了解它的作用是什么了。
在函数创建后,调用函数并传递参数的之前。要怎么处理函数的个数问题?因为参数个数不确定。
所以这个时候,JavaScript就在函数上下文里面内置了一个对象,用于管理参数,不管传入的参数个数有多少。
都放在这个对象里面。ok,看到这里,还是没有说明,这个对象该怎么就类似数组了。别急,下面介绍:
这个对象提供了一个obj.length方法,可以获取传入参数的个数。这个对象还有索引的概念。en,这个不就是数组的方法吗?,对,这个点有点类似于数组。所以就叫它类数组。en,应该很明了了。
这里再提最后一点:可以使用Array.fromm()方法来将其转变成真正的数组。
尾调用
这是一种严格模式下的机制;函数的最后一步操作时调用另一个函数,那么外边的这个函数就没必要再保留其执行上下文了,删除外层函数的执行上下文,然后创建另一个函数的执行上下文。这样用于节约内存空间。
ES6模块系统和commonJS模块系统
commonJS是用于node.js环境的。node.js是单线程,所以这个模块是同步加载
ES6适用于浏览器环境。支持异步操作。
各自的导入导出的语法不一样。
强类型语言和弱类型语言
强类型语言:顾名思义,就是需要强制类型转换才可以改变数据类型。没有隐式类型转换。
优点:更安全
缺点:性能上会慢一点
弱类型语言:可以进行隐式转换的语言。
优点:性能快一点
缺点:不安全一点。
闭包
现在来简单说一下闭包的构成。就是一个函数里面嵌套了另一个函数。内层函数使用了外层函数里面的变量。
function a(){
let i=0;
function b(){
console.log(i);
i++
}
}
// 此代码实现了闭包,条件一函数嵌套函数,条件二内层函数使用了外层函数的变量。。
// 我认为私有化并不是闭包的优点,或者说是作用。
闭包的好处在于实现一种特性,而不是奔着私有化的功能去的。要想私有化,普通函数里的局部变量也是是私有化变量,为何要闭包。所以下面代码的特性才是闭包的用处。
function a(){
let i=0;
return function b(){
if(i<100){
i++;
console.log(i);
}
}
}
// 现在在控制台输入a()后观察打印的东西,你会发现,嗯?根本没有正确打印,我明明把i写在console的
// 上面,为什么会打印b函数本身?因为a()是执行一次a函数,执行一次a函数会定义变量i,
// 然后返回函数b本身,并不会执行里面的代码。
function a(){
let i=0;
return function b(){
if(i<100){
console.log(`我是${i}`);
i++;
}
}
}
const se=a();
// 为什么带控制台输入se()又可以成功打印,并且变量i也可以实现自增操作。
// 刚刚说过了,a()返回的是b函数本身,所以现在se就是b函数本身,现在又执行se(),
// 相当于执行b函数。所以就超过打印了。
闭包实现的特性就是在实现了私有函数的同时,外部又可以通过函数来操作这个私有函数。实现了一种半私有的状态。可以调用含有私有变量的函数,但不能直接使用这个私有函数。
JavaScript扩展
BOM
BOM叫浏览器对象模型。
BOM模型定义了一些方法和对象。用户可以通过JavaScript来调用BOM模型里面的方法和对象来操作浏览器。
所以就叫做浏览器对象模型。
BOM对象模型里面有各种的方法和对象。其中主要的对象:
window对象:代表浏览器的窗口或标签页,是BOM的核心对象。它提供了许多属性和方法,例如打开和关闭窗口、操作窗口尺寸和位置、加载和导航URL、设置定时器、处理事件等。document对象:表示当前窗口或标签页中加载的文档。它提供了许多方法和属性,用于访问和操作文档内容,例如获取和修改元素、添加和删除节点、修改文档的标题等。navigator对象:提供了关于浏览器和用户代理的信息,如浏览器的名称、版本、平台等。它还可以判断浏览器的属性和功能,从而进行浏览器兼容性处理。history对象:用于操作浏览器的历史记录。通过history对象,可以在用户浏览历史中向前或向后导航,以及在历史记录中添加新的URL。location对象:提供了访问和操作当前页面URL的接口。通过location对象,可以获取当前页面的URL、修改页面的URL、导航到新的URL等操作。screen对象:表示用户屏幕的信息,如屏幕的尺寸、分辨率等。它提供了一些属性,用于获取关于屏幕的信息,例如获取屏幕的宽度和高度。
windows对象提供了三种可以在浏览器显示对话框的方法。
方法一:
alert() // 可以在浏览器弹出一个对话框,只有一个确认按钮,对话框只显示一段文本内容。
方法二:
confirm()// 在浏览器弹出一个对话框,有确认按钮和取消按钮,对话框只显示一段文本内容。
// 会返回一个布尔值,用户点击确定就是true,点击取消就是false。
方法三:
prompt()// 在浏览器弹出一个对话框,有确认和取消按钮,有一个可以供用户输入的输入框。
// 点击确定后,返回用户输入的字符串。
// 可以有两个参数,第一个参数是想要显示的文本内容,第二个参数是输入框中的默认值。
windows对象中有一些关于浏览器窗口的只读属性
innerWidth// 浏览器窗口的宽度。
innerHeight// 浏览器窗口的高度。
outWidth// 整个浏览器的宽度。
outHeight// 整个浏览器的高度。
获取设备屏幕的尺寸
注意:
访问windows对象的screen属性,会返回一个screen对象。就可以访问screen对象里面的关于设备屏幕的宽高信息。
screen.width// 设备屏幕的宽度。
screen.height// 设备屏幕的高度。
screen.availWidth// 屏幕的可用宽度。
screen.availHeight// 屏幕的可用高度。(不包括下方的任务栏。)
location对象的属性
location.herf// 可用获取整个URL的信息。
location.procotol// 获取URL的协议类型。
location.hostname// 获取URL的域名。
location.pathname// 获取URL斜杠/后面的路径。
location.search// 获取URL中问号?后面的查询字符串。
location.hash// 获取URL中的井号#后面的片段标识符。
// 片段标识符是用来定位到一个网页的指定位置的。
history对象的方法
location.go()// 一个参数。1代表历史记录中的下一个页面。-1代表浏览记录中的上一个页面。
// 0代表重新加载页面。还可以-2,表示后退两个页面。以此类推。
// 注意:此方法会触发重新提交表单数据。
location.back()// 没有参数,只会返回一个页面,且不会触发吃重新提交表单数据。
DOM
- 定义:DOM(Document Object Model)是W3C制定的访问HTML和XML文档的标准编程接口,定义了文档的逻辑结构以及访问和操作文档的方式。
- 功能:
-
- 节点操作:DOM将文档表示为一个树状结构,每个节点可以是元素节点、属性节点、文本节点等。提供了方法如
node.appendChild()、node.removeChild()、node.replaceChild()等来操作节点。 - 属性操作:提供了方法如
element.getAttribute()、element.setAttribute()、element.removeAttribute()等来操作元素的属性。 - 样式操作:通过
element.style属性可以动态地修改元素的CSS样式。 - 事件处理:提供了标准的事件处理机制,如
element.addEventListener()、element.removeEventListener()等方法用于添加和移除事件监听器。
- 节点操作:DOM将文档表示为一个树状结构,每个节点可以是元素节点、属性节点、文本节点等。提供了方法如
注意:BOM里面的document对象操作只是简单的文档操作,例如元素的增删改查,事件处理。
而DOM模型是把BOM模型里面的document对象单独拿出来定义了更标准的访问,操作文档的模型。DOM可以实现更 灵活和强大得操作document文档。
获取document对象的某一类元素
document.title// 获取文档的标题。
document.URL// 获取文档的URL。
document.某种标签// 获取文档的标签元素。
特别:
document.link// 获取文档里面的超文本链接 <a>
想要操作文档里面的特定元素,肯定就要通过document对象进行操作了,
操作标签的第一步就是找到标签。
JavaScript找到标签的方式,也就是获取元素节点:
- 通过id选择器查找标签。
- 通过类选择器查找标签。
- 通过标签名获取元素列表。
- 通过name属性获取元素列表。
- 通过选择器获取第一个匹配的元素。
- 通过选择器获取所有匹配的元素列表。
document.getElementById()
document.getElementsByClassName()
document.getElementByTagName()
document.getElementByName()
document.querySelector()
document.querySlecttorAll()
在获取到元素节点后还可用根据这个节点来获取上一层或下一层(也就是父元素,子元素),还可以获取同层级的前一个或后一个元素。
const box=document.getElementByClassName('box')// 获取类名为box的元素节点。
console.log(box.parentElement)// 打印获取的元素节点的父元素。
console.log(box.children)// 打印获取的元素节点的子元素列表。
console.log(box.firstElementChild)// 打印获取的元素节点的第一个子元素
console.log(box.lastElementChild)// 打印获取的元素节点的最后一个子元素
console.log(box.previousElementSibling)// 打印获取元素节点的同层级的前一个元素
console.log(box.nextElementSibling)// 打印获取元素节点的同层级的下一个元素
注意:想要删除DOM节点的话,就要先获取其父元素,再通过父元素来删除。用removeChild()方法来删除
const parent=document.getElementById('bigBox');
const son= document.getElementById('littleBox');
bigBox.removeChild(littleBox);
交换DOM节点的位置:
使用insertBefore()方法。先获取父节点,通过父节点来操作。方法里面放上对应的位置即可。
定时器
一次性定时器:倒计时结束后执行一次定时器中的函数
setTimeout(function,delay,prams1,prams2、、、)// setTimeout方法是异步函数。
// function参数:倒计时结束后要执行的方法。
// delay参数:倒计时时间。
// params参数:传递给function的指定函数的方法。
清除定时器
setTimeout()执行完后会返回一个ID,这个ID是一个正整数。
// 创建一个setTimeout定时器,并将返回的ID传给time
const time=setTimeout(()=>{
console.log('我执行了!')
},1000)
clearTimeout(time)// 清除定时器。
间隔定时器:每隔一段时间就会执行一次定时器中的函数。
// 使用格式
setInterval(function, delay, param1, param2, /* …, */ paramN)
// function参数:定时器里执行的函数
// delay参数:时间间隔。
清除定时器
间隔定时器也会返回一个ID,也是正整数。
const interval=setInterval(()=>{
console.log('间隔定时器执行了。')
},1000)
clearInerval(interval);// 清除定时器。
本地存储
首先介绍一个概念,应用程序编程接口API:
一种软件和服务器之间之间的数据交互方式。软件和服务器之间想要交互数据,就可以使用API
API的功能可用类比于菜单。后厨是服务器端,客户是软件客户端。用户可以使用API发送请求到服务器端,客户端通过API返回数据给客户端。
localStorage API
长期存储
localstorage对象是HTML5新特性。早期cookie被用作存储客户端数据,但是,服务端也会知道cookie中的信息。而新的localstorage对象存储的客户端的数据不会被服务端知道。
ok,现在知道了API的作用,现在就是了解怎么通过API发送请求了。
// 调用localStorage对象的setItem方法。key是键值,value是属性值。
localStorage.setItem(key,value);// 保存数据到本地
// 调用localStorage对象的getItem方法,参数是键值。
localStorage.getItem(key);// 从本地获取特定值
// 调用localStorage对象的removeItem方法,参数是键值。
localStorage.removeItem(key);// 在本地删除删除特定值
// 从本地清除所有数据
localStorage.clear();
sessionStorage API
短期存储
// 调用session对象的setItem方法。key是键值,value是属性值。
session.setItem(key,value);// 保存数据到本地
// 调用session对象的getItem方法,参数是键值。
session.getItem(key);// 从本地获取特定值
// 调用session对象的removeItem方法,参数是键值。
session.removeItem(key);// 在本地删除删除特定值
// 从本地清除所有数据
session.clear();
事件
鼠标事件
click// 鼠标点击事件
mousedown// 鼠标按下事件
mouseup// 鼠标抬起松开事件
mouseover// 鼠标移入事件
mouseout// 鼠标移出事件
mousemove// 鼠标移动事件
就是鼠标对元素做出一系列动作的时候,要发生的事件。
// 先获取鼠标要操作的元素节点
const q=document.getElementByClassName('box');
// 给名为q的元素节点添加一个事件监听器,可以监听鼠标点击事件,第二个参数就是监听到点击后
// 要执行的函数。
q.addEventLisenter('click',function()=>{
q.style.width=20px;
})
键盘事件
keydown// 键盘按下
keyup// 键盘抬起
表单事件
focus// 表单元素聚焦时候触发的事件
blur// 表单元素失焦时候触发的事件
事件对象(事件对象是一个大类,事件对象里面还有细分的鼠标事件对象,键盘事件对象)
事件对象是一个和事件有关的对象。这个对象主要是当有事件触发时候,要给函数传递一个参数,然后里面的函数会把信息自动传递给这个对象,从而使得这个对象可以获取各种事件的信息。
const q=document.getElementByClassName('box');
q.addEventLisenter('click',function(event)=>{
q.style.width=20px;
console.log(`这就是一个${event.ty}事件`);
})
鼠标事件对象
const q=document.getElementByClassName('box');
q.addEventLisenter('click',function(event)=>{
q.style.width=20px;
console.log(`这就是一个${event.type}事件`);
console.log(event.button);
console.log(event.clientX);// 视口的x坐标
console.log(event.clientY);// 视口的Y坐标
console.log(event.pageX);// 整个页面(包括滚动条)的x坐标
console.log(event.pageY);// 整个页面(包括滚动条)的y坐标
console.log(event.movementX)// 鼠标相对于上一次的movementX事件的x坐标
console.log(event.movementY)// 鼠标相对于上一次的movementY事件的Y坐标
})
键盘事件对象
| 属性 | 说明 |
|---|---|
code | 键盘上的按键的代码值 |
key | 按键产生的字符(考虑大小写) |
shiftKey | 是否按下 Shift 键 |
ctrlkey | 是否按下 Ctrl 键 |
altkey | 是否按下 Alt 键 |
网络请求
首先来介绍一个比较有用的东西:XML
简单来说,XML就是一种语言,叫做可扩展标记语言。
什么叫标记语言?
可以理解成类似成HTML,因为HTML也是标记语言。标记语言就是通过标签来定义文本的结构,格式内容。标签,标记嘛,像HTML这样用标签来写的语言,就是标记语言。这下就好懂了。
什么叫可扩展性?
XML标记语言可以自定义标签。这个就是可扩展。
比如我想写一个图书信息的XML,如下:
<books>
<book>
<title>JavaScript: The Good Parts</title>
<author>Douglas Crockford</author>
<year>2008</year>
</book>
<book>
<title>DOM Scripting</title>
<author>Jeremy Keith</author>
<year>2005</year>
</book>
</books>
是不是容易读懂其中的结构,有两本书,每本书的信息,一目了然。
知道了什么叫XML,那么就要知道XML被创造出来后,有什么用了。
XML的作用:用来传输和存储数据。所以XML可以用来进行网络的数据请求和响应。
早期Ajax使用XML来传输数据。现在一般采用json格式来传输。
Ajax
Ajax是一种技术,用来创建动态的网页的。
AJax包含的内容:
- JavaScript脚本用于编写异步请求和响应。
- JSON数据传输格式。
- 构建网页结构
XMLHttpRequest对象是用于与服务器进行异步通信的JavaScript对象。
简单来说ajax是实例化一个XMLHttpRequest对象,调用里面的方法和属性。
想要从服务器拿到自己想要的数据,就要用get方法,并传入一些参数来帮助找到想要的数据。参数只能是纯文本。
想要发送给服务器数据就要使用post方法,可以发送各种类型数据给服务器。
不过原生的Ajax编写起来比较繁琐。所以axios就诞生了。为的就是使数据的请求和响应变得简洁。
axios
axios是一个封装了Ajax的第三方的功能强大的库。
- axios封装了请求和响应的细节。可以更好的发送和响应。
- axios还提供了一些方法来发送不同类型的请求。
- axios设计了请求和响应拦截器。在发送前先拦截处理数据,在响应后先拦截处理数据。
- axios提供了一个全局的错误处理方法,可以捕获和处理所有的请求错误。
- axios支持取消请求,可以撤回正在但还未成功发送的请求。
- axios可以对请求和响应的数据进行转换。
- axios是基于promise的异步处理。
总的来说,axios是一个封装了Ajax的第三方库。所以使用要先下载导入。
导入后,就可以使用axios里面的方法了。
axios既是一个函数也是一个对象。
// get请求的axios写法。axios.get()里面有两个参数,第一个是url,第二个是要传入的参数,get方法
// 要加一个params来说明这是get请求的参数。post请求不需要params。直接花括号就可以了。
async function()=>{
const red=await axios.get('www.baidu.com/common/one',{
params:{
name:'小兰花',
age:18
}
})
}
async function ()=>{
const res=await axios.post('www.baidu.com/common/two',{
book:'吞噬星空',
name:'小兰花'
})
}
当然axios不只是有请求,还可以创建一个新的axios实例,然后再配置实例的一些每次请求公有的配置,这个操作也可以叫做二次封装。因为axios本身就是封装的Ajax的嘛。
// 用create方法创建一个新axios实例。
const newOne=axios.create({
baseUrl:'http://www.baidu.com/common'// 基地址,把公共的地址提取出来。便于维护
})
async function ()=>{
// 既然使用了新的axios实例,就不能写成axios.post了,要写成:新实例.post
const res=await newOne.post('/two',{
book:'吞噬星空',
name:'小兰花'
})
}
请求拦截器:
const re=axios.create();// 创建一个axios实例。
// 调用实例里面的intertseptors对象里的request属性。
// 想要自己配置请求拦截器,就要注册request拦截器,用request.use()方法,
// 就可以自己配置请求拦截器了。
re.interseptors.request.use(config=>{
console.log('请求经过了请求拦截器。')
return config;
});
// 调用实例里面的interseptors对象的response属性。
// 想要自己配置响应拦截器的内容,就要先注册reponse拦截器,
// 用reponse.use()就可以注册并自己配置了。
re.interseptors.reponse.use(config=>{
conslle.log('响应经过了响应拦截器。')
return config;
});
use()方法里面的函数就是配置拦截器的函数。config就是函数的一个传入的参数,这个参数是一个对象。
这个对象包含了许多配置项的信息,当在函数体里面对某些配置进行了修改后,return config就会返回一个新的config对象。
JSON.stringfy// 可以将JavaScript对象格式转换成JSON格式。
🌟 进阶学习
当你掌握了JavaScript后,就可以开始学习更高级的内容,比如框架(如React、Vue等)但要记得:不要急于开始Vue的学习,不然会走很多弯路,听我一句劝,工程化很重要。
🎉 总结
恭喜你!你已经迈出了JavaScript学习的第一步。🎈 记住,编程是一个不断学习和实践的过程,不要害怕犯错,多写代码,多思考,你一定可以成为一名JavaScript大师!👏