【前端之旅】JavaScript进阶笔记
一名软件工程专业学生的前端之旅,记录自己对三件套(HTML、CSS、JavaScript)、Jquery、Ajax、Node.js、Vue、Axios、小程序开发(Uniapp)以及各种UI组件库、前端框架的学习。
【前端之旅】Web基础与开发工具
【前端之旅】手把手教你安装VS Code并附上超实用插件指南
【前端之旅】HTML大总结
【前端之旅】CSS三万字总结
【前端之旅】JavaScript学习笔记
【前端之旅】Web前端发展简史
【前端之旅】JavaScript进阶笔记
【前端之旅】Node.js安装教程(图文版)
【前端之旅】uni-app学习笔记
【前端之旅】手把手教你将uView UI配置到uniapp项目中
JavaScript进阶笔记
- 一、JavaScript代码调试
- 二、JavaScript语言
- 2.1 语法特点
- 2.2 变量的声明
- 2.3 数据结构
- 2.4 函数
- 2.5 JavaScript对象
- 2.6 原型链和对象的继承
- 2.7 数组Array
- 三、DOM&BOM
一、JavaScript代码调试
-
代码调试:检查代码的执行路径和变量的值与你设计程序时期望的是否一致。
-
在Chrome、Edge浏览器中调试
- alert()
- console.log():常用,Ctrl+Shift+I -> Console
- 开发者工具:设断点, Ctrl+Shift+I -> Sources
-
debugger 关键词
- debugger 关键词会停止 JavaScript 的执行,并调用(如果有)调试函数。
二、JavaScript语言
2.1 语法特点
- 弱类型语言:变量声明时不指定类型,一个变量可以赋不同数据类型的值
- 区分大小写:Student与student是两个不同的标识符
- 变量命名规范:一般全部小写,多个单词,从第二个单词开始首字母大写
- 关于语句结束后的分号:可选,但建议加上
2.2 变量的声明
-
使用var或let关键字
- var关键字:传统方式,只有全局作用域和函数作用域
- let关键字:现代JavaScript中的方式
-
两者的区别:
- let声明的变量只在代码块内有效,实现块级作用域,而var声明的变量没有块级作用域,只有函数作用域和全局作用域
- var声明的变量存在变量提升,而let声明的变量不存在变量提升
-
作用域
- 变量的作用域(Scope),指变量在脚本代码中可读、写的有效范围
- ECMAScript 6之后,变量的作用域有全局作用域、函数作用域和块级作用域,ECMAScript 6之前,无块级作用域
- var声明的变量只有全局作用域和函数作用域,let声明的变量有全局作用域、函数作用域和块级作用域
- 因var存在变量提升,所以var变量的全局作用域指整个页面的JavaScript代码,函数作用域指整个函数;而let和const不存在变量提升,故全局作用域指声明语句开始到整个页面脚本代码,函数和块级作用域指从声明语句到函数/块结束
-
块级作用域例子
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1 --- if (true) { let test = true; // 使用 "let" } alert(test); // Error: test is not defined --- if (true) { var test = true; // 使用 "var" 而不是 "let" } alert(test); // true,变量在 if 结束后仍存在
-
函数作用域例子
function f1() { var a = 1; let b = 2; } console.log(a); console.log(b); // 报错,a和b均未定义
-
变量提升:即变量可以在声明之前使用,值为undefined。
// 未声明变量,直接使用 console.log(foo); // 报错ReferenceError --- // 使用var,先使用变量,后声明并赋值 console.log(foo); // 输出undefined var foo = 2; --- // let 的情况 console.log(bar); // 报错ReferenceError let bar = 2;
- var声明提高到所属作用域的顶端:如var声明变量在任何函数之外,则变量的作用域会提升到整个代码的最高处(全局变量);在函数体内使用var声明变量,其作用域会被提升至所在函数。
- 注意:
- 1.仅声明被提升,赋初值仍在原位置
- 2.变量提升不会跨script块
- let声明的变量不存在变量提升
-
使用const声明常量
-
常量在脚本代码整个运行过程中保持不变
-
使用const声明常量时,必需赋初值
-
const 常量名 = 值
-
常量不能重复声明
const pi = 3.1415926;
-
2.3 数据结构
-
typeof 操作符
-
typeof 操作符返回一个字符串,表示未经计算的操作数的类型
-
-
数字类型
-
包括整型数字和浮点型数字
-
整型:十进制、十六进制(0X或0x开头)、八进制(ECMAScript标准不支持,严格模式下不支持)
-
浮点型:3.1 .666 1.23e11 // 1.23 * 1011 2.321E-12 // 2.321 * 10-12
let a = 10; let b = 1.2; let c = 2.1E-2; console.log(typeof a); // number console.log(typeof b); // number console.log(typeof c); // number
-
-
字符串类型
- 由单引号或双引号括起来的一组16位Unicode字符组成的字符序列
- 转义字符:
\n \r \"
等 - 查阅String文档
-
字符串模板
- 允许嵌入表达式的字符串字面量
- 使用反引号 (``键盘数字1左边) 来代替普通字符串中的用双引号和单引号
- 字符串模板保留其中的换行,可以包含特定语法(${expression})的占位符
var a = 5; var b = 10; console.log("Fifteen is " + (a + b) + " and\nnot " + (2 * a + b) + "."); var a = 5; var b = 10; console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`); // "Fifteen is 15 and // not 20."
-
布尔类型
-
true false a==1
-
进行算术操作时,true转换为1,false转换为0
var b = true; console.log(typeof b); // boolean console.log(true * true); // 1 * 1 = 1
-
-
null与undefined
- null表示空或不存在的引用,参与算术运算时自动转换为0,布尔运算时转换为false
- undefined表示变量已声明但没有赋值,布尔运算时转换为false,出现的典型情况:
- 变量声明后没有赋值
- 调用函数时,应提供的参数未提供
- 对象的属性没有赋值时
- 数组定义后没有给元素赋值,元素的值
- 函数没有返回值,默认返回值
-
对象类型
- 类似与Java语言中的对象,数据+处理数据的函数,数据称为对象的属性,函数称为对象的方法
- 内置对象:window document Date Math等等
- 自定义对象
- 数组(Array)是一种内置对象
- typeof 返回值都是object
-
函数类型
-
有名函数,函数名称为函数变量;匿名函数赋值于某变量后,该变量为函数变量
-
函数变量的值为整个函数定义语句
let fn1 = function() { console.log('调用fn1'); } let fn2 = () => { console.log('调用fn2'); } function fn3() { console.log('调用fn3'); } console.log(typeof fn1); // function console.log(typeof fn2); // function console.log(typeof fn3); // function console.log(fn1); // function() { console.log('调用fn1'); } console.log(fn2); // () => { console.log('调用fn2'); } console.log(fn3); // function fn3() ... ...
-
-
数据类型转换 ——加号(+)
-
加号(+)两边存在字符串或引用类型时,加号为字符串连接符,非字符串操作数自动转换为字符串,引用类型调用其toString()方法转换为字符串。
console.log("我是" + 2019 + "级学生"); // "我是2019级学生" console.log("15" + 5); // 155 console.log("abc" + true + 1); // abctrue1 console.log("abc" + null + 2); // abcnull2 console.log(true + 1 + "abc"); // 2abc console.log(null + 2 + "abc"); // 2abc console.log("abc" + function() {console.log('a')}); // abcfunction(){console … … console.log({name: '张三'} + 1); // [object Object]1 console.log({name: '张三', toString: function(){return '人员' + this.name}} + 1); // 人员张三1 console.log(1 + function() {console.log('a')}); // function() {console.log('a')}
-
加号(+)两边为数值、布尔、null或undefined类型时,加号为加法算术操作符,此时非数值操作数自动转换为数值型,若存在不能转换为数值的操作数(仅有undefined),结果为NaN。
console.log(true + 1); // 2, 1 + 1 console.log(undefined + 1); // NaN console.log(null + 2); // 2, 0 + 2 console.log(true + 1 + “abc”); // 2abc,true与1间的加号为算术加法,其结果(2)与“abc”间连接字符串 console.log("abc" + 2 + null); // abc2null
-
-
数据类型转换 ——其它操作符
-
减(-)、乘(*)、除(/)、取余(%)两边存在字符串时,字符串自动转换为数字(如有无法转换操作数,则结果为NaN)
-
++和–,字符串自动转换为数字,无法转换的结果为NaN
console.log("20" / 2); // 10 console.log("20" / "2"); // 10 console.log("20" - "a"); // NaN let n = "6"; let s = "a"; console.log(++n); // 7 console.log(++s); // NaN let a = true; console.log(++a); // 2
-
>、<、==、<=、>=
:表达式包含数字和字符串时,字符串自动转换为数字,不能转换成数字的字符串或其它类型值,表达式均返回false -
当比较两个字符串的大小时,按ASCII码比较,比较true false null 的大小时,按值比较( true=1, false=null=0)
console.log("10" > 9); // true console.log("a" > 0); // false console.log("a" <= 0); // false console.log(true > 0); // true console.log(null < 1); // true console.log(undefined < 1); // false
-
-
相等与严格相等
-
== 左右两边的值相等即返回true,数据类型可不相同
-
=== 左右两边的值相等,且数据类型相同,返回true
-
!= 和 !== 与以上类似,其区别在数据类型是否相同
console.log('15' == 15); // true console.log('15' === 15); // false console.log(true == 1); // true console.log(false == 0); // true console.log(true === 1); // false
-
-
显式类型转换
- Number(value) 全局函数,对value进行整体转换,value中包含无法转换为数字的部分时,转换失败,返回NaN
- parseInt(stringNum, [radix])全局函数,将stringNum转换为整数,radix为2-36之间的数字,指定转换时进制,进制为10时可省略
- parseFloat(stringNum)全局函数,将stringNum转换为浮点数
-
… 扩展运算符(对象展开符)
-
扩展运算符( spread )是三个点(…),其作用是将一个数组转为用逗号分隔的参数序列
-
常用于函数调用时传递参数
-
扩展运算符提供了数组合并的新写法。注:Array.concat()方法用于连接两个数组
-
2.4 函数
函数声明方法一:有名函数
function 函数名([参数列表]) {
函数体;
[return [表达式];]
}
函数声明方法二:函数表达式(匿名函数)
--不需要函数名
--常用于定义事件的回调函数
变量名 = function ([参数列表]) {
函数体;
[return [表达式];]
}
例:匿名函数作为回调函数
<script>
let btns = document.querySelectorAll("button");
btns.forEach(function (btn) { // 匿名函数
btn.addEventListener("click", function () { // 匿名函数
let bgColor = this.dataset.bgColor;
bind = setTimeout(function () { // 匿名函数
console.log(bgColor);
document.body.style.backgroundColor = bgColor;
}, 1000, bgColor);
});
});
</script>
例:匿名函数作为表达式赋值给变量
// 定义匿名函数并作为表达式赋值给变量
var fn = function(name) {
console.log(`Hello ${name}`); // 字符串模板
};
// 通过变量名调用匿名函数
fn('VUE'); // Hello VUE
函数参数:
-
参数列表可选,不指定类型,传递时可接受任意类型数据,调用时如实参未指定,默认值为undefined
-
从ES2015开始:可为参数指定默认值
函数中的this
- JavaScript中的this通常指向当前对象
- 当前对象指调用当前正在执行的函数(方法)的对象
- 在全局环境中:
- 非严格模式下,this指向window(浏览器环境)
- 严格模式下,this为undefined
- setTimeout和setInterval中延迟调用的方法都在全局环境中执行
定义函数方式三: Lambada 表达式
-
Lambada 表达式(箭头函数):比函数表达式更加简洁,不绑定this、arguments、super
-
语法形式1:(参数1, 参数2, …, 参数N) => { 函数声明; }
var f = (num1, num2) => { return num1*num2 }; console.log(f(3, 4)); // 12
-
箭头函数-语法形式2:(参数1, 参数2, …, 参数N) => 表达式(单一)
var f = (num1, num2) => num1*num2; console.log(f(3, 4)); // 12
-
箭头函数-语法形式3:单一参数 => {函数声明}
var f = num => num*num; console.log(f(3)); // 9
-
箭头函数-语法形式4:() => {函数声明} // 无参数
var f = () => 'hello world'; console.log(f()); // hello world
常用内置函数
2.5 JavaScript对象
JavaScript面向对象编程概述:
JavaScript中对象的定义为:无序属性的集合,其中的属性可以是基本值、对象或者函数(ECMA-262)。简单来说,对象就是一组无序的名称/值对。当值为基本值或对象时,这样的名称/值对称为对象的属性及属性值,当值为函数时,这样的名称/值对称为对象的方法。
JavaScript对象的创建:
方法一:调用Object()创建对象
方法二:使用对象字面量,相比方法一,更简洁
方法三:使用构造函数模式:构造函数实质上是具有如下特点的普通函数:
-
构造函数内通常使用this关键字对属性和方法赋值
-
通过 new 构造函数名()的形式调用构造函数创建对象
-
构造函数不需要返回值
-
注意:构造函数与普通函数无实质区别,普通函数也可以执行 new 普通函数(),只是函数内没有通过this对属性和方法赋值,创建的是空对象,构造函数也可以像普通函数那样被调用
function Student(num, name, dob, gender) { this.num = num; this.name = name; this.dob = dob, this.gender = gender; this.getAge = function () { let now = new Date(); let diffInMM = now - this.dob; return Math.trunc(diffInMM / (1000 * 60 * 60 * 24 * 365)); }; this.toString = function () { return `学号:${this.num},姓名:${this.name},年龄:${this.getAge()},性别:${this.gender}`; }; } let stu1 = new Student('2018111123', '张华', new Date('2001-10-20'), '女'); let stu2 = new Student('2018111124', '刘晓', new Date('2001-07-15'), '男'); console.log(stu1.toString()); console.log(stu2.toString());
原型(Prototype)
-
在JavaScript中,每个函数均有一个prototype(原型)属性,其值为一个对象,称为“原型对象”
-
所有的对象均有一个__proto__(proto前后两个下划线)属性,通过调用new 函数名()创建的对象,其__proto__属性指向该函数的prototype
-
将属性、方法放置在构造函数的prototype中,可实现所有实例对象共享这些属性和方法
2.6 原型链和对象的继承
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
使用class关键字定义类
-
现有的基于原型的继承的语法糖
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } }
-
不同于函数声明,类声明不会提升,先声明,后访问
let p = new Rectangle(); // ReferenceError class Rectangle {}
匿名类
// 匿名类
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name); // output: "Rectangle"
// 具名类
let Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name); // 输出: "Rectangle2"
2.7 数组Array
数组的定义
-
JavaScript中的数组为Array对象,Array为JavaScript语言内置对象
-
创建数组的两种方式,第一种方式更常用:
-
两种方式基本等效,除以下情况外:
数组元素的访问
console.log(a1[1]); // 15
console.log(a2[0]); // 软件工程
console.log(a3[a3.length - 1]); // 软件183
数组的常用属性
-
length属性: 可读写,读返回数组中元素的个数,写可增加或减少元素的个数
let a2 = ['软件工程', '计算机科学与技术', '网络工程']; console.log(a2.length); // 3 a.length = 5; console.log(a2); // (5) ["软件工程", "计算机科学与技术", "网络工程", empty × 2] a2.length = 2; console.log(a2); // (2) ["软件工程", "计算机科学与技术"]
-
push(e1, …, en):将参数指定的元素依次添加到数组的末尾,并返回新数组的长度
let a = ['软件181', '软件182', '软件183']; console.log(a.push('软测181', '软测182')); // 5 console.log(a); // (5) ['软件181', '软件182', '软件183', '软测181', '软测182']
-
pop():弹出(返回,并在数组中删除)数组最末一个元素
let c = a.pop(); console.log(c); // 软测182 console.log(a); // (4) ["软件181", "软件182", "软件183", "软测181"]
-
unshift(e1, …, en): 将参数e1, …, en依次添加到数组的开头,并返回新数组的长度。
let b = ['软件181', '软件182', '软件183']; console.log(b.unshift('软测181', '软测182')); // 5 console.log(b); // (5) ["软测181", "软测182", "软件181", "软件182", "软件183"]
-
shift(): 删除数组的第一个元素,并返回该元素。
let c2 = b.shift(); console.log(c2); // 软测181 console.log(b); // (4) ["软测182", "软件181", "软件182", "软件183"]
-
splice(index, count[, e1, …, en): 在数组中删除、替换或添加元素
-
删除元素:只提供index和count两个参数,删除从index位置开始的count个元素,并返回删除的元素数组
let a = [1, 2, 3, 4, 5]; let e = a.splice(2, 1); // 从索引为2的位置开始,删除1个元素 console.log(e); // (1) [3],被删除元素组成的数组 console.log(a); // (4) [1, 2, 4, 5],改变后的数组
-
替换元素:提供三个以上参数,前两个参数的作用与1相同,在1删除的基础上,第三个及之后的参数被插入到index位置,返回值与1相同
let a2 = [1, 2, 3, 4, 5]; // 从索引为2的位置开始,删除2个元素,并将元素10播放到索引为2的位置 let e2 = a2.splice(2, 2, 10); console.log(e2); // (2) [3, 4] console.log(a2); // (4) [1, 2, 10, 5] a2.splice(1, 1, 6); // a2[1] = 6 console.log(a2); // (4) [1, 6, 10, 5 ]
-
添加元素:提供三个以上参数,第二个count参数值为0,此时不删除元素,只在index位置插入第三个及之后的参数,返回值为空数组
let a3 = [1, 2, 3, 4, 5]; // 从索引为2的位置开始,删除0个元素,并将元素10播放到索引为2的位置 let e3 = a3.splice(2, 0, 10); console.log(e3); // [] console.log(a3); // (6) [1, 2, 10, 3, 4, 5]
-
-
sort()或sort(compareFunction):对数组元素排序
-
不提供参数时,按字典顺序(Unicode从小到大)排序
-
提供compareFunction时,如compareFunction返回正值则交换元素
let a = [50, 6, 200, 3, 7, 80]; // 按字典排序 console.log(a.sort()); // (6) [200, 3, 50, 6, 7, 80] // 当v1 - v2为正值时交换元素 console.log(a.sort((v1, v2) => v1 - v2)); // (6) [3, 6, 7, 50, 80, 200] console.log(a.sort((v1, v2) => v2 - v1)); // (6) [200, 80, 50, 7, 6, 3]
-
-
forEach(function(currentValue[, index[, array]])[, thisArg]): 对数组中的每个元素执行一次回调函数
let a = ["软测181", "软测182", "软件181", "软件182", "软件183"]; let eUl = document.createElement('ul'); a.forEach((c) => { let eLi = document.createElement('li') eLi.textContent = c; eUl.appendChild(eLi); console.log(this); // window }); document.body.appendChild(eUl);
-
filter(function(currentValue[, index[, array]])[, thisArg]): 对所有元素依次应用回调函数,返回一个新数组,包含所有回调函数返回true的元素
let a = ['软件181', '软件182', '软件183', '软测181', '软测181']; // 包含所有“软件”开始的元素,输出 (3) ["软件181", "软件182", "软件183"] console.log(a.filter(item => item.startsWith('软件')));
-
map生成一个新的数组,其中的元素为指定数组中的元素应用回调函数返回值
let a = ["软测181", "软测182", "软件181", "软件182", "软件183"]; // 生成一个新数组,元素为li元素 let aLi = a.map(item => { let eLi = document.createElement('li'); eLi.textContent = item; return eLi; }); let eUl = document.createElement('ul'); aLi.forEach(item => eUl.appendChild(item)); document.body.appendChild(eUl);
-
reduce(function(previousResult, currentValue[, index[, array]])[, initialValue]):使用回调函数对数组中的每个元素进行处理,并将处理结果汇总返回,其中:
-
previousResult: 必须提供,上一次执行的结果,第一次执行时,如提供initialValue参数,则previousResult的值为initialValue,否则previousResult的值为数组中的第一个元素, 且currentValue为数组中的第二个元素
-
currentValue:必须提供,当前正在处理的元素
function sum(...numbers) { return numbers.reduce((prev, curr) => prev + curr, 0) } console.log(sum(1, 2, 3)); // 6 console.log(sum(10, 5, 100, 20)); // 135
-
三、DOM&BOM
阿里云盘:JavaScript-DOM.pdf