600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 呕血整理JavaScript知识重点(面试复习必备)

呕血整理JavaScript知识重点(面试复习必备)

时间:2018-11-22 08:27:47

相关推荐

呕血整理JavaScript知识重点(面试复习必备)

ECMAScript

JavaScript 官方名称是ECMAScript是一种属于网络的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。

编辑器

VS code一款微软出品的强大前端编辑器,具有很强的拓展性,常用插件有如下类型:

live server,服务器形式运行代码,ctrl+s保存自动刷新代码;auto rename tag,标签更改后自动补全;Bracket Pair Colorizer 2,代码语法高亮;Prettier - Code formatter,代码格式化,需要进入vscode设置,搜索save关键词,勾选format on save选项;vue,代码高亮;

代码执行顺序

由于程序由上往下执行,避免程序页面延迟,将js代码放入body的尾部。 script标签按顺序执行,引入模块代码延后执行。

命名规则

数字、字母、下划线、$符号均可以作为变量名,但是要注意不能以数字开头和关键名命名,例如while、class等关键词均不可以使用。

声明关键字

var

var声明的变量没有块级作用域,仅有函数作用域,重复命名会覆盖;

if (1) {var a = 6;let b = 9;}console.log(a); // 6console.log(b); // b is not defined

造成可以先使用后申明的现象(变量提升),环境在执行代码之前优先将解析变量并放入堆中,未定义的变量优先定义为undefined,不会发生报错的现象,打开严格模式也不会进行报错。

a = 3;alert(a);var a;// 真实的执行顺序1.var a;2.a = 3;3.alert(a);

let

先声明后使用会造成TDC暂时性死区(报错);存在块级作用域;同一作用域下不可以重复声明;

const

定义后的变量不可改变;声明的同时必须赋值;当const是对象等引用类型变量时只要地址不变即可;拥有块、函数、全局作用域;

无关键词

无关键词定义全局变量会造成全域污染,导致变量冲突难以调试的问题。

object.freeze变量冻结

非严格模式下变量被冻结后再次被使用不会报错,开启严格模式后控制台会进行报错。

"use strict"const INFO = {url: 'https://n.huasenjio.top/',port: '8080'};Object.freeze(INFO);INFO.port = '443'; //Cannot assign to read only propertyconsole.log(INFO);

作用域

1.块级作用域

if、while、for语句形成块级作用域,var声明的变量是没有块级作用域。

<script>if (1) {var a = 9;console.log("1是逻辑值为真");}console.log(a); // 输出9</script>

2.函数作用域

当声明函数时会在函数的内部形成一个函数的作用域,let和const声明的变量有块级作用域和函数作用域,var也不能穿透。所以可以用函数作用域来解决var无块级作用域的缺点。

<script>function b() {var a = 9;}console.log(a); // a is not defined</script>

传值与传址

1.传值

函数传递参数时,基本数据类型的赋值是直接传递值。

2.传址

引用类型的复合对象的赋值是传递地址

严格模式

先声明再使用;必须使用关键词声明变量;匿名函数的this不再指向window对象而是undefined;

运算符

1.数学运算符

let a = 4;let b = 5console.log(3 % 2); // 1,取余;console.log(3 / 2); // 1.5,除法;console.log(a++); // 5,自加运算符,此时a=4,但这条指令执行完成后a+1;console.log(--b); // 6,自减运算符,此时a=6,执行这段代码前b+1;

2.比较运算符

比较运算符分为两个,“==”和``“===”`,双等号的运算符比较时会将等式两边的数据变成相同的数据类型再比较,这个特点叫隐式转换;三等号运算符称为严格比较,不会转变为相同的数据类型来比较,数据类型不同就返回false。

<script>// undefined仅与null和本身相等console.log(0 == undefined); //false;console.log("" == undefined); //false;console.log([] == undefined); //false;console.log({} == undefined); //false;console.log(undefined == null); //true</script>

3.三目运算符

表达式 ? 表达式为真时 :表达式为假时,可以嵌套使用。

循环控制

1.if

if (1) {// 表达式为真的代码块} else {// 表达式为假的代码块}

2.for

for (let i = 0; i < 10; i++) {// 执行代码块}

3.for-in

for-in遍历数组时,根据下标遍历,遍历对象时是根据属性名遍历。

let arr = ["森", "酱", "与", "猪"];let obj = {name: "花森",sex: "男",};// 遍历数组for (const key in arr) {console.log(key); //下标console.log(arr[key]);}// 遍历对象for (const key in obj) {console.log(key); // name sexconsole.log(obj[key]); // 花森 男}

4.for-of

for-of是根据数组内的元素进行遍历,可以遍历迭代器对象。

let arr = ["森", "酱", "与", "猪"]for (const item of arr) {console.log(item); // 森 酱...}

5.forEach

let arr = ["森", "酱", "与", "猪"];arr.forEach((item, index, arr) => {console.log(item); // 元素console.log(index); // 下标console.log(arr); // 原数组});

6.while

var a = 0;while (1) {a++;if (a == 10) {continue; // 跳过a = 10并直接进入a=11循环}console.log(a);if (a == 20) {break; // 跳出结束循环}}

7.switch

不设置break会造成穿透效果

let name = '视频';switch (name) {case '产品':console.log('huasen');break;case '视频':console.log('zhuqi');break;default:console.log('hs');}

8.break和continue

break跳出当前循环体;continue跳过本次循环;通过label跳出父级循环;

var a = 0;while (1) {a++;if (a == 10) {continue; // 跳过a = 10并直接进入a=11循环}console.log(a);if (a == 20) {break; // 跳出结束循环}}

this指向

匿名函数中的this指向的是window对象(严格模式下指向undefined),可以通过作用域链的方式获取到外部的this,同样也可以通过ES6箭头函数取到父级作用域的this指针。

<script>let huasen = {names: "花森",sex: "男",showSex: function () {let that = this;function getName() {console.log(this.names); //this指向window,输出 undefined。console.log(that.names); //that指向huasen,作用域链向上查找。}getName(); //调用getName是一个函数getName,它没有没有依靠,因为匿名函数中的this是指向window,所以调用者就是window,所以this就指向window。},showName: () => {// 箭头函数默然会指向父级作用域,此处调用showName方法的是huasen,它的父级作用域就是window,this指向window。console.log("方法是箭头函数", this);},};huasen.showSex(); // 男huasen.showName();</script>

Call与Apply

函数a.call(对象b,参数1,参数2...),函数a将它的this指针指向对象b,同时传递参数的形式是一个一个传递;函数a.apply(对象b,[参数1,参数2...]),函数a将它的this指针指向对象b,传递参数的形式是数组,两者更改this指向后不会立刻执行函数,bind的方式绑定this会立即执行该函数。

function User(name, type = "人类") {this.name = name;this.type = type;showType = function () {console.log(this.name);};// 默认会返回一个生成后的实例return this;}function Pig(name, type = "猪") {this.name = name;this.type = type;showType = function () {console.log(this.name);};}console.log(new User("花森", "人类"));let zhuqi = {name: "李琦",sex: "女",};// User.call(zhuqi, "猪琦", "猪"); // 通过call将User函数的this指向zhuqi对象,call通过一个一个参数传入,最后返回this再次指向zhuqi,相同属性名会遭到覆盖。User.apply(zhuqi, ["猪琦", "猪"]); // apply的不同点是通过数组直接传入参数,其他效果一致。console.log(zhuqi); //{sex: "女", name: "猪琦", type: "猪"}

Bind

bind()是将函数绑定到某个对象,比如 a.bind(hd) 可以理解为将a函数绑定到hd对象上即 hd.a()。绑定后会立即执行函数,bind是赋值函数行为会返回一个新的函数。

function hs(a, b) {console.log(this);return this.f + a + b;}//使用bind会生成新函数let newFunc = hs.bind({f: 1 }, 3); // 将函数hs绑定给了对象{f:1}//1+3+2 参数2赋值给b即 a=3 b=2console.log(newFunc(2));

数据类型检测

1.typeof

基本数据类型检测,无法辨别数组和对象,合适基本的数据类型辨别,具体可以辨别如下类型:

number/string/boolean;function;object;undefined;

let a = 1;console.log(typeof a); //numberlet b = "1";console.log(typeof b); //string//未赋值或不存在的变量返回undefinedvar huasen;console.log(typeof huasen);function run() {}console.log(typeof run); //functionlet c = [1, 2, 3];console.log(typeof c); //objectlet d = {name: "n.huasenjio.top" };console.log(typeof d); //object

2.instanceof

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,就是它是否存于某一个继承的链,如果是数组,那么他的祖先就必定存在Array构造函数,如此进行精准判断数据类型;

let hs = [];let huasen = {};console.log(hs instanceof Array); //trueconsole.log(huasen instanceof Array); //falselet c = [1, 2, 3];console.log(c instanceof Array); //truelet d = {name: "n.huasenjio.top" };console.log(d instanceof Object); //truefunction User() {}let hd = new User();console.log(hd instanceof User); //true

字面量与对象

当我们声明一个数组let a =[]时,同样可以使用a.push()方法,据我的了解,只有对象才可以调用方法而Array构造函数的prototype原型上面拥有这个push方法,所以推论内部将[]转换成为对象。

let hd = "huasen"; // 字面量形式let h = new String("hs"); // 对象的形式console.log(huasen.length); //6个字符console.log(h.length); //2个字符

Number

Number用于表示整数和浮点数,数字是Number实例化的对象,可以使用对象原型上提供的丰富方法。

1.数字变量的命名

let huasen = 3; //字面量形式let h = new Number(3); //对象的形式console.log(h+3); //6

2.判断书否是整数

console.log(Number.isInteger(1.2));

3.NaN无效数值

console.log(Number("huasen")); //声明传参传递数字无效产生NaNconsole.log(2 / 'huasen'); //无效的数值计算会产生NaNNumber.isNaN(变量) // 判断是否是NaNObject.is(对象1,对象2) // 判断两个对象是否相等

4.类型转换

使用Number构造函数基本上可以转换所有类型

console.log(Number('huasen')); //NaNconsole.log(Number(true)); //1console.log(Number(false));//0console.log(Number('9')); //9console.log(Number([])); //0console.log(Number([5]));//5console.log(Number([5, 2])); //NaNconsole.log(Number({})); //NaN

5.常用方法

1)字符串转数字parseInt:

提取的字符串并去除空白数字字串转变为整数

console.log(parseInt('1' * 1));//1 隐式转换console.log(parseInt(' 99huasen'));//99console.log(parseInt('18.55'));//18 小数点被忽略

2)字符串转为浮点数parseFloat:

console.log(parseFloat(' 99huasen'));//99console.log(parseFloat('18.55'));//18.55且不会忽略小数点

3)浮点数四舍五入toFixed:

console.log(1.55667.toFixed(2)); //1.56

String

字符串类型是使用非常多的数据类型

1.字符串变量的声明

let hs = new String('huasen');// 获取字符串长度console.log(hs.length);// 获取字符串console.log(hs.toString());

2.转义符号

有些字符有双层含义,需要使用\转义符号进行含义转换,下例中引号为字符串边界符,如果输出引号时需要使用转义符号。

let content = '花森 \'huasenjio.top\''; // 内部两个'是需要转义console.log(content); // 花森 'huasenjio,top'

常见转义符号表:

3.连接运算符

let year = ,name = '花森导航';console.log(name + '成立于' + year + '年');

4.模板字面量

使用 ` …` 符号包裹的字符串中可以写入引入变量与表达式,变量使用${}放入,并且可以换行而不产生错误。

let url = 'huasenjio.top';console.log(`花森导航网址是${url}`); //花森导航网址是huasenjio.top

5.标签模板

通过tag方法将字符串和变量分离到两个数组中

let lesson = 'css';let web = '花森';tag `访问${web}学习${lesson}前端知识`;function tag(strings, ...values) {console.log(strings); //["访问", "学习", "前端知识"]console.log(values); // ["花森", "css"]}

6.常用方法

1)使用length属性可以获取字符串长度:

console.log("huasenjio.top".length)

2)字母大小写转换(toLowerCase和toLowerCase):

console.log('HUASENJIO.top'.toLowerCase()); //huasenjio.top 转小写console.log('huasenjio.top'.toLowerCase()); //HUASENJIO.TOP 转小写

3)移除字符空白trim:

let str = ' huasenjio.top ';console.log(str.length);console.log(str.trim().length);console.log(name.trimLeft()); // 删除左空白console.log(name.trimRight()); // 删除又空白

4)抓取单个字符chatAt:

console.log('huasenjio'.charAt(3)) // s 通过下标(0开始)获取到对于的字符

5)数组形式获取字符:

console.log('huasenjio'[3]) // 3 数字索引获取字符串

7.字符串的截取

slice

截掉

let hs = "12345678";console.log(hs.slice(3)); //45678 slice(截掉的个数)

截取

// 参数为正数情况let hs = "12345678";console.log(hs.slice(3, 6)); //456 [开始下标,结束下标)// 参数为负数情况console.log(hs.slice(-2)); //78 末尾取两个console.log(hs.slice(1, -2)); //23456 [开始下标,-2(倒数第二个开始)) 第二个参数是负数说明从尾部数

substring

截掉

let hs = "12345678";console.log(hs.substring(3)); //45678 substring(截掉的个数)

截取

let hs = "12345678";console.log(hs.substring(3, 6)); //456 [开始下标,结束下标)console.log(hs.substring(3, 0)); //123 参数中选最小值作为开始下标进行截取console.log(hs.substring(3, -9)); //123 负数转为0再按最小值作为开始下标进行截取

substr

截掉

let hs = "12345678";console.log(hs.substr(3)); //45678 substr(截掉的个数)

截取

let hs = "12345678";console.log(hs.substr(3, 4)); //4567 [开始下标,截取个数]console.log(hs.substr(-3, 2)); //78 [-3,个数]

8.查找字符串

indexOf检索

从开始(下标为零)进行查找,若查找到返回第一个目标的下标,查找不到返回-1,可以指定开始查找的位置。

console.log("12345678".indexOf("3")); //1console.log("12345678".indexOf("5", 3)); //4 从第3个字符向后搜索

search检索

search() 方法用于检索字符串中指定的子字符串,也可以使用正则表达式搜索进行搜索,返回子串的开始下标。

let str = "huasenjio.top";console.log(str.search("top"));console.log(str.search(/top/i));

9.字符串替换

replace

replace方法用于字符串的替换操作,默认替换第一个匹配的字符串,如果想要通过全局替换需要配合正则表达式使用。

let name = "1122331122";web = name.replace("22", "**");console.log(web);// 配合正则完成替换let str = "/02/12";console.log(str.replace(/\//g, "-"));

10.重复生成repeat

通过已有的字符串生成重复的字符串

function star(num = 3) {return '*'.repeat(num);}console.log(star());

11.字符串分割split

通过某一个符号对字符串进行分割并返回分割后的数组

console.log("1,2,3".split(",")); //[1,2,3]

12.类型转换

let hs = 99;console.log(hs.toString()); //99 String类型let arr = ["1", "2", "3"];console.log(arr.toString()); //1,2,3 String类型let a = {name: "huasen",};console.log(a.toString()); //[object Object] String类型

Math

math数学对象,提供了很多数学相关的计算方法。

1.最大值最小值

console.log(Math.min(1, 2, 3)); // 1console.log(Math.max(1, 2, 3)); // 3

2.向上取整ceil

console.log(Math.ceil(1.111)); // 2

3.向下整数floor

console.log(Math.floor(1.555)); // 1

4.四舍五入处理round

console.log(Math.round(1.5)); //2

5.生成随机数random

random方法用于返回 >=0 且 <1 的随机数(包括0但不包括1)

const number = Math.floor(Math.random() * 5); // [0,5)console.log(number);const number = Math.floor(Math.random() * (5+1)); // [0,5]console.log(number);const number = Math.floor(Math.random() * (5 - 2)) + 2; // [2,5)console.log(number);const number = Math.floor(Math.random() * (5 - 2+1)) + 2; // [2,5]console.log(number);X+Math.floor(Math.random()*(Y-X)) // [X,Y)X+Math.floor(Math.random()*(Y-X+1)) // [X,Y]

Date

处理日期的方法,通过Date类型提供的丰富功能可以非常方便的操作。

1.时间戳

定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数

2.获取当前日期

let now = new Date();console.log(now); // Mon Oct 19 21:15:23 GMT+0800 (中国标准时间)console.log(typeof now); //objectconsole.log(now * 1); //获取时间戳//直接使用函数获取当前时间console.log(Date());console.log(typeof Date()); //string//获取当前时间戳单位毫秒console.log(Date.now());

3.时间格式的封装

function dateFormat(date, format = "YYYY-MM-DD HH:mm:ss") {const config = {YYYY: date.getFullYear(),MM: date.getMonth() + 1,DD: date.getDate(),HH: date.getHours(),mm: date.getMinutes(),ss: date.getSeconds(),};for (const key in config) {format = format.replace(key, config[key]);}return format;}console.log(dateFormat(new Date(), "YYYY年MM月DD日HH时mm分ss秒"));

Boolean

布尔类型包括truefalse两个值

1.声明定义

console.log(new Boolean(true)); //true 对象形式let hs =true; // true 字面量形式

2.隐式转换

逻辑运算符"=="进行两个是比较是否相等时,不同的数据类型会造成隐式转换后再比较,属于js中比较难的部分,具体情况如下:

数组和布尔值比较,[] == true;//false,空数组转为""再转为0,逻辑值true直接转为1;数组符串比较,[1,2,3] == '1,2,3' // true,[1,2,3]转换成字符串"1,2,3"最后跟字符串比较;字符串和数字进行比较,'1' == 1 // true,字符串转为数字后与数字1比较;字符串和布尔值进行比较,'1' == true; // true,字符串转为数字1,布尔值转为数字1,比较相等;布尔值和数字比较,true == 1;/ true,布尔值转为数字1再比较;

有趣的事情是[] == false和![] == false的结果都是true,第一个[]数组转为""再转成0,false直接转为0,比较相等;第二个同理,!符号优先级高所以先执行,所以式子就变成(![]) == false,估计就能理解了!另外有几个比较的特殊undefined == null // trueNaN == XXX //NaN和任意类型都不等(包括自己)

3.显示转换

使用!!转换布尔类型

let hs = "";console.log(!!hs); //falsehs = 0;console.log(!!hs); //falsehs = null;console.log(!!hs); //falsehs = new Date("-2-22 10:33");console.log(!!hs); //truehs = [];console.log(!!hs); //falsehs = {};console.log(!!hs); //false

Array

数组是多个变量值的集合,数组是Array对象的实例,Array原型上系统预设很多方式可以供我们调用。

1.数组的声明

console.log(new Array(1, '花森', 'hs')); //[1, "花森", "hs"]const array = ["花森", "huasen"];const array = [["花森","hs"], ["猪琦","zhuqi"]];console.log(array[1][0]); // 猪琦let hs = Array.of(3);console.log(hs); // [3]hs = new Array(3); // 创建长度为3的空数组console.log(hs);hs = Array.of(1, 2, 3);console.log(hs); //[1, 2, 3]

2.类型检测isArray

console.log(Array.isArray([1, "花森", "hs"])); //trueconsole.log(Array.isArray(9)); //false

3.类型转换

数组2字符串

大部分数据类型都可以使用.toString()函数转换为字符串

console.log(([1, 2, 3]).toString()); // 1,2,3 toSring()console.log(String([1, 2, 3])); //1,2,3 构造函数console.log([1, 2, 3].join("-"));//1-2-3 join()

伪数组2数组

使用Array.from可将类数组转换为数组,类数组指包含length属性或可迭代的对象,伪数组不具备数组中的某一个方法,一般使用时都要转为数组再进行使用。

let str = '花森酱';console.log(Array.from(str)); //["花", "森", "酱"]let user = {0: '花森酱','1': 18,length: 2};console.log(Array.from(user)); //["花森酱", 18]let divs = document.querySelectorAll("div");let hs = [...divs]

字符串2数组

使用join,将数组中的元素连接成字符串。

let price = "99,78,68";console.log(price.split(",")); //["99", "78", "68"]

4.数组合并拆分

展开语法

使用展开语法来合并数组相比concat要更简单,使用...可将数组展开为多个值。

et a = [1, 2, 3];let b = ['a', '花森', ...a];console.log(b); //["a", "花森", 1, 2, 3]

concat

concat方法用于连接两个或多个数组,返回是一个数组,元素是值类型的是复制操作,如果元素是引用类型还是指向同一对象,属于浅拷贝的操作。

let arr = ["hs", "花森"];let hs = [1, 2];let cms = [3, 4];console.log(arr.concat(hs, cms)); //["hs", "花森", 1, 2, 3, 4]

copyWithin

使用copyWithin从数组中复制一部分到同数组中的另外位置,语法说明array.copyWithin(target, start, end),target复制到的索引地址位置,start元素复制的开始索引,end结束的索引。

const arr = [1, 2, 3, 4,5];console.log(arr.copyWithin(2, 0, 2)); //[1, 2, 1, 2, 5]

5.解构赋值

解构是一种更简洁的赋值特性,可以理解为分解一个数据的结构。

//数组使用let [name, url] = ['花森', 'hs'];console.log(name);// 解构函数返回值function huasen() {return ['hs', '花森'];}let [a, b] = huasen();console.log(a); //hs// 函数参数解构function hs([a, b], c) {console.log(a, b);console.log(c);}hs(["花森", "hs"], "猪琦");// 解构数组let [a, ...b] = ['花森', 'hs', '猪琦'];console.log(b); // ['hs', '猪琦'];// 解构字符串"use strict";const [...a] = "huasenjio";console.log(a); //Array(9)// 解构单个变量let [,url]=['花森','huasenjio'];console.log(url);//huasenjio

6.操作元素

// 数组追回元素let arr = [1, "花森", "hs"];arr[arr.length] = "huasenjio";console.log(arr); //[1, "花森", "hs", "huasenjio"]// push批量压入元素,改变数组长度,返回值是元素的数量。let arr = [1, "花森", "hs"];arr.push("huasenjio","zhuqi");console.log(arr); //[1, "花森", "hs", "huasenjio","zhuqi"]// pop尾弹一个元素,改变数组长度,返回被弹出的元素。let arr = [1, "花森", "hs"];arr.pop();console.log(arr); //[1, "花森"]// shift头弹一个元素,改变数组长度,返回头弹出的元素。let arr = [1, "花森", "hs"];console.log(arr.shift());console.log(arr); //["花森","hs"]// unshift头插一个元素,改变数组长度,返回值是元素的数量。let arr = [1, "花森", "hs"];console.log(arr.unshift("爱"));console.log(arr); //["花森","hs"]// fill填充数组,返回填充完成的数组。console.log(new Array(4).fill("hs")); //["hs", "hs", "hs", "hs"]

7.数组截取

slice截取

使用slice方法从数组中截取部分元素组合成新数组(并不会改变原数组),不传第二个参数时截取到数组的最后元素。不设置参数默认截取整个数组。

let arr = [0, 1, 2, 3, 4, 5, 6];console.log(arr.slice(1, 3)); // [1,2] [开始下标,结束下标]let str = "0123456";console.log(str.slice(1, 3)); // 12 [开始下标,结束下标)

splice截掉

使用splice方法可以添加、删除、替换数组中的元素,会对原数组进行改变,返回值为删除的元素。

// [开始下标,删除数量]let arr = [0, 1, 2, 3, 4, 5, 6];console.log(arr.splice(1, 3)); //返回删除的元素 [1, 2, 3] console.log(arr); //删除数据后的原数组 [0, 4, 5, 6]// 通过修改length删除最后一个元素let arr = ["1", "2","3","4","5"];arr.length = arr.length - 1;console.log(arr);// [开始下标,删除参数,删除位置的新元素]let arr = [0, 1, 2, 3, 4, 5, 6];console.log(arr.splice(1, 3, 8)); //返回删除的元素 [1, 2, 3]console.log(arr); //删除数据后的原数组 [0, 4, 5, 6]// 两个交换位置小案例function move(array, before, to) {if (before < 0 || to >= array.length) {console.error("指定位置错误");return;}const newArray = [...array];const elem = newArray.splice(before, 1);newArray.splice(to, 0, ...elem);return newArray;}const array = [1, 2, 3, 4];console.table(move(array, 0, 3));

8.数组清空

// 更改指针法let user = [{name: "hs" }, {name: "花森" }];let cms = user;user = [];console.log(user); // []console.log(cms); // [{...},{...}]// 修改长度法let user = [{name: "hs" }, {name: "花森" }];user.length = 0;console.log(user);// splice方法删除所有数组元素let user = [{name: "hs" }, {name: "花森" }];user.splice(0, user.length);console.log(user);

9.元素查找

indexOf

使用 indexOf 从前向后查找元素出现的位置,如果找不到返回 -1,indexOf使用严格模式去匹配,找到则返回第一次出现的下标。

let arr = [7, 3, 2, 8, 2, 6];console.log(arr.indexOf(2)); // 2 从前往后查找第一个出现2的下标// 指定索引向后进行查找let arr = [7, 3, 2, 8, 2, 6];console.log(arr.indexOf(2, 3)); //4 从第二个元素开始向后查找

lastIndexOf

使用lastIndexOf从后向前查找元素出现的位置,具体使用参考indexOf方法使用。

let arr = [7, 3, 2, 8, 2, 6];console.log(arr.lastIndexOf(2)); // 4 从后查找2出现的位置

includes

使用includes查找字符串返回值是布尔类型更方便判断

let arr = [7, 3, 2, 6];console.log(arr.includes(8)); //true

find

find 方法找到后会把值返回出来,找不到则返回值为undefined,返回第一次查找到的词就停止查找。

let arr = [{name: "猪琦", sex: "女" },{name: "花森", sex: "男" },{name: "皮卡丘", sex: "动物" },];let find = arr.find(function (item) {return item.name == "花森";});console.log(find); // {name: "花森", sex: "男"}

10.数组顺序

reverse

对数组进行反转输出

let arr = [1, 4, 2, 9];console.log(arr.reverse()); //[9, 2, 4, 1]

sort

sort方法每次使用两个值进行比较,Array.sort((a,b) => a-b),返回负数a排在b的前,从小到大排序;返回正数则b排在a的前面,返回0时不动。

// 默认从小到大排序let arr = [1, 4, 2, 9];console.log(arr.sort()); //[1, 2, 4, 9]// 按某值进行排序console.log(arr.sort(function (v1, v2) {return v2 - v1;})); //[9, 4, 2, 1]

11.数组拓展函数

every一假则假

every用于递归的检测元素,要所有元素操作都要返回真结果才为真,遇到一个不满足条件就立即退出,不再继续遍历下去,最终返回布尔值。

const user = [{name: "李四", js: 89 },{name: "马六", js: 55 },{name: "张三", js: 78 },];const resust = user.every((item, index, arr) => {console.log(item); // 元素console.log(index); // 下标console.log(arr); // 原数组item.js >= 60;});console.log(resust);

some一真则真

使用some函数可以递归的检测元素,如果有一个返回true。

const user = [{name: "李四", js: 89 },{name: "马六", js: 55 },{name: "张三", js: 78 },];const resust = user.some((item, index, arr) => {console.log(item); // 元素console.log(index); // 下标console.log(arr); // 原数组item.js >= 60;});console.log(resust);

filter

按一定条件筛选元素并组成新数组返回

const user = [{name: "李四", js: 89 },{name: "马六", js: 55 },{name: "张三", js: 78 },];const resust = user.filter((item, index, arr) => {console.log(item); // 元素console.log(index); // 下标console.log(arr); // 原数组return item.js > 60; // 过滤取到js成绩大于60的元素并返回一个新的数组});console.log(resust);

map

将遍历数组,遍历过程中执行代码,最后返回一个元素添加到新数组中并返回。

const user = [{name: "李四", js: 89 },{name: "马六", js: 55 },{name: "张三", js: 78 },];const resust = user.map((item, index, arr) => {console.log(item); // 元素console.log(index); // 下标console.log(arr); // 原数组item.js > 60;return item.js + "分数";});console.log(resust);

reduce

使用reducereduceRight函数可以迭代数组的所有元素,reduce从前开始reduceRight从后面开始,存在如下参数。

const user = [{name: "李四", js: 89 },{name: "马六", js: 55 },{name: "张三", js: 78 },];const resust = user.reduce((pre, cur, index, arr) => {console.log(cur); // 当前元素console.log(index); // 下标console.log(pre); // 上一个遍历的元素return "返回值"; // 返回值将作为pre参数}, 0); // 0指定pre的初始值//计算所有js成绩的和const user = [{name: "李四", js: 89 },{name: "马六", js: 55 },{name: "张三", js: 78 },];let t = 0;const resust = user.reduce((pre, cur, index, arr) => {console.log(cur); // 当前元素console.log(index); // 下标console.log("pre", pre); // 上一个遍历的元素return (pre += cur.js); // 返回值将作为pre参数}, 89);

迭代器

数组中可以使用多种迭代器方法

keys

通过迭代对象获取索引,可通过next()一步一步向下拿到全部索引并执行操作。

const hs = ["花森", "huasen","猪琦"];const keys = hs.keys(); // 获得迭代器对象console.log(keys.next()); // {value: 0, done: false}console.log(keys.next()); // {value: 1, done: false}// 需要把上面两行console.log去掉,因为上面两行迭代器就已经遍历到最后了,所以影响输出效果。for (const key of keys) {console.log(key);}

values

通过迭代对象获取值,可通过next()一步一步向下拿到全部索引并执行操作。

entries

返回数组所有键值对的迭代器,可以使用解构加for-of循环获取数据。

const arr = ["a", "b", "c", "花森"];for (const [key, value] of arr.entries()) {console.log(key, value);}

Symbol

Symbol 的值是唯一的数据类型,防止使用属性名冲突而产生,比如向第三方对象中添加属性就有可能产生变量名冲突,而且Symbol不可以添加属性。

1.变量声明

let hs = Symbol();let edu = Symbol("传入描述Symbol的字符串,有利于分辨Symbol!");console.log(hs); // symbolconsole.log(edu); // Symbol(传入描述Symbol的字符串,有利于分辨Symbol!)console.log(edu.description) // 获取描述console.log(hs == edu); //false

2.查找Symbol.for

根据描述获取到唯一Symbol变量,如果不存则新建一个Symbol对象,使用Symbol会被系统登记,而使用Symbol不会被系统登记。

let hs = Symbol.for("传入描述Symbol的字符串,有利于分辨Symbol!");let edu = Symbol.for("传入描述Symbol的字符串,有利于分辨Symbol!");console.log(hs == edu); // true

3.登记记录Symbol.keyFor

返回Symbol登记的描述,如果没有找到则返回undefined

let hs = Symbol.for("花森");console.log(Symbol.keyFor(hs)); //花森

4.操作要点

Symbol可以保证对象属性的唯一,Symbol的声明和访问使用[]的形式进行操作,不能使用.,因为点是操作字符串属性的写法。

let symbol = Symbol("花森");let obj = {[symbol]: "huasenjio"};console.log(obj[symbol]); //huasenjio

5.实例操作

覆盖耦合

通过Symbol作为键值,每一个Symbol均是唯一,所以存入数据不会因为key值相同造成覆盖的可能。

class Cache {static data = {};static set(name, value) {this.data[name] = value;}static get(name) {return this.data[name];}}let user = {name: "花森",key: Symbol("343434"),};let cart = {name: "猪琦",key: Symbol("121212"),};Cache.set(user.key, user);Cache.set(cart.key, cart);console.log(Cache.get(user.key));console.log(Cache.data);

遍历属性

Symbol 不能使用for/in和for/of遍历操作,遍历时Symbol属性默认被忽略,但是可以通过Object.getOwnPropertySymbols,遍历到对象中的所有Symbol属性。

let symbol = Symbol("导航");let user = {name: "花森",[symbol]: "huasenjio",};for (const key of Object.getOwnPropertySymbols(user)) {console.log(key);}let symbol = Symbol("导航");let user = {name: "花森",[symbol]: "huasenjio",};for (const key of Object.getOwnPropertySymbols(user)) {console.log(key);}

变量保护

使用symbol作为属性名,起到变量保护的作用,无法被遍历访问到,可以通过对外函数返回的方式去访问。

const site = Symbol("网站名称");class User {constructor(name) {this[site] = "森酱";this[name] = name;}getName() {return `${this[site]}-${this.name}`;}}const hs = new User("hs");console.log(hs.getName());console.log(hs.site); // 无法访问到森酱for (const key in hs) {console.log(key);}

Set

无论是基本类型还是对象引用,都不能存入重复的元素,只可以保存值而没有键名;严格类型检测入字符串不等于数值型数字;元素唯一。

1.变量声明

使用数组作为初始化的数据,如果存在多个相同值则仅会保存第一个值,其余的全部忽略。

let hs = new Set(["花森", "hs", "1", 1]); console.log(hs.values()); //{"花森", "hs", "1" ,1}

2.常用操作

add添加元素

let hs = new Set();hs.add('huasen');

size获取数量

let hs = new Set(['huasen', '花森']);console.log(hs.size); //2

has检测元素

let hs = new Set(['huasen', '花森']);console.log(hs.has("花森")); // true

delete删除

console.log(hs.delete("hdcms")); //true

clear清空元素

hs.clear();

3.数组转换

const set = new Set(["hs", "花森"]);console.log([...set]) // 点语法console.log(Array.from(set)); // array.from

4.数组去重

console.log([...new Set("hhssjjoo")].join(""));//hsjo

5.遍历数据

let arr = [7, 6, 2, 8, 2, 6];let set = new Set(arr);//使用forEach遍历set.forEach((item,key) => console.log(item,key)); // item 和 value一致//使用for-offor (const iterator of set) {console.log(iterator);}

6.交集

通过数组的过滤函数filter和has查询,

let hs = new Set(["1", "8"]);let zhuqi = new Set(["2", "8"]);let newSet = new Set([...hs].filter((item) => zhuqi.has(item))); // 返回zhuqi数据中也有的数console.log(newSet); //{"8"}

7.差集

let hs = new Set(["1", "8"]);let zhuqi = new Set(["2", "8"]);let newSet = new Set([...hs].filter((item) => !zhuqi.has(item))); // 返回zhuqi数据中没有的数console.log(newSet);

8.交集

let hs = new Set(["1", "8"]);let zhuqi = new Set(["2", "8"]);let newSet = new Set([...hs,...zhuqi]); // 返回zhuqi数据中没有的数console.log(newSet);

9.WeakSet

WeakSet结构同样不会存储重复的值,储存的元素必须是对象类型,垃圾回收机制不考虑WeakSet,当一个变量被引用时,内存中会有一个引用计数器会进行加一,但对于weakset引用,计数器不会加一,所以weakset不管是否在使用变量都均会被删除,是属于弱引用的范畴,weakset没有keys(),values(),entries()和size等方法,且不能被遍历!

声明

new WeakSet(["hs", "zhuqi"]); //Invalid value used in weak setnew WeakSet("hdcms"); //Invalid value used in weak set new WeakSet([{name: "huasen" }]); //正确

基本操作

const hs = new WeakSet();const arr = ["hdcms"];//添加操作hd.add(arr);console.log(hs.has(arr));//删除操作hd.delete(arr);//检索判断console.log(hs.has(arr));

Map

Map是一组键值对的结构,用于解决以往不能用对象做为键的问题,具有极快的查找速度,函数、对象、基本数据类型均可以作为键和值。其中键是对象则保存的是内存地址,如果值相同但内存地址不同也会被视为两个键值对。

1.声明定义

可以接受数组作为参数,该数组的成员是一个表示键值对的数组。

let m = new Map([["h", "花"],["s", "森"],]);console.log(m.get("h")); //花

2.基本操作

let m = new Map([["h", "花"],["s", "森"],]);// 获取键值对的数量console.log(m.size);// 读取元素console.log(m.get("h")); //花// 删除元素console.log(m.delete("h")); //花// 清空mapconsole.log(map.clear());

3.遍历数据

可以使用keys/values函数遍历键与值

let hs = new Map([["h", "花"], ["s", "森"]]);for (const key of hs.keys()) {console.log(key);}for (const value of hs.values()) {console.log(value);}

4.数组转换

[…map];Array.from(map);

5.WeabMap

详细请参考WeabSet的用法

函数进阶

函数是将复用的代码块封装起来的模块,js中的函数也是对象,可以通过构造函数Function创建实例,全域定义的函数是属于window对象中,容易造成覆盖的问题,使用let/const定义的参数时不会压入window对象中。标准声明的函数优先级更高,解析器会优先执行提取放在代码树的顶端,所以同意作用域下函数声明的位置顺序不限制。

1.声明定义

// 构造函数的方式let hs = new Function("title", "console.log(title)");hd('花森酱');// 标准语法的方式function hs(num) {return ++num;}console.log(hs(3));// es6的箭头函数let huasen = () => {}// 简写形式user = {setName() {}}

2.匿名函数

匿名函数需要赋值给某一个变量,而且一定需要以;结尾,匿名函数的执行者是window对象即内部的this是指向window对象,严格模式下this指向undefined而不是window对象。

let hs = function () {console.log(this); // window对象};hs();[].map(()=>{})

3.立即执行函数

立即执行函数指函数定义时立即执行,可以定义私有作用域防止污染全局作用域。

"use strict";(function () {var web = 'hs';})();console.log(web); //web is not defined

4.函数参数

形参实参

调用函数时传入的变量叫做实参,定义函数参数是的变量叫做形参。当形参的数量大于实参时,没有被赋值的形参就被定义为undefined;实参的数量大于形参时,多余的实参将被忽略并且不会报错。

// n1,n2 为形参function sum(n1, n2) {return n1+n2;}// 参数 2,3 为实参console.log(sum(2, 3)); //5

默认参数

通常为形参设置默认值

// 无序考虑兼容function avg(total, year) {year = year || 1; // 赋值return Math.round(total / year);}console.log(avg(2000, 3));//es6写法需要考虑兼容性function avg(total, year = 1) {return Math.round(total / year);}console.log(avg(2000, 3));

回调函数

函数调用时可以传递函数参数,一般用于回调函数,执行调用的函数a时,默认执行传入的函数,这个场景叫做回调函数。

// 箭头函数演示[].filter(()=> {})// 普通函数[].filter(function(){})

argument

arguments 是函数获得到所有参数集合,获得传入参数的集合。

// 普通参数let hs = function () {console.log(arguments); // 函数参数};hs(1, 2, 3, 4);// 对参数进行求和function sum() {return [...arguments].reduce((total, num) => {return (total += num);}, 0);}console.log(sum(2, 3, 4, 2, 6)); //17

5.递归调用

递归指函数内部调用自身的方式,主要用于数量不确定的循环操作,必须有要结束循环的条件否则会陷入死循环,一般慎用递归调用,容易造成内存泄露的风险。

function factorial(sum) {if (sum <= 1) {return sum;} else {return sum * factorial(--sum);}}console.log(factorial(4));

6.标签函数

function hd(str, ...values) {console.log(str); //["站点", "-", "", raw: Array(3)]console.log(values); //["花森", "huasenjio.top]}let name = "花森",url = "huasenjio.top";hd`站点${name}-${url}`;

作用域

作用域链只向上查找,找到全局window即终止。当在每一个对象中引用了一个变量是,如果挡墙作用域不存在就往上级作用域查找,[].toString(),可以调用输出字符串,细心的小伙伴可能发现,数组的原型根本没有toString方法,但是Object上面存在toString方法。所以查找toString变量的顺序是先去找数组的原型,再去找在Object上的原型,直到window全局,如果还没有找到则报错。

console.log(name); // 直接这样输出并不会报错因为window上默认存在name属性且为空console.log(n); // n is not defined 因为window上面没有n属性

对象

面向对象程序设计成为OOP,对象是属性和方法的集合体,内部的复杂逻辑代码被隐藏,仅仅暴露少量代码给外界,更改对象内部的复杂逻辑不会对外部造成影响和抽象,继承是通过代码复用减少冗余,根据不同形态的对象产生不同的结果称之为多态。

1.声明定义

let obj = {name: 'huasen',get:function() {return this.name;}}console.log(obj.get()); //huasen

2.属性管理

基本操作

let user = {name: "huasen",["my-title"]: "花森导航",};console.log(user.name); // 使用点语法console.log(user["name"]); // 使用数组形式console.log(user["my-title"]); // 如果属性名不是合法变量就必须使用括号的形式// 添加属性user.a = "A" // 使用点语法user["b"] = "B" // 使用数组形式// 删除属性delete user.name;

检测属性

1)hasOwnProperty检测对象自身是否包含指定的属性,不检测原型链上继承的属性。

let obj = {name: '花森'};console.log(obj.hasOwnProperty('name')); //true

2)使用in可以在原型对象上检测。

let obj = {name: "花森"};let hs = {web: "huasenjio"};//设置hs为obj的新原型Object.setPrototypeOf(obj, hs);console.log(obj);console.log("web" in obj); //true 说明web属性在obj的原型链上console.log(obj.hasOwnProperty("web")); //false

assign属性合并

使用Object.assign静态方法进行从一个或多个对象复制属性,并将属性合并,但是是浅拷贝。

"use strict";let hs = {a: 1, b: 2 };hd = Object.assign(hs, {f: 1 }, {m: 9 });console.log(hs); //{a: 1, b: 2, f: 1, m: 9}

动态属性

对象属性可以通过表达式计算定义,动态设置属性或者执行方法时很美妙。

let id = 0;const user = {[`id-${id++}`]: id,[`id-${id++}`]: id,[`id-${id++}`]: id};console.log(user);

3.引用特性

对象和函数一样是引用数据类型,赋值操作相当于复制并赋予地址,其实还是引用同一块内存地址。

let user = {name: "huasen",["tit-le"]: "花森导航",};let per = user;console.log(per);per.name = "猪琦";console.log(user); // user中也被修改了// 函数参数同样也是赋值地址(function () {arguments[0]["tit-le"] = "笔录";})(user);

4.对象转换

对象直接参与计算时,系统根据计算的场景在string、number、default之间转换。如果场景需要字符串类型,则对象执行toString()后执行valueOf获取到字符串,如果需要字符串型,先执行valueOf获得数值后执行toString获得字符串。

let hs = {name: "花森",num: 1,valueOf: function () {console.log("valueOf");return this.num;},toString: function () {console.log("toString");return this.name;},};console.log(hs + 3); //valueOf 4console.log(`${hs}导航`); //toString 花森导航

5.解构赋值

解构是一种更简洁的赋值特性,可以理解为分解一个数据的结构,用法与数组的解构相似,建议解构使用var/let/const声明,否则严格模式下会报错。

// 对象的解构let info = {name:'花森',url:'huasenjio'};let {name:n,url:u} = info // 重新定义变量名为n uconsole.log(n); // 如果属性名与变量相同可以省略属性定义let {name:n,url:u} = {name:'花森',url:'huasenjio'};// 函数返回值解构到变量function hs() {return {name: '花森',url: 'huasenjio'};}let {name: n,url: u} = hs();console.log(n);// 函数传参"use strict";function hd({name, age }) {console.log(name, age); //花森 18}hd({name: "花森", age: 18 });

6.默认值

let [name, site = '花森'] = ['hs'];console.log(site); //花森

7.遍历对象

keys/values/entries

const hs = {name: "花森",age: 10};console.log(Object.keys(hs)); //["name", "age"] 获取对象属性名组成的迭代器console.log(Object.values(hs)); //["花森", 10] 获取对象属性组成的迭代器console.table(Object.entries(hs)); //[["name","花森"],["age",10]] 两个迭代器

for/of遍历迭代器

For-of是不可以直接遍历对象,它是用于遍历迭代对象。

const hd = {name: "后盾人",age: 10};for (const key of Object.keys(hd)) {console.log(key);}

8.对象的拷贝

浅拷贝

// for-in的方式遍历对象进行浅拷贝let obj = {name: "花森"};let hs = {};for (const key in obj) {hs[key] = obj[key];}hs.name = "huasen";console.log(hs);console.log(obj);// Object.assign进行简单的浅拷贝但同名属性将会被覆盖Object.assign(hs, obj);hs.name = "huasen";console.log(hs);console.log(obj);// 展开语法let hs = {...obj };

深拷贝

浅拷贝不会将深层的数据复制,深拷贝完全就是复制出一个新的对象,两个对象完全独立。

let obj = {name: "花森",user: {name: "hs",},data: [],};function copy(object) {let obj = object instanceof Array ? [] : {}; // 判断是数组或者对象进行声明变量// 解构获得键值对for (const [k, v] of Object.entries(object)) {obj[k] = typeof v == "object" ? copy(v) : v; // 如果当前属性是引用类型则递归调用,基础数据类型则直接赋值。}return obj; // 返还对象}let huasen = copy(obj);huasen.data.push("zhuqi");console.log(JSON.stringify(huasen, null, 2));console.log(JSON.stringify(obj, null, 2));

9.构造函数

工厂模式

普通函数中返还一个相同结构的对象,修改工厂模式的方法会影响所有的同类对象,且声明不需要new关键词。

function stu(name) {return {name,show() {console.log(this.name); // this代表函数调用者}};}const lisi = stu("李四");lisi.show();const hs = stu("huasen");hs.show();

构造函数

构造函数的函数名首字母需要大写的命名规范,this指向当前创建的对象,系统会自动返回this关键词,但也可以收到return返回,手动返回必须是对象,不然setter方法会屏蔽且需要new关键词生成对象。

function Student(name) {this.name = name;this.show = function() {console.log(this.name);};// return this; // 系统会自动返回}const lisi = new Student("李四");lisi.show();const xj = new Student("王五");wangwu.show();

10.属性特征

查看特征

使用Object.getOwnPropertyDescriptor查看对象属性的描述

let obj = {name: "花森",user: {name: "hs",},data: [],};console.log(JSON.stringify(Object.getOwnPropertyDescriptor(obj, "name"), null, 2));// {// "value": "花森",// "writable": true,// "enumerable": true,// "configurable": true

设置属性

使用Object.defineProperty方法修改属性特性

// 禁止遍历修改删除"use strict";const user = {name: "花森",age: 18};Object.defineProperty(user, "name", {value: "花森",writable: false,enumerable: false,configurable: false});// 一次性修改多个属性Object.defineProperty(user, {name: {value: "花森", writable: false},age: {value: 18,enumerable: false}});

禁止添加

Object.preventExtensions禁止向对象添加属性,Object.isExtensible(user)可以判断是否可以向属性中添加属性。

"use strict";const user = {name: "花森"};Object.preventExtensions(user);user.age = 18; //Error

封闭对象

Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为configurable: false,可以通过isSealed检测是否发生封闭。

冻结对象

Object.freeza 冻结的对象不允许添加、删除、修改、writable、configurable都会被标记为false

"use strict";const user = {name: "花森"};Object.freeze(user);user.name = "花森"; //Error

12.属性访问器

getter方法用于获得属性值,setter方法用于设置属性,这是JS提供的存取器特性即使用函数来管理属性。用于避免错误的赋值,需要动态检测值的改变。属性只能在访问器和普通属性选择一个,不能共同存在。

getter/setter

"use strict";const user = {data: {name: "花森", age: null },set age(value) {if (typeof value != "number" || value > 100 || value < 10) {throw new Error("年龄格式错误");}this.data.age = value;},get age() {return `年龄: ${this.data.age}`;},};user.age = 18; // 不能随便赋值console.log(user.age);

内部私有属性

"use strict";const user = {get name() {return this._name;},set name(value) {if (value.length <= 3) {throw new Error("用户名不能小于三位");}this._name = value;},};user.name = "花森酱酱";console.log(user.name);

访问器描述符

使用defineProperty定义私有属性

// 函数写法function User(name, age) {let data = {name, age };Object.defineProperties(this, {name: {get() {return data.name;},set(value) {if (value.trim() == "") throw new Error("无效的用户名");data.name = value;}},age: {get() {return data.name;},set(value) {if (value.trim() == "") throw new Error("无效的用户名");data.name = value;}}});}// class语法糖写法"use strict";const DATA = Symbol();class User {constructor(name, age) {this[DATA] = {name, age };}get name() {return this[DATA].name;}set name(value) {if (value.trim() == "") throw new Error("无效的用户名");this[DATA].name = value;}get age() {return this[DATA].name;}set age(value) {if (value.trim() == "") throw new Error("无效的用户名");this[DATA].name = value;}}// 测试代码let hs = new User("花森", 18);console.log(hs.name);hs.name = "huasen";console.log(hs.name);console.log(hs);

13.代理拦截

代理(拦截器)是整个对象的访问控制,setter/getter是对单个对象属性的控制,而代理是对整个对象的控制。

读写属性时代码更简洁;对象的多个属性控制统一交给代理完成;严格模式下set必须返回布尔值;

let user = {data: {name: "花森", sex: 18 },title: "函数代理",};("use strict");// 为对象建立代理const proxy = new Proxy(user, {get(obj, property) {console.log("getter方法执行了");return obj[property];},set(obj, property, value) {console.log("setter方法执行了");obj[property] = value;return true;},});// 使用代理修改属性proxy.age = 10;// 使用代理获得属性console.log(proxy.name);

14.JSON

json是广泛运用前后端数据交换的格式,具有轻量级且易于阅读和编写的特点。

json是数据格式替换xml的最佳方式前后端交互数据的主要格式;json标准中要求双引号包裹属性;

let lessons = [{"title": '媒体查询',"category": 'css',"click": 199},{"title": 'FLEX',"category": 'css',"click": 12},{"title": 'MYSQL',"category": 'mysql',"click": 89}];console.log(lessons[0].title);

序列化

序列化是将json转换为字符串,一般用来向其他语言传输使用。

let lessons = [{"title": '媒体查询',"category": 'css',"click": 199},{"title": 'FLEX',"category": 'css',"click": 12},{"title": 'MYSQL',"category": 'mysql',"click": 89}];// 使用JSON字符串序列化console.log(JSON.stringify(lessons)); // 值 属性 tab数

反序列化

使用JSON.parse将字符串json解析成对象

console.log(JSON.parse(jsonStr));

事件

文档、浏览器、标签元素等元素在特定状态下触发的行为即为事件,JS为不同的事件定义的类型,事件目标是指产生时间的对象,事件具有冒泡捕获的特性,一个行为可能会造成多个事件的触发,处理事件的一段代码称为处理程序。

1.事件绑定

html中绑定

可以在html元素上设置事件处理程序,浏览器解析后会绑定到DOM属性中。

<button onclick="alert(`huasen`)">huasen</button>

dom绑定

可以将事件处理程序绑定到DOM属性中,使用setAttribute方法设置事件处理程序无效,属性名称区分大小写。

<div id="app">huasen</div><script>const app = document.querySelector('#app')app.onclick = function () {this.style.color = 'red'}</script>

事件监听

建议使用新的事件监听绑定方式,transtionend / DOMContentLoaded等事件类型只能使用事件监听addEventListener 处理,同一个事件类型设置多个事件处理程序则会按顺序先后执行,同样可以给为添加元素添加事件。具有以下参数方法:

1)参数一,事件类型;

2)参数二,事件处理程序;

3)参数三,制定的选项,once仅执行一次,capture:true/false捕获阶段,passive:true永远不调用preventDefault()阻值默认行为;

2.事件对象

执行事件处理程序时,会产生当前事件相关信息的对象,即为事件对事。系统会自动做为参数传递给事件处理程序,事件对象常用属性如下:

3.冒泡捕获

冒泡行为

标签元素是嵌套的,在一个元素上触发的事件,同时也会向上执行父级元素的事件处理程序,一直到HTML标签元素。大部分事件都有冒泡特性,但像focus事件则不会发生冒泡。

<!DOCTYPE html><html><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>测试</title><style>#app {background: #34495e;width: 300px;padding: 30px;}#app h2 {background-color: #f1c40f;margin-right: -100px;}</style></head><body><div id="app"><h2>花森酱</h2></div><script>const app = document.querySelector("#app");const h2 = document.querySelector("h2");app.addEventListener("click", (event) => {console.log(`执行事件的节点:${event.currentTarget.nodeName}`);console.log(`触发节点:${event.target.nodeName}`);});h2.addEventListener("click", () => {console.log(`执行事件的节点:${event.currentTarget.nodeName}`);console.log(`触发节点:${event.target.nodeName}`);});</script></body></html>

阻止冒泡

冒泡过程中的任何事件处理程序中,都可以执行event.stopPropagation()方法阻止继续进行冒泡传递,仅会阻止当前代码段的程序,但可以通过event.stopImmediatePropagation()阻止相同事件的冒泡行为。

事件捕获

事件执行顺序为 捕获 > 事件目标 > 冒泡阶段执行,在向下传递到目标对象的过程即为事件捕获,通过设置第三个参数为true或{ capture: true } 在捕获阶段执行事件处理程序。

<html><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>测试</title><style>#app {background: #34495e;width: 300px;padding: 30px;}#app h2 {background-color: #f1c40f;margin-right: -100px;}</style></head><body><div id="app"><h2>huasenjio<span>花森</span></h2></div><script>const app = document.querySelector("#app");const h2 = document.querySelector("h2");const span = document.querySelector("span");app.addEventListener("click",(event) => {console.log("底部 事件");},{capture: true });h2.addEventListener("click", (event) => {console.log("中间 事件");});span.addEventListener("click", (event) => {console.log("上面 事件");});</script></body></html>

事件委托

借助冒泡思路,我们可以不为子元素设置事件,而将事件设置在父级。然后通过父级事件对象的event.target查找子元素,并对他做出处理,此过程叫做事件委托。

<ul><li data-action="hidden">花森导航</li><li data-action="color" data-color="red">笔录</li></ul><script>class HS {constructor(el) {el.addEventListener("click", (e) => {const action = e.target.dataset.action;this[action](e);});}hidden() {event.target.hidden = true;}color() {event.target.style.color = event.target.dataset.color;}}new HS(document.querySelector("ul"));</script>

4.默认行为

JS会有些对象会设置默认事件处理程序,比如A链接在点击时会进行跳转。一般默认处理程序会在用户定义的处理程序后执行,所以我们可以在我们定义的事件处理程序员取消默认事件处理程序的执行。使用onclick绑定的时间处理程序,return false就可以阻止默认行为,推荐event.preventDefault()进行阻止默认行为。

<a href="http://n.huasenjio.top">花森笔录</a><script>document.querySelector('a').addEventListener('click', () => {event.preventDefault()alert(event.target.innerText)})</script>

5.窗口文档

6.鼠标事件

针对鼠标操作的行为有多种事件类型,鼠标事件会触发在Z-INDEX最高的那个元素,仅能在顶层元素触发事件,被盖住的元素是无法触发点击事件。

事件属性

7.键盘事件

针对键盘输入操作的行为有多种事件类型

事件属性

8.表单事件

下面是可以用在表单上的事件类型

原型和继承

每一个构造函数都存在prototype原型对象,通过new实例的对象会继承原型对象的方法属性,实例对象通过__proto__属性指向构造函数的prototype原型对象,所有的函数的原型默认是Object的实例对象,因为Object构造函数的原型上具有toString/toValues/isPrototypeOf方法,所以每个对象都可以调用。当实例上不存在属性或者方法则将到原型上查找,原型对象包含constructor属性指向构造函数,对象包含__proto__指向他的原型对象。

1.Object.getPrototypeOf

用于获取一个对象的原型

console.log(Object.getPrototypeOf(a));

2.Object.setPrototypeOf

可以使用__proto__Object.setPrototypeOf设置对象的原型

function Person() {this.getName = function() {return this.name;};}function User(name, age) {this.name = name;this.age = age;}let lisi = new User("李四", 12);Object.setPrototypeOf(lisi, new Person()); //将Person的一个实例对象作为lisi的原型console.log(lisi.getName()); //李四

3.原型检测

instanceof

instanceof 检测构造函数的prototype属性是否出现在某个实例对象的原型链上

function A() {}function B() {}function C() {}const c = new C();B.prototype = c;const b = new B();A.prototype = b;const a = new A();console.dir(a instanceof A); //trueconsole.dir(a instanceof B); //trueconsole.dir(a instanceof C); //trueconsole.dir(b instanceof C); //trueconsole.dir(c instanceof B); //false

isPrototypeOf

使用isPrototypeOf检测一个对象是否是另一个对象的原型链中

const a = {};const b = {};const c = {};Object.setPrototypeOf(a, b);Object.setPrototypeOf(b, c);console.log(b.isPrototypeOf(a)); //trueconsole.log(c.isPrototypeOf(a)); //trueconsole.log(c.isPrototypeOf(b)); //true

4.属性遍历

in

使用in检测原型链上是否存在属性,使用hasOwnProperty只检测当前对象是否存在属性。

let a = {url: "huasen" };let b = {name: "花森" };Object.setPrototypeOf(a, b); // 将a设置为b的原型console.log("name" in a); // trueconsole.log(a.hasOwnProperty("name")); // falseconsole.log(a.hasOwnProperty("url")); // false

for-in

let hs = {name: "花森" };// 以hs为原型创建huasen对象let huasen = Object.create(hs, {url: {value: "huasenjio",enumerable: true,},});console.log(huasen);for (const key in huasen) {console.log(key);}

5.借用原型

使用callapply可以借用其他原型方法完成功能

let hs = {data: [1, 2, 3, 4, 5],};// 借用Math上面的max方法console.log(Math.max.apply(null, hs.data));let zhuqi = {lessons: {js: 100, php: 78, node: 78, linux: 125 },};// Object.values()被返回可枚举属性值的对象console.log(Math.max.apply(zhuqi, Object.values(zhuqi.lessons)));

6._proto_

实例化对象上存在一个__proto__记录原型的信息,可以通过对象访问到原型的属性和方法,严格意义上讲__proto__不是对象属性,可以理解为protortype的一个getter/setter实现,是一个非标准的定义,内部使用getter/setter控制输入值,所以只允许对象或者null的赋值,建议使用建议使用Object.setPrototypeOfObject.getProttoeypOf替代__proto__的使用。

lisi.__proto__ = new Person();

7.继承与多态

实现继承

<script>function Person() {}Person.prototype.getName = function () {console.log("parent method");};function User() {}// 方式一,全部实例对象实现继承,Object.create(Person.prototype),建立一个a空对象且空对象的原型对象是Person的原型对象,User.prototype = a.__proto__ = Person.prototype。User.prototype = Object.create(Person.prototype);User.prototype.constructor = User;// 原型对象的方法设置放在建立原型链之后,避免造成覆盖。User.prototype.showUser = function () {console.log("User的方法");};console.log(new User());// 方式二,对单个对象实现继承,设置lisi对象原型,等同于lisi.__proto__ = Person.prototype,同样会造成原型对象的constructor构造函数丢失。// let lisi = new User();// Object.setPrototypeOf(lisi, Person.prototype);// console.dir(lisi);// console.dir(new User());</script>

继承

对象能使用它上游原型链上存在的方法和状态

// Array原型对象上不存在valueOf方法,但是因为Array继承Object对象,即[]的__proto__指向的是Array的原型对象,而Array的__proto__指向的是Object的原型对象,因为Object的原型对象上存在valueOf方法,所以[]可以使用。console.log([]);console.log([1, 2, 3, 4].valueOf()); // (4) [1, 2, 3, 4]

方法重写

子类的定义与父类相同方法名的方法就可以重写父类的方法,思想就是不让子类顺着原型链网上找,因为它在自己的原型上查找到方法后就不会再向上查找,这一点与nodejs找依赖库的思想一致。

function Person() {}Person.prototype.getName = function() {console.log("parent method");};function User(name) {}User.prototype = Object.create(Person.prototype);User.prototype.constructor = User;User.prototype.getName = function() {//调用父级同名方法Person.prototype.getName.call(this);console.log("child method");};let hd = new User();hd.getName();

多态

根据多种不同的形态产生不同的结果,下而会根据不同形态的对象得到了不同的结果。例如动物是猫和狗的父类,动物有会叫的方法,猫和狗实现动物叫的方法时表现形式不同。这就是多态。

function User() {}User.prototype.show = function() {console.log(this.description());};function Admin() {}Admin.prototype = Object.create(User.prototype);Admin.prototype.description = function() {return "管理员在此";};function Member() {}Member.prototype = Object.create(User.prototype);Member.prototype.description = function() {return "我是会员";};function Enterprise() {}Enterprise.prototype = Object.create(User.prototype);Enterprise.prototype.description = function() {return "企业帐户";};for (const obj of [new Admin(), new Member(), new Enterprise()]) {obj.show();}

8.继承进阶

构造函数

function User(name) {this.name = name;console.log(this); // Admin}User.prototype.getUserName = function () {return this.name;};function Admin(name) {User.call(this, name); // 将User构造函数绑定给Admin}Admin.prototype = Object.create(User.prototype);let hs = new Admin("花森");console.log(hs.getUserName()); //花森

Mixin多继承

JS不能实现多继承,如果要使用多个类的方法时可以使用mixin混合模式来完成。mixin类是一个包含许多其他类方法的对象,经过Object.assign对象合并的方式为原型添加方法。

<script>function extend(sub, sup) {sub.prototype = Object.create(sup.prototype);sub.prototype.constructor = sub;}function User(name, age) {this.name = name;this.age = age;}User.prototype.show = function () {console.log(this.name, this.age);};const Credit = {total() {console.log("统计积分");},};const Request = {ajax() {console.log("请求后台");},};function Admin(...args) {User.apply(this, args);}extend(Admin, User);Object.assign(Admin.prototype, Request, Credit);let hs = new Admin("花森", 19);console.dir(hs);hs.show();hs.total(); //统计积分hs.ajax(); //请求后台</script>

class

为了和其他语言的形态一致,JS提供了class关键词用于模拟传统的class,只是原型继承的语法糖形式,class为了让类的声明和继承更加简洁清晰,class默认使用严格模式执行。

1.声明定义

<script>// 构造函数构造对象function User(name) {//实例对象属性this.name = name;}//静态属性User.type = "用户";//静态方法User.showTypea = function() {// 静态方法调用的对象是构造函数 所以静态方法中仅可以使用静态属性return this.type;};// 原型添加函数(所有实例对象可以访问)User.prototype.showName = function() {return this.name;};console.dir(User);console.dir(new User("森哥哥"));// 类的方式构建对象 (extends继承对于构造函数是 Admin.__proto__ == User, 对于实例对象是 实例对象.__proto__ == User.prototype.)class Admin extends User {//静态属性static type = "管理员";constructor(name) {super(); // 指调用父类的构造函数//实例对象属性this.name = name;}//静态方法static showType() {// 静态方法调用的对象是构造函数 所以静态方法中仅可以使用静态属性return this.type;}// 原型添加函数(所有实例对象可以访问)showName() {return this.name;}}console.dir(Admin);console.dir(new Admin("猪琦琦"));</script>

2.静态访问

静态属性

静态属性即为类设置属性,无需实例化即可调用,针对的是构造器设置。

// es5构造函数构造对象function User(name) {}//静态属性User.type = "用户";// class中使用static关键词class Admin extends User {//静态属性static type = "管理员";}

静态方法

// es5形式function User(name) {}User.showTypea = function() {// 静态方法调用的对象是构造函数 所以静态方法中仅可以使用静态属性return this.type;};// class静态方法static showType() {// 静态方法调用的对象是构造函数 所以静态方法中仅可以使用静态属性return this.type;}

3.访问器

访问器可以对对象的属性进行访问控制,访问器可以管控属性,有效的防止属性随意修改,加上get/set修饰,操作是不需要添加函数括号。

class User {constructor(name) {this.data = {name };}get name() {return this.data.name;}set name(value) {if (value.trim() == "") throw new Error("invalid params");this.data.name = value;}}let hs = new User("花森");hs.name = "huasen";console.log(hs.name);

public

public指不受保护的属性,在类的内部与外部都可以访问到。

class User {url = "huasenjio";constructor(name) {this.name = name;}}let hs = new User("花森");console.log(hs.name, hs.url);

protected

protected是受保护的属性修释,不允许外部直接操作,但可以继承后在类内部访问。

// 属性定义为以 _ 开始,来告诉使用者这是一个私有属性,请不要在外部使用,自启提示作用。class Article {_host = "http://huasenjio.top/";set host(url) {if (!/^https:\/\//i.test(url)) {throw new Error("网址错误");}this._host = url;}lists() {return `${this._host}/article`;}}

private

private指私有属性,只在当前类可以访问到,并且不允许继承使用,为属性或者方法名前家#则是声明私有属性,是有属性只能在声明的类中使用。

class User {#host = "http://huasenjio.top/";constructor(name) {this.name = name;this.#check(name);}#check = () => {if (this.name.length <= 5) {throw new Error("用户名长度不能小于五位");}return true;};}class Pig extends User {constructor(name) {super(name);this.name = name;}}let pig = new Pig("猪琦猪猪猪户");// console.log(pig.#host); // '#host' must be declared in an enclosing class// console.log(new User().#host); // '#host' must be declared in an enclosing class

4.super

所有继承中this始终为调用对象,super是用来查找当前对象的原型。在constructor中super指调用父类引用,必须先调用super()后再进行this赋值。

<script>class User {constructor(name) {this.name = name;}}class Pig extends User {constructor(name, type) {// super会调用父类的constructor构造器,因为父类的name属性是会被Pig继承,所以实例子类时需要将父类需要的参数通过super传递。super(name);this.type = type;}}let pig = new Pig("猪琦", "猪");console.log(pig);</script>// 实现原理function Parent(name) {this.name = name;}function User(...args) {Parent.apply(this, args);}User.prototype = Object.create(User.prototype)User.prototype.constructor = User;const hs = new User("花森");console.log(hs.name);

任务管理

JavaScript 语言的一大特点就是单线程,同一个时间只能处理一个任务。为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,JavaScript 处理任务是在等待任务、执行任务 、休眠等待新任务中不断循环中,也称这种机制为事件循环。

console.log("1.花森是同步代码");setTimeout(function () {console.log("6.定时器是宏任务代码");Promise.resolve().then(() => {console.log("7.下次循环才会执行当前微任务");});}, 0);// 激活微任务Promise.resolve().then(function () {console.log("3.promise1微任务");}).then(function () {console.log("4.promise2微任务");}).then(() => {console.log("5.单次循环内微任务执行完成后才可以执行宏任务");});console.log("2.huasenjio时同步代码");

模块化开发

项目变大时需要把不同的业务分割成多个文件,即模块的思想,模块是比对象与函数更大的单元,使用模块组织程序便于维护与扩展且模块默认运行在严格模式下。使用模块化开发有以下优点:

模块化是一个独立的文件,可以包含函数或者类库;使用模块化可以解决全局变量冲突;模块可以隐藏内部实现,只对外保留开发接口;模块化可以避免全局变量造成打代码不可控;模块可以服用提高编码的效率;

1.管理引擎

过去JS不支持模块时我们使用AMD/CMD(浏览器端使用)、CommonJS(Node.js)、UMD(都支持)等形式定义模块。AMD代表性的是require.js,CMD 代表是淘宝的seaJS框架。

2.基本使用

// html文件中导入模块需要定义属性 type="module"<script type="module">import {hs } from "./a.js"; // 浏览器中使用必须添加路径</script>// 新建文件a.js内容如下export let hs = {name: "花森"};

3.作用域

模块都有独立的顶级作用域,不同模块之间不可以访问。

<script type="module">let hs = "huasenjio";</script><script type="module">alert(hs); // Error</script>

4.预解析

模块在导入时只执行一次解析,之后的导入不会再执行模块代码,而使用第一次解析结果,并共享数据,可以在首次导入时完成一些初始化工作。

5.导入导出

ES6使用基于文件的模块即一个文件一个模块:

使用export导出方法属性;使用import导入模块接口;使用 * 导入全部模块接口;导出的状态和方法都是地址,模块内修改会影响导入的变量;

导出模块

定义文件hd.js导出内容如下

export const site = "花森导航";export const func = function() {return "is a module function";};export class User {show() {console.log("user.show");}}// 别名导出export {site, func as action, User as user };

导入模块

// 具名导入(不忽略大小写)<script type="module">import {User, site, func } from "./hd.js";console.log(site);console.log(User);</script>// 批量导入<script type="module">import * as api from "./hd.js";console.log(api.site);console.log(api.User);</script>// 别名导入<script type="module">import {User as user, func as action, site as name } from "./hd.js";let func = "houdunren";console.log(name);console.log(user);console.log(action);</script>

6.默认导出

当文件中导出的内容模块只有一个,也就是说仅需导入一个内容,这时可以使用默认导入,使用default定义默认导出的接口,导入时不需要使用{}且名字任意。

// hd.js文件中暴露导出default class {static show() {console.log("User.method");}}// html中引入<script type="module">import User from "./hd.js";User.show();</script>

7.动态导入

<script>if (true) {let hd = import("./hd.js").then(module => {console.log(module.site);});}</script>

8.指令总结

Promise

JavaScript中存在很多异步操作,Promise将异步操作队列化,按照期望的顺序执行,返回符合预期的结果,可以通过链式调用多个Promise达到我们的目的,使用promise还可以避免回调地域的现象,当一个Promise建立时执行内部的同步代码,resolve或者reject后开启微任务(then内部代码),并载入微任务队列。

1.基本理解

我在打游戏的时候(主进程),突然想吃可乐鸡腿,于是你让猪琦(Promise)开了一个做可乐鸡腿的任务(异步任务),因为不影响你打游戏,所以它叫异步任务。如果猪琦同意接受(resolve)则猪琦将去开始去烹饪,做好叫我吃可乐鸡腿(then中result回调函数);如果猪琦烹饪时可乐鸡腿发生意外(错误)或者拒绝任务(reject)并将我打了一顿,让我跪键盘(then中error回调函数),如果你没有跪键盘或者其他举措补偿(没设置error回调),则统一按分手(catch)处理。不管怎样最后都会和好(finally),开心的过日子。

new Promise((resolve, reject) => {resolve("成功处理");reject("拒绝处理");console.log("同步代码"); // 优先执行}).then((res) => {console.log("成功", res); // resolve则会执行此方法体},(err) => {console.log("拒绝", err); // 语法错误与reject则执行此方法体}).catch((error) => {console.log("发生语法错误"); // 未设置reject回调函数时交给catch统一处理});

状态说明

Promise包含pending、fulfilled、rejected三种状态,Promise是队列状态,状态可一直向后传递,每一个Promise都可以改变状态,Promise可以链式传递一个传一个。

pending初始等待状态,可由new Promise()获得该状态;resolve已经解决,promise状态设置为fulfilled,可由Promise.resolve()获得该状态;reject拒绝处理,promise状态设置为rejected,可由Promise.reject()获得该状态;promise是生产者,通过resolvereject函数告知异步任务的状态,一旦状态改变将不可更改;

2.异步加载

function load(file, resolve, reject) {const script = document.createElement("script");script.src = file;script.onload = resolve;script.onerror = reject;document.body.appendChild(script);}load("./js/hs.js",script => {console.log(`${script.path[0].src} 加载成功`);hs();},error => {console.log(`${error.srcElement.src} 加载失败`);});

3.then

promise 需要提供一个then方法访问promise 结果,then用于定义当promise状态发生改变时的处理,即promise处理异步操作,then用于结果处理输出。

then方法必须返回Promise,手动返回或者系统自动返回;执行resolve时跳入then方法中的第一个回调参数;执行reject时跳入then方法中的第二个回调参数;

<script>new Promise((resolve, reject) => {resolve("成功");}).then((res) => {console.log("1" + res);return new Promise((resolve, reject) => {resolve("成功");});}).then((res) => {console.log("2" + res);// 如果返回的是未处理的Promise则阻塞等待处理return new Promise((resolve, reject) => {reject("失败");});}).then(null, (err) => {console.log("3" + err);return "then链式调用默认执行resolve回调方法并将return指赋值给res";}).then((res) => {console.log("4" + res);}).catch((error) => {console.log(error);});</script>

4.catch

catch用于失败状态的处理函数,等同于then(null,reject){},建议使用catch统一处理错误。如果不存在reject回调函数则会跳入catch方法,整一个Promise链上的错误都可以被catch捕获。

new Promise((resolve, reject) => {resolve("成功");}).then((res) => {console.log("1" + res);abc; // 这个错误一直下沉,直到找到reject回调方法,如果then链上没有同意由catch捕获。return new Promise((resolve, reject) => {resolve("成功");});}).then((res) => {console.log("2" + res);// 如果返回的是未处理的Promise则阻塞等待处理return new Promise((resolve, reject) => {reject("失败");});}).then(null, (err) => {console.log("3" + err);return "then链式调用默认执行resolve回调方法并将return指赋值给res";}).then((res) => {console.log("4" + res);}).catch((error) => {console.log(error);});

5.事件处理

unhandledrejection事件用于捕获到未处理的Promise错误,下面的 then 产生了错误,但没有catch处理,这时就会触发事件。

window.addEventListener("unhandledrejection", function(event) {console.log(event.promise); // 产生错误的promise对象console.log(event.reason); // Promise的reason});new Promise((resolve, reject) => {resolve("success");}).then(msg => {throw new Error("fail");});

6.finally

无论状态是resolvereject都会执行此动作

const promise = new Promise((resolve, reject) => {reject("hs");}).then(msg => {console.log("resolve");}).catch(msg => {console.log("reject");}).finally(() => {console.log("resolve/reject状态都会执行");});

7.拓展接口

resolve

使用Promise.resolve()方法可以快速的返回一个状态为fulfilled的promise对象

Promise.resolve("花森").then(value => {console.log(value); // 花森});

reject

使用Promise.reject()方法可以快速的返回一个状态为rejected的promise对象

Promise.reject("花森").then(null,err => {console.log(err); // 花森});

all

使用Promise.all方法可以同时执行多个并行异步操作,等待多个promise完成任务后返回一个有序的数组,需要注意一下几点:

任何一个Promise执行失败都会调用catch方法;一次发送多个异步操作;参数必须是可迭代对象例如Array和Set;

const hs = new Promise((resolve, reject) => {setTimeout(() => {resolve("第一个Promise");}, 1000);});const zhuqi = new Promise((resolve, reject) => {setTimeout(() => {resolve("第二个Promise");}, 1000);});const h = Promise.all([hs, zhuqi]).then((results) => {console.log(results);}).catch((msg) => {console.log(msg);});setTimeout(() => {console.log(h);}, 1000);

allSettled

allSettled用于处理多个promise,只关注执行完成,不关注是否全部执行成功,allSettled状态只会是fulfilled

const p1 = new Promise((resolve, reject) => {resolve("resolved");});const p2 = new Promise((resolve, reject) => {reject("rejected");});Promise.allSettled([p1, p2]).then(msg => {console.log(msg);})

race

使用Promise.race()处理容错异步,队列中Promise优先执行则优先返回,具有一下的几点特性:

最快返回的promise为准;如果传入参数不是Promise则内部自动转为Promise;无论内部的Promise返回的转态是reject还是resolve,race都会返回一个fulfilled状态promise,如果传入的Promise都存在语法错误则会返回一个Pending状态的Promise;

const hs = new Promise((resolve, reject) => {setTimeout(() => {reject("第一个Promise");}, 1000);});const zhuqi = new Promise((resolve, reject) => {setTimeout(() => {reject("第二个Promise");}, 3000);});const h = Promise.race([hs, zhuqi]).then((results) => {console.log("成功" + results);}).catch((msg) => {console.log("错误" + msg);});setTimeout(() => {console.log(h); // Promise {<fulfilled>: undefined}}, 4000);

8.async/await

使用async/await是promise 的语法糖,可以让编写 promise 更清晰易懂。

async

函数前加上async关键词,函数将返回promise就可以像使用标准Promise一样使用了。

async function hs() {return "huasenjio";}console.log(hs());hs().then((value) => {console.log(value);});

await

使用await关键词后面的Promise执行完成则继续向下执行,否则阻塞等待,内部await一旦有一个出现reject或者语法错误,则直接跳入err的回调函数中,具有一下特性:

await后面跟Promise,若不是则直接返回后面值;await必须放在async定义的函数中使用;await用于代替then的链式调用;

const promise = new Promise((resolve, reject) => {setTimeout(() => {resolve("promise处理");}, 1000);});const huasen = new Promise((resolve, reject) => {setTimeout(() => {reject("p拒绝"); // 一旦出现拒绝又不进行处理,直接跳入err回调函数中,下面的代码将不再执行。}, 1000);});async function hs() {let result = await promise;console.log("需要等待await完成");let res = await huasen;return "执行完成"; // 如果不存在语法错误且不拒绝,则返回值跳入resolve回调函数中的res参数中。}hs().then((res) => {console.log("成功", res);},(err) => {console.log("失败", err);});

DOM

浏览器解析HTML文件时会生成一个DOM对象,JS可以调用控制页面元素,但需要注意的是操控是浏览器以及渲染了页面内容,否则无法读取到节点对象。

1.节点对象

html中的每一个标签对应着js中的一个DOM节点对象,包含有属性方法,可以调用,以下是对象的基本知识:

包括有12种类型节点对象;常用节点对象为document、标签元素节点、文本节点、注释节点;节点均继承node对象,所以拥有相同的属性和方法;document节点是DOM的根节点;

<body id="hs"><!-- 花森 -->huasenjio</body><script>console.log(document.nodeType) //9 document对象console.log(document.childNodes.item(0).nodeType) //10console.log(document.body.nodeType) //1console.log(document.body.attributes[0].nodeType) //2console.log(document.body.childNodes[1].nodeType) //8</script>

2.DOM原型链

浏览器渲染过程中会将文档内容生成为不同的对象,不同的节点类型有专门的构造参数创建对象,使用console.dir可以查看详细属性方法,节点也是对象所以觉有JS对象的特征,有以下节点类型:

<div id="hs">花森酱</div><input type="text" name="title" id="title" /><script>let hs = document.getElementById("id");function prototype(el) {let proto = Object.getPrototypeOf(el); // 获取对象原型console.log(proto);Object.getPrototypeOf(proto) ? prototype(proto) : ""; // 递归获取}prototype(hs);</script>

对象合并

<div id="hs">huasenjio</div><script>let hs = document.getElementById('hs')Object.assign(hs, {color: 'red',change() {this.innerHTML = '花森酱'this.style.color = this.color},onclick() {this.change()},})</script>

样式合并

<div id="hs">huasenjio</div><script>let hs = document.getElementById('hs')Object.assign(hs.style, {color: 'red',})</script>

3.页面文档

document是window对象的属性,是由HTMLDocument类实现的实例,继承node类则可以用node的相关方法,document文档包含着页面唯一的元素标签。

html

系统提供了简单的方式来获取html元素

console.log(document.documentElement)

文档信息

使用title获取和设置文档标题

//获取文档标题console.log(document.title)//设置文档标签document.title = '花森酱测试文件'//获取当前URLconsole.log(document.URL)//获取域名document.domain//获取来源地址console.log(document.referrer)

body

可通过document.body获取到页面

4.节点属性

nodeType

不同类型的节点拥有不同的属性,nodeType指以数值返回节点类型,递归获取元素某节点下的全部标签元素。

<div id="hs"><ul><li><h2><strong>花森</strong></h2></li></ul></div><script>function all(el) {let items = [];[...el.childNodes].map((node) => {if (node.nodeType == 1) {items.push(node, ...all(node));}});return items;}console.log(all(document.body));</script>

nodeName

nodeName指定节点的名称,获取值为大写形式,注意空行也是一个文本节点。

5.节点集合

Nodelist与HTMLCollection都是包含多个节点标签的集合

getElementsBy…等方法返回的是HTMLCollection;querySelectorAll样式选择器返回的是 NodeList;NodeList和NodeList集合是动态的;

length

Nodelist与HTMLCollection包含length属性则记录节点元素的数量

转换数组

有时使用数组方法来操作节点集合,这就需要将节点集合转化为数组类型,有以下几种方式可以实现。

// Array.from()console.log(Array.from(elements))// 点语法[...elements]

6.常用元素

系统针对特定标签提供了快速选择的方式

7.节点关系

节点是根据HTML内容产生的,所以也存在父子、兄弟、祖先、后代等节点关系,下例中的代码就会产生这种多重关系,目前文本节点也会匹配上关系。

h1与ul是兄弟关系;span与li是父子关系;ul与span是后代关系;span与ul是祖先关系;

<h1>花森</h1><ul><li><span>huasen</span><strong>猪琦</strong></li></ul>

8.元素关系

使用childNodes等获取的节点包括文本与注释

9.选择器

系统提供了丰富的选择节点(NODE)的操作方法

getElementById

使用ID选择是非常方便的选择具有ID值的节点元素,此方法仅存在与document对象上。

<div id="hs">huasen</div><div id="app"></div><script>function getByElementIds(ids) {return ids.map((id) => document.getElementById(id));}let nodes = getByElementIds(["hs", "app"]);console.dir(nodes);</script>

getElementByClassName

getElementsByClassName用于按class 样式属性值获取元素集合

const nodes = document.getElementsByClassName('hs')

getElementByName

使用getElementByName获取设置了name属性的元素,返回NodeList节点列表对象,顺序即元素在文档中的顺序。

<div name="hs">花森</div><script>const div = document.getElementsByName("hs");console.dir(div);</script>

getElementByTagName

使用getElementsByTagName用于按标签名获取元素

const divs = document.getElementsByTagName('div')

通配符

可以使用通配符*获取所有元素

const nodes = document.getElementsByTagName('*')

querySelectorAll

在DOM操作中也可以使用这种方式查找元素,使用querySelectorAll根据CSS选择器获取Nodelist节点列表。获取的NodeList节点列表是静态的就是添加或者删除元素后list不会发生变化。

<div id="app"><div>花森/div><div>猪琦</div></div><script>const app = document.body.querySelectorAll("#app")[0];const nodes = app.querySelectorAll("div");console.log(nodes); //2</script>

querySelector

querySelector使用CSS选择器获取一个元素

const nodes = app.querySelectorAll("div");

matches

用于检测元素是否是指定的样式选择器匹配

<div id="app" class="app"><div>花森/div><div>猪琦</div></div><script>const app = document.body.querySelectorAll("#app")[0];console.log(app.matches(".app")) // true</script>

closest

根据样式查找某元素最近的祖先元素

// 这个例子是查找li元素最近的祖先并且符合`.comment`样式<div class="comment"><ul class="comment"><li>huasenjio</li></ul></div><script>const li = document.getElementsByTagName("li")[0];const node = li.closest(`.comment`);console.log(node);</script>

10.动态与静态

通过 getElementsByTagname 等getElementsBy… 函数获取的Nodelist与HTMLCollection集合是动态,即有元素添加或移动操作集合将实时反映最新状态。

使用getElement…返回的都是动态的集合;使用querySelectorAll返回的是静态集合;

<h1>花森导航</h1><h1>huasenjio</h1><button id="add">添加元素</button><script>let elements = document.getElementsByTagName("h1");//let elements = document.querySelectorAll("h1");console.log(elements);let button = document.querySelector("#add");button.addEventListener("click", () => {document.querySelector("body").insertAdjacentHTML("beforeend", "<h1>hs</h1>");console.log(elements);});</script>

11.元素特征

标准的属性(src|className|herf)可以使用DOM属性的方式进行操作,但是对于非标准的属性则不可以,可以理解为元素的属性分两个地方保存,DOM属性记录标准属性,特征中记录标准和定制属性。简而言之就是对奇怪的属性进行操作,有以下几种方法:

attributes

元素提供了attributes 属性可以只读的获取元素的属性

<div class="hs" data-content="花森"></div><script>let hs = document.querySelector(".hs");console.dir(hs.attributes["class"].nodeValue); //hsconsole.dir(hs.attributes["data-content"].nodeValue); //后盾人</script>

hasAttribute

用于检测对象是否存在某个属性

console.log(hdcms.hasAttribute('class')) //false

自定义属性

虽然可以随意定义特征并使用getAttribute等方法管理,建议使用以data-为前缀的自定义特征处理,针对这种定义方式JS也提供了接口方便操作。

元素中以data-作为前缀的属性会添加按到属性map集合中;使用元素的dataset可以获取属性集合中的属性;改变dataset的值也会影响元素;

<div class="hs" data-title-color="red">花森酱</div><script>let hs = document.querySelector(".hs");console.log(hs.dataset);hs.innerHTML = `<span style="color:${hs.dataset.titleColor}">${hs.innerHTML}</span>`;</script>

12.创建节点

创建节点的就是构建出DOM对象

createTextNode

创建文本对象并添加到元素中

<div id="app"></div><script>let app = document.querySelector('#app')let text = document.createTextNode('花森')app.append(text)</script>

createElement

使用createElement方法可以标签节点对象

<div id="app"></div><script>let app = document.querySelector('#app')let span = document.createElement('span')span.innerHTML = '花森酱'app.append(span)</script>

createDocumentFragment

使用createDocumentFragment创建虚拟节点容器具有一下特点:

创建的节点的parentNode为Null;使用createDocumentFragment创建的节点来暂存文档节点createDocumentFragment创建的节点添加到其他节点上时;不直接操作DOM所以性能更好;排序/移动等大量DOM操作时建议使用createDocumentFragment

cloneNode&importNode

使用cloneNode和document.importNode用于复制节点对象操作

cloneNode是节点方法;cloneNode参数为true时递归赋值子节点即深拷贝;importNode是document对象方法;

<div id="app">huasen</div><script>let app = document.querySelector('#app')let newApp = app.cloneNode(true)document.body.appendChild(newApp)</script>

13.节点内容

innerHTML

inneHTML用于向标签中添加html内容,同时出发浏览器的解析器重绘DOM树。

<div id="app"></div><script>let app = document.querySelector("#app");console.log(app.innerHTML);app.innerHTML = "<h1>花森</h1>";</script>

outerHTML

outerHTML与innerHTML的区别是包含父标签,不会将原来的内容删除掉依然在DOM树节点上。

<div id="app"><div class="hs" data="hs">花森</div><div class="zhuqi">猪琦</div></div><script>let app = document.querySelector('#app')console.log(app.outerHTML)app.outerHTML = '<h1>酱</h1>'</script>

textContent与innerText

textContent与innerText是访问或添加文本内容到元素中

<div id="app"><div class="hs" data="hs">花森</div><div class="zhuqi">猪琦</div></div><script>let app = document.querySelector('#app')console.log(app.outerHTML)app.textContent = '<h1>酱</h1>'</script>

insertAdjacentText

将文本插入到元素指定位置,不会对文本中的标签进行解析,即不会将<h1>皮卡丘</h1>以HTML标签形式去渲染。

<div id="app"><div class="hs">花森</div><div class="zq">猪琦</div></div><script>let app = document.querySelector(".hs");app.insertAdjacentText("afterend", "<h1>皮卡丘</h1>");</script>

14.节点管理

节点元素的管理,包括添加、删除、替换等操作,具体有以下参数位置:

推荐方法

<div id="app">花森酱</div><script>let app = document.querySelector("#app");app.append("huasenjio.top");</script>

inserAdjacentHTML

html文本插入到元素指定位置,浏览器会对文本进行标签解析,具有包括以下参数位置:

<div id="app">花森酱</div><script>let app = document.querySelector("#app");app.insertAdjacentHTML("afterbegin",'<h1>后盾人</h1>');</script>

inserAdjacentElement

nsertAdjacentElement() 方法将指定元素插入到元素的指定位置

第一个参数是位置;第二个参数为新元素节点;

<div id="app">花森酱</div><script>let app = document.querySelector("#app");let span = document.createElement('span')app.insertAdjacentHTML("afterbegin",span);</script>

古老管理手法

下面列表过去使用的操作节点的方法

15.表单控制

表单查找

JS为表单的操作提供了单独的集合控制

使用document.forms获取表单集合;使用form的name属性获取指定form元素;根据表单项的name属性使用form.elements.title获取表单项;直接写成form.name形式;针对radio/checkbox获取的表单项是一个集合;

<form action="" name="hs"><input type="text" name="title" /></form><script>const form = document.forms.hs;console.log(form.title.form === form); //true</script>

16.样式管理

通过DOM修改样式可以通过更改元素的class属性或通过style对象设置行样式来完成

className

使用JS的className可以批量设置样式,style中设置对应的类名,达到修改样式的目的。

let app = document.getElementById('app')app.className = 'hs'

classList

通过使用 classList属性,添加移除操作操作,实现样式和dom的绑定。

let app = document.getElementById('app')app.classList.add('hs')console.log(app.classList.contains('hs')) //false

单个样式属性设置

使用节点的style对象来设置行样式,单词采用驼峰命名法。

let app = document.getElementById('app')app.style.backgroundColor = 'red'app.style.color = 'yellow'

批量设置样式

// 方式一let app = document.getElementById('app')app.style.cssText = `background-color:red;color:yellow`// 方式二app.setAttribute('style', `background-color:red;color:yellow;`)

17.样式获取

style

可以使用DOM对象的style属性读取行样式,需要注意的是style对象仅可以获取内联样式属性。

<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>测试</title><style>div {color: yellow;}</style></head><body><div id="app" style="background-color: red; margin: 20px">后盾人</div><script>let app = document.getElementById("app");console.log(app.style.backgroundColor);console.log(app.style.margin);console.log(app.style.marginTop);console.log(app.style.color);</script></body>

getComputedStyle

使用window.getComputedStyle可获取所有应用在元素上的样式属性,第一个参数为元素,第二个参数为伪类,此方式获得的是经过计算后的样式属性,可能与真实的设置值有所不同。

let fontSize = window.getComputedStyle(app).fontSize

空间坐标

1.基础理解

首先参考画布分为视口(窗口)与文档的含义

文档尺寸一般大于视口尺寸;F12打开控制台会造成尺寸相应变小;视口的尺寸不包括浏览器的菜单和状态栏等;视口坐标的操作需要考虑滚动条的位置;

2.视口文档

获取浏览器视口宽度的集中方法

3.定位方向

4.坐标判断

获取相对于视口的xy坐标上的元素,如果坐标定在视口外则返回值为null。

5.滚动控制

document.documentElement.scroll({top: 30, behavior: 'smooth' })

网络请求

浏览器天生具发送HTTP请求的能力

1.XMLHttpRequest

使用XMLHttpRequest发送请求数据

// 通过XMLHttpRequst封装Ajax网络请求// 实例:HuaSenAjax.get(url,options,data),HuaSenAjax.post(url,options,data),new HuaSenAjax("GET",url,options,data),默认请求GET且异步请求,函数返回一个Promise对象。class HuaAjaxTools {// 定义返回类型为JSON类型options = {responseType: "json",};// 构造函数,默认get请求,默认不传参数,参数列表options用于覆盖。constructor(method = "GET", url, options, data = null, async = true) {this.method = method;this.url = url;this.data = this.formatData(data);this.async = async;Object.assign(this.options, options); // 合并覆盖参数}// 格式化处理参数,处理将POST请求数据序列化。formatData(data) {if (typeof data != "object" || data == null) data = {};let form = new FormData(); // FormData类型其实是在XMLHttpRequest2级定义,它是为序列化表以及创建与表单格式相同的数据(当然是用于XHR传输)提供便利。for (const [name, value] of Object.entries(data)) {form.append(name, value); //添加至序列化列表中}return form;}// 处理连接参数的静态方法static handleUrl(url = null, data = null) {if (typeof url != "string" || url == null) url = "";if (typeof data != "object" || data == null) data = {};if (Object.keys(data).length !== 0) {url = url.replace(/\/$/, "") + "?";let params = "";for (const [name, value] of Object.entries(data)) {params += "&" + name + "=" + encodeURIComponent(value);}params = params.replace(/^&/, ""); // 利用正则去除参数中的第一个&符号url = url + params;}return url;}// 声明get静态方法static get(url, options, data) {try {url = this.handleUrl(url, data);return new this("GET", url, options).xhr();} catch (e) {return Promise.reject("get方法内部出错" + e);}}// 声明post静态方法static post(url, options, data) {try {return new this("POST", url, options, data).xhr();} catch (e) {return Promise.reject("post方法内部出错" + e);}}static jsonp(url, data) {return new Promise((resolve, reject) => {try {// window对象绑定一个回调函数const body = document.body;const script = document.createElement("script");window[data.cb] = function (res) {resolve(res); // 处理promise激活微任务};url = this.handleUrl(url, data); // 处理URLscript.src = url;body.appendChild(script); // body标签后添加script标签程序自动加载内容body.removeChild(script); // 获得参数后移除添加的script标签} catch (e) {body.removeChild(script);reject("jsonp中发生错误", e);}});}// 异步发送xhr() {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest(); // 声明网络请求对象xhr.open(this.method, this.url, this.async); // 设置网络请求配置xhr.responseType = this.options.responseType; // 请求数据类型xhr.send(this.data); //发送参数xhr.onload = function () {// 请求成功后的网络执行的函数if (xhr.status == 200) {//状态码resolve(xhr.response);} else {reject({status: xhr.status, error: xhr.statusText });}};xhr.onerror = function (error) {reject(error);};});}}export {getByElementIds, HuaAjaxTools };// 使用实例import {HuaAjaxTools } from "../utils.js";HuaAjaxTools.get("/txapi/everyday/index",{responseType: "json" },{key: "2cab6669e9d6766c2990eccfa3253ee5",}).then((result) => {console.log(result);},(reject) => {console.log(reject);});HuaAjaxTools.post("/txapi/everyday/index",{responseType: "json",headers: {"Content-Type": "application/json;charset=utf-8",},},{key: "2cab6669e9d6766c2990eccfa3253ee5",}).then((result) => {console.log(result);},(reject) => {console.log(reject);});HuaAjaxTools.jsonp("/su", {wd: "花森", // 参数关键值cb: "handleSuggestion", // 回调函数}).then((success) => {console.log(success);},(error) => {console.log(error);});

2.fetch

FETCH是JS升级后提供的更简便的网络请求的操作方法,内部使用Promise完成请求,使用response.json()接收JSON类型数据,使用response.text()接收text类型数据。

get

fetch("/weather_mini?city=%E9%87%91%E5%B7%9E%E5%8C%BA").then((res) => {return res.json();}).then((data) => {console.log(data);});

post

发送的JSON类型需要设置请求头为application/json;charset=utf-8

fetch(`链接`, {method: 'POST',headers: {'Content-Type': 'application/json;charset=utf-8',},body: JSON.stringify({name: '花森' }),}).then((response) => {return response.json()}).then((data) => {console.log(data)})

正则表达式

正则表达式是用于匹配字符串中字符组合的模式,正则表达式是在宿主环境下运行,不是一门单独的语言,几乎主流语言(js/php/node.js)等都存在正则表达式语法。

1.声明正则

// 字面量let hs = "huasenjio";console.log(/u/.test(hs));//true "//"字面量形式写法但不可以在其中使用变量console.log(/hs/.test(hs)); //false 变量形式被当做字符型去匹配console.log(eval(`/${hs}/`).test(hs)); //true 通过eval函数实现解析变量// 对象形式let hs = "huasenjio.top";let zhuqi = ".top";let reg = new RegExp(zhuqi); // 匹配规则console.log(reg.test(hs)); //true

2.选择符

|这个符号带表选择修释符,选择符左右两侧仅能匹配一个。

let hs = "森酱生气了";console.log(/(森酱|猪琦)生气了/.test(hs));

3.字符转义

转义用于改变字符的含义,例如/在是正则符号的边界,如果想要输入网址匹配,则需要通过\/转移成字符串的含义。

const url = "https://";console.log(/https:\/\//.test(url)); //true

4.字符边界

使用字符边界符用于控制匹配内容的开始与结束约定

let hs = "n.huasenjio.top";console.log(/^n\./.test(hs)); //n.打头的字符串会被匹配

5.元子字符

元字符是正则表达式中的最小元素,仅代表单一一个字符。

let hd = "huasenjio ";console.log(hd.match(/\d/g)); // ["2", "0", "1", "0"]console.log(hd.match(/\d/)); // ["2"]console.log(hd.match(/\d+/)); // [""]

6.模式修饰

正则表达式在执行时会按他们的默认执行方式进行匹配

// 全域g,忽略大小写i。let hd = "huasenJIO ";console.log(hd.match(/Jio \d+/gi)); // ["JIO "]// u配合属性别名,L表示字母,P表示标点符号,\p{sc=Han}表示中国汉字。console.log(hd.match(/\p{L}+ \d+/gu)); // ["huasenJIO "]// RegExp对象lastIndex 属性可以返回或者设置正则表达式开始匹配的位置,必须配合g模式使用,匹配完成时lastIndex会被重置为0且对exec有效。let hs = `花森导航网址`;let reg = /\p{sc=Han}/gu;reg.lastIndex = 2; // 正则遍历的脚步while ((res = reg.exec(hs))) {console.log(reg.lastIndex);console.log(res[0]);}// ylet hs = "udunren";let reg = /u/y;console.log(reg.exec(hs));console.log(reg.lastIndex); //1console.log(reg.exec(hs)); //nullconsole.log(reg.lastIndex); //0

7.原子表

在一组字符中匹配某个元字符就要将其加入[]

let hs= "huasenJIO ";console.log(hs.match(/[hua]/g)); // ["h", "u", "a"] 匹配到h、u、a中任意的单个字符

8.原子组

多个元子当成一个整体匹配,可以通过元子组完成,用括号()进行包裹。

const hs = `<h1>huasenjio</h1>`;console.log(/<(h1)>.+<\/\1>/.test(hs)); //true

使用分组

$n指在替换时使用匹配的分组数据,下面是匹配h标题标签替换成p段落标签,并引用第二个分组的数据放入。

let hs = `<h1>花森酱</h1><span>huasen</span><h2>Jio</h2>`;// 第一个分组(h[1-6]),第二个分组([\s\S]*)。let reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi;console.log(hs.replace(reg, `<p>$2</p>`));

9.重复匹配

基本使用

如果要重复匹配一些内容时我们要使用重复匹配修饰符

let hd = "hssss";console.log(hd.match(/hs+/i)); //hssss

禁止贪婪

正则表达式在进行重复匹配时,默认是贪婪匹配模式,当后面还存在符合的字符就一直贪婪匹配下去,这时就可以使用禁止贪婪,可以通过?进行修饰。

// 满足表达式即可不贪婪下去let str = "1234";console.log(str.match(/\d+/)); //1234console.log(str.match(/\d+?/)); //1console.log(str.match(/\d{2,3}?/)); //12console.log(str.match(/\d{2,}?/)); //12<h1>huasen</h1><h2>花森</h2><h3></h3><h1></h1><script>let body = document.body.innerHTML;let reg = /<(h[1-6])>[\s\S]*?<\/\1>/gi; // 禁止贪婪 ["<h1>huasen</h1>","<h2>花森</h2>","<h3></h3>","<h1></h1>"]let reg = /<(h[1-6])>[\s\S]*<\/\1>/gi; // 开启贪婪 ["<h1>huasen</h1>↵ <h2>花森</h2>↵ <h3></h3>↵ <h1></h1>"]console.table(body.match(reg));</script>

10.字符串方法

search

search() 方法用于检索字符串中指定的子字符串,也可以使用正则表达式搜索,返回值为索引位置。

let str = "huasenjio.top";console.log(str.search("top")); // 10下标匹配到console.log(str.search(/\.top/i)); // 9

match

直接使用字符串搜索

let str = "huasenjio.top";console.log(str.match("top")); // 返回匹配的参数console.log(str.match(/\.top/i)); // 返回匹配的参数

split

通过正则匹配到的符号进行字符串分割

let str = "/02-12";console.log(str.split(/-|\//));

replace

replace方法不仅可以执行基本字符替换,也可以进行正则替换,下面替换日期连接符。

let str = "/02/12";console.log(str.replace(/\//g, "-")); //-02-12

11.正则方法

test

使用正则判定字符串是否存在特定符号,检测输入的邮箱是否合法。

<body><input type="text" name="email" /></body><script>let email = document.querySelector(`[name="email"]`);email.addEventListener("keyup", e => {console.log(/^\w+@\w+\.\w+$/.test(e.target.value));});</script>

exec

不使用g修饰符时与match方法使用相似,使用g修饰符后可以循环调用直到全部匹配完。

<div class="content">花森导航中的作者花森不断分享种花技巧</div><script>let content = document.querySelector(".content");let reg = /(?<tag>花)/g;let num = 0;while ((result = reg.exec(content.innerHTML))) {num++;}console.log(`花共出现${num}次`);</script>

11.断言匹配

断言虽然写在扩号中但它不是组,所以不会在匹配结果中保存,可以将断言理解为正则中的条件。

(?=exp)

零宽先行断言?=exp匹配后面为exp的内容

(?<=exp)

零宽后行断言?<=exp匹配前面为exp的内容

(?!exp)

零宽负向先行断言后面不能出现exp指定的内容

(?<!exp)

零宽负向后行断言前面不能出现exp指定的内容s

window对象

window 是客户端浏览器对象模型的基类,window对象也称BOM是JavaScript的全局对象,每一个标签页就是一个独立的窗口独立的BOM对象,通过BOM对象我们可以操作当前窗口,进行进一步操作!

1.对象分类

Window,客户端js的顶层对象,当body和frameset标签出现时window对象就会自动创建;navigator,包含客户端浏览器的信息;screen,客户端屏幕信息;history,浏览器窗口访问过的URL信息;location,包含当前网页文档的URL信息;document,整个页面文档标签信息;

2.系统交互

window定义了3个人机交互的方法

alert("你好");console.log(confirm("你的电脑将被攻击")); // trueconsole.log(prompt("花森导航的网址为:")); // 输入值

3.打开窗口

使用 window 对象的 open() 方法可以打开一个新窗口并返回一个window对象

//window.open (URL, name, features, replace)let a = window.open("http://n.huasenjio.top/", "zhuzhu");console.log(a); // 返回window对象

URL跳转网页的链接;name新窗口的名称,注意不是网站标题;feature声明了新窗口要显示的标准浏览器的特征;replace确定打开的网页在浏览器记录中添加一条访问记录还是替换掉原来网页中的条目;

新创建的window对象有一个opener属性指向原始的网页对象

win = window.open(); //打开新的空白窗口win.document.write("<h1>这是新打开的窗口</h1>"); //在新窗口中输出提示信息win.focus(); //让原窗口获取焦点win.opener.document.write("<h1>这是原来窗口</h1>"); //在原窗口中输出提示信息console.log(win.opener == window); //检测window.opener属性值

4.关闭窗口

关闭当前窗口,使用 window.closed 属性可以检测当前窗口是否关闭。

window.close();

5.定时器

6.框架集合

每一个frame标签都是一个window对象,使用frame可以访问每一个window对象,frames是一个数据集合,存储者所有的window对象,下标从0开始,访问顺序从左到右从上到下,通过parent.frames[0]访问对应的frame框架的window对象。

<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><frameset rows="50%,50%" cols="*" frameborder="yes" border="1" framespacing="0"><frameset rows="*" cols="50%,*,*" framespacing="0" frameborder="yes" border="1"><frame src="http://n.huasenjio.top/" name="left" id="left" /><frame src="middle.html" name="middle" id="middle"><frame src="right.html"name="right" id="right"></frameset><frame src="http://huasenjio.top/" name="bottom" id="bottom"></frameset><body></body></html>

7.窗口大小位置

方法 moveTo() 可以将窗口的左上角移动到指定的坐标,方法 moveBy() 可以将窗口上移、下移、左移、右移指定数量的像素,方法 resizeTo() 和 resizeBy() 可以按照绝对数量和相对数量调整窗口的大小。

调整窗口位置

moveTo() 和 moveBy()

窗体大小

resizeTo() 和 resizeBy()

滚动条

scrollTo() 和 scrollBy()

navigator对象

navigator 对象存储了与浏览器相关的基本信息,例如名称、版本、系统信息,通过window.navigator 可以引用该对象,读取客户端基本信息。

1.浏览器检测方法

特征检测法

特征检测法就是根据浏览器是否支持特定的功能来决定相应操作的非精确判断方式,但却是最安全的检测方法,仅仅在意浏览器的执行能力,那么使用特征检测法就完全可以满足需要。

// 检测当前浏览器是否支持 document.getElementsByName 特性,不支持则使用document.getElementsByTagName 特性,进行兼容处理。if (document.getElementsByName) {//如果存在则使用该方法获取a元素var a = document.getElementsByName ("a");} else if (document.getElementsByTagName) {//如果存在则使用该方法获取a元素var a = document.getElementsByTagName ("a");}

字符串检测法

客户端浏览器每次发送 HTTP 请求时,请求头中有一个user-agent(用户代理)属性,使用用户代理字符串检测浏览器类型,可以通过navigator.userAgent获取客户端信息。

var s = window.navigator.userAgent;//简写方法var s = navigator.userAgent;console.log(s);//返回类似信息:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36

2.检测版本号

获得客户端信息后通过正则表达式筛选对应信息即可

location对象

location 对象存储了当前文档位置(URL)相关的信息,例如网页的地址,访问历史信息,可以通过window.location进行访问。location中定义有一下8个属性:

1.对象属性

2.对象方法

reload

可以重新装载当前文档

replace

可以装载一个新文档而无须为它创建一个新的历史记录,即不能通过单击“返回”按钮返回当前的文档,window.locationdocument.location不同,前者是location对象,后者是一个只读字符串,当服务器发生重定向,document.location 包含的是已经装载的 URL,而 location.href 包含的则是原始请求文档的 URL。

history对象

history 对象存储了库互动浏览器的浏览历史,通过window.history可以访问对象,取到最近访问且有限的条目URL信息。HTML5 之前,为了保护客户端浏览信息的安全和隐私,history 对象禁止 JavaScript 脚本直接操作这些访问信息。HTML5 新增了一个 History API,该 API 允许用户通过 JavaScript 管理浏览器的历史记录,实现无刷新更改浏览器地址栏的地址。

1.基本操作

window.history.back();1// 历史记录中后退,等效于在浏览器的工具栏上单击“返回”按钮。window.history.forward();1 // 历史记录中前进,等效于浏览器中单击“前进”按钮。window.history.go(1); // 移动到指定的历史记录点window.history.length; // 访问记录长度window.history.state; // 当前标签的访问记录

2.添加修改

添加和修改历史记录条目,HTML5 新增 history.pushState() 和 history.replaceState() 方法,允许用户逐条添加和修改历史记录条目。

pushState

pushState可以修改referer值,调用该方法后创建的 XMLHttpRequest 对象会在 HTTP 请求头中使用referer值,referrer 的值则是创建 XMLHttpRequest 对象时所处的窗口的 URL。 pushState() 方法类似于设置 window.location=’#foo’,它们都会在当前文档内创建和激活新的历史记录条目,pushState() 方法永远不会触发 hashchange 事件。

var stateObj = {foo : "bar"};history.pushState (stateObj, "标题", "bar.html"); // 当前标签URL变为XXX/bar.html

replaceState

history.replaceState() 与 history.pushState() 用法相同,pushState() 是在 history 栈中添加一个新的条目,replaceState() 是替换当前的记录值。

3.popstate事件

每当激活的历史记录发生变化时,都会触发 popstate 事件。如果被激活的历史记录条目是由 pushState() 创建,或者是被 replaceState() 方法替换的,popstate 事件的状态属性将包含历史记录的状态对象。

screen对象

screen 对象存储了客户端屏幕信息,可以用来探测客户端硬件配置,实现根据显示器屏幕大小选择使用图像的大小,根据颜色深度选择使用 16 色图像或 8 色图像。

function center(url) {//窗口居中处理函数var w = screen.availWidth / 2; //获取客户端屏幕宽度的一半var h = screen.availHeight / 2; //获取客户端屏幕高度的一半var t = (screen.availHeight - h) / 2; //计算居中显示时顶部坐标var l = (screen.availWidth - w) / 2; //计算居中显示时左侧坐标var p = "top=" + t + ",left=" + l + ",width=" + w + ",height=" + h; //设计坐标参数字符串var win = window.open(url, "url", p); //打开指定的窗口,并传递参数win.focus(); //获取窗口焦点}console.log(screen);center("http://huasenjio.top"); //调用该函数

document对象

浏览器加载文档会自动构建文档对象模型(DOM),文档中每个元素都映射到一个数据集合。

1.动态生成文档内容

使用 document 对象的 write() 和 writeln() 方法可以动态生成文档内容

write()

生成内容追加在文档树的末尾

document.write ('Hello,World');

writeln()

writeln() 方法与 write() 方法完全相同,只不过在输出参数之后附加一个换行符。

XMLHttpRequest

XMLHttpRequest(XHR)对象用于与服务器交互,通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,更新页面的局部内容。

1.定义声明

构造函数用于初始化一个XMLHttpRequest实例对象

let xhr = new XMLHttpRequest();

2.属性类型

3.常用方法

4.事件监听

5.简单示例

let xhr = new XMLHttpRequest();// 开启请求任务 readyState=0xhr.open("GET","/weather_mini?city=%E9%87%91%E5%B7%9E%E5%8C%BA");xhr.send(); //发送请求 readyState=1// 监听readyState变化来等待服务器响应xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {console.log("获取数据:" + xhr.responseText); // readyState=4} else {console.log("readyState:" + xhr.readyState); // readyState=2 readyState=3}};// 请求成功回调xhr.addEventListener("load", () => {console.log("请求成功会触发事件!");});

6.XMLHttpRequest2新特性

旧版本只支持文本格式传输,无法用于读取上传二进制文件,接收和传送时无法查看进度信息,不能跨域请求,第二版本优化功能具有一下特点:

设置HTTP请求的时限;可以使用FormData对象管理表单数据;上传文件;可以请求不同域名下的数据(跨域请求);获取服务器端的二进制数据;可以获得数据传输的进度信息;

formData

ajax操作往往用来传递表单数据,所以HTML5新增了一个FormData对象,可以模拟表单数据。

// 实例化对象var formData = new FormData(); // 添加表单项formData.append('username', '张三');formData.append('id', 123456);// 发送格式xhr.send(formData);

上传文件

新版XMLHttpRequest对象可以上传文件,假定files是一个"选择文件"的表单元素(input[type="file"]),我们同样使用formData格式进行传输。

var formData = new FormData();for (var i = 0; i < files.length;i++) {formData.append('files[]', files[i]);}xhr.send(formData);

接收二进制数据

服务器取回二进制数据是使用新增的responseType属性,如果服务器返回文本数据则属性值为”text“,不要用IE6这种古董浏览器还可以支持其他格式数据,例如设置传回属性是json字符串格式。

var xhr = new XMLHttpRequest();xhr.open('GET', '/weather_mini?city=%E9%87%91%E5%B7%9E%E5%8C%BA');xhr.responseType: "json",xhr.send();xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {console.log(xhr.response); // readyState=4} else {console.log("readyState:" + xhr.readyState); // readyState=2 readyState=3}};

进度信息

新版本的XMLHttpRequest对象在传输数据时可以通过监听progress事件用来返回进行信息,它分成上传下载情况,下载的progress事件属于XMLHttpRequest对象,上传的progress事件属于XMLHttpRequest.upload对象。

xhr.onprogress = 方法;xhr.upload.onprogress = 方法;// 实例xhr.onprogress = function (event) {console.log(event);if (event.lengthComputable) {var percentComplete = event.loaded / event.total;}};

HTTP请求头

HTTP协议报文头部由方法、URL、HTTP版本、HTTP首部字段等部分构成,携带报文主题大小,使用语言,认证信息详细信息。

1.首部字段类型

通用首部字段;请求首部字段;响应首部字段;实体首部字段;

2.通用首部字段

3.请求首部字段

4.响应首部字段

5.实体首部字段

6.服务Cookie的首部字段

属于响应首部字段,用于服务端开始管理客户端状态的交互的载体。

声明

一切资源均来源于各大免费论坛社区,只提供互联网数据收录查询服务且不会存在储存用户数据和贩卖数据。资源仅供大家学习交流,请勿用于商业用途,造成损失及法律责任与本人无关。如有侵犯您的权益,请联系小生,吾将积极配合!

寄语

前段时间在金蝶软件园实习结束,回来就开发了一款网络导航应用,因为由于搜索引擎的竞价很严重,所以获取到优质内容已经不再容易了,这也是我开发这个网站的初衷,网站无广告,推荐的网站和工具都是很良心,涉及生活的方方面面,生活上的娱乐追剧,学习上的奋笔直书,它都是你的好帮手,我相信你给它几分钟的时间浏览,你就会喜欢上它了。尽管我只是一个大三的学生,但服务器运维费均是我自掏腰包,凭着一份爱好在支撑!vue项目第一次进会慢一些,同样也想听到大家的宝贵意见,不出意外的话我会将它开源,希望大家能够支持鼓励!

花酱与猪 - 自定义网络资源导航

联系

🐧企鹅:184820911

📕邮箱:184820911@

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。