深拷贝 浅拷贝
只有引用(复杂)数据类型, 是在堆里开辟空间, 那么就会有深拷贝还是浅拷贝问题存在
正则表达式
创建方式 /正则表达式/
使用.test() 传入字符串 如果符合正则条件则返回true
javaScript - 脚本语言运行在浏览器的html文件中
作用: 编写代码实现交互
JS组成:
ES6: ECMAScript第6代标准
浏览器会向下兼容 - 既支持先进的也支持原来的 (所以新学的和原来的都可以使用)
大括号 - let声明的变量 - 块级作用域 - 只能在大括号里使用
// 作用域:
// 1. 函数级作用域: 在function内, var声明的变量, 只能在函数内使用
function myFn() {
var myName = "小传";
console.log(myName); // 小传
}
myFn();
// console.log(myName); // 报错: myName is not defined
// 2. 块级作用域: (if大括号/for大括号/while大括号/function大括号/独立的大括号), 在大括号内, 用let声明的变量, 形成块级作用域, 只能在大括号内使用
if (true) {
let youName = "老李头";
console.log(youName); // 老李头
}
// console.log(youName); // 报错: youName is not defined
// 总结:
// (1): 作用域其实就是影响变量访问的方式
// (2): 在大括号里使用let 声明的变量 - 是块级作用域
避免for循环 变量 泄露到 全局作用域上
// 1. 避免全局污染问题
var i = 100;
for (var i = 0; i < 10; i++){
}
console.log(i); // 10
// 问题: 变量泄露到了全局上
// 解决: ES6引入了let来解决
var j = 90;
for (let j = 0; j < 10; j++){
}
// console.log(j); // 90
避免污染window原有的全局变量
// 问题: 变量叫alert, 会覆盖原有功能
// var alert = function(){};
// window.alert("我要弹窗");
// 使用let呢?
// let alert = function(){};
// window.alert("我要弹窗");
let a = 10;
console.log(window.a);
// 总结: let声明的全局变量 - 不会挂载到window上(防止污染window原有的全局变量)
// 回顾: ES5, var 变量提升
function myFn() {
console.log(theName); // undefined
var theName = "我的";
}
myFn();
// 变量提升: 在代码编译期间, 只会把声明, 提升到当前作用域的第一行
// 1. let不存在变量提示 - 暂时性死区
function youFn() {
// console.log(youName); // 报错: Cannot access 'youName' before initialization, 初始化之前不能访问youName
let youName = "let声明的变量";
// 2. 同一个作用域下 - let变量名不可以重复
// let youName = 100; // Identifier 'youName' has already been declared
}
youFn()
// 好处: 去掉不严谨的代码格式, 一定要先声明变量, 再使用.
// 注意: let声明的变量不能重复 (在同一个作用域下)
例子: 循环遍历加监听, 使用let取代var是趋势
var liList = document.querySelectorAll("#myUL>li");
// 方式1. 自定义属性
// for (var i = 0; i < liList.length; i++) {
// liList[i].index = i;
// liList[i].onclick = function(){
// console.log(this.index);
// }
// }
// 方式2. 闭包
// for (var i = 0; i < liList.length; i++) {
// (function (ind) {
// liList[ind].onclick = function () {
// console.log(ind);
// }
// })(i);
// }
// 方式3. let
for (let i = 0; i < liList.length; i++) {
liList[i].onclick = function () {
console.log(i);
}
}
// 总结:
// for循环比较特殊, let每次都会产生一个父级的块级作用域, JS引擎内部会自动记录i的值, 分配给下个循环时, 父级作用域上i的初始值
// 内部使用时, {}里没有声明过i, 所以自然会向外层父级作用域查找
作用: 与var和let一样, 也是用于定义变量的
// let / var 声明的是变量, 变量的值可以重新赋予
// 1. 值不可以被重新赋予, 其他特点同let
const MYCONST = "我是常量";
// MYCONST = "123"; // Assignment to constant variable. 常量不可以重新赋值, 只能在初始化时, 给一次值
// 2. 应用场景
// 数学, 圆的周长2πr, π的值是固定的
const PI = 3.1415926;
console.log(2 * PI * 3);
// 总结: 大家遵循规范, 为了和普通的变量做一个区分, 要求常量必须都大写
const声明的变量, 保存的是对象/数组的堆内存地址
const修饰的变量的值不能重新赋值, 但是指向的对象/数组可以操作
const OBJ = {
name: "张三",
age: 19
};
// 疑问: 如果const修饰的变量, 保存的是对象/数组, 那么对象/数组能否修改?
// const修饰的变量不能被重新赋值, 但是指向的对象可以添加/删除key+value
OBJ['age'] = 18;
console.log(OBJ);
// OBJ = {}; // 会报错, 对象本身不能被重新赋值, 这是在修改OBJ对象本身, 重新赋予对象
总结
关键字 | 初始值 | 块级作用域 | 变量提升 | 重新赋值 | 通过window调用 |
---|---|---|---|---|---|
let | - | √ | x | Yes | No |
const | YES | √ | x | No | No |
var | - | x | √ | Yes | Yes |
let 和 const 是块级作用域, 不会挂载到window上, 没有变量提升
var 是函数级作用域
const必须有初始值, 之后无法重新赋予
概念: ES6 允许 按照一个模式, 从目标结构(数组/对象)中提取值, 赋予给变量, 解构的目的方便取值.
关键字:模式 数组/对象 提取 赋值 变量
目的: 优化取值赋值的过程
// 什么是解构赋值: ES6 允许 按照一个模式, 从数组/对象中提取值, 赋予给变量
// 例1: 数组解构举例
let arr = ["春天", "夏天", "秋天", "冬天"];
// let winOne = arr[0];
// let winTwo = arr[1];
// let winThree = arr[2];
// let winFour = arr[3];
// 写起来很麻烦, 解构赋值怎么写呢?
let [winOne, winTwo, winThree, winFour] = arr;
// 右侧是数组, 左侧[] 对应模式
console.log(winThree);
// 例2: 对象解构举例
let obj = {
username: "小花",
age: 18
}
// let myUserName = obj['username'];
// let myAge = obj['age'];
// console.log(myUserName); // "小花"
let { username: myUserName, age: myAge } = obj;
console.log(myUserName, myAge); // "小花", 18
// 上面这行代码的意思如下
// let {key : 变量, key : 变量} = obj;
// 总结:赋值运算符, 左边的是模式和变量声明,右边是要解构的数组或对象
// 以[]或{}的形式出现在赋值运算符左边的时候,一定是解构赋值(模式)
完全解构
不完全解构赋值
使用场景: 交换2个变量值
注意: 一般数组里的数据每个取出, 还是正常循环
let arr = ["春天", "夏天", "秋天", "明天"];
// 解构赋值 = 左右两边的模式要匹配
// 模式: 左边是[], 右边也要是[]
// 解构赋值的目的: 优化取值和赋值的过程
// 1. 完全解构 - 都接收
// 会按照下角标对应的关系, 把右侧结构里面对应下标的值, 赋予给左边的变量
// 核心: 下标对应
let [one, two, three, four] = arr;
// 2. 不完全解构 - 只要某几个
let [, , , last] = arr;
console.log(last);
// 3. 交换数组里2个元素的值
var x = 20;
var y = 50;
[x, y] = [y, x]; // 注意这里右侧是把变量的值都取到原地, 才开始赋予给左侧(=赋值运算符先看右边)
console.log(x, y);// 50, 20
// 注意1: 模式匹配, 不写var就是重新赋值, 而不是重新声明+赋值
// 注意2: = 是把右侧的变量的值, 都赋予给左边 (所以在右侧先把值取到原地再赋予给左边)
var brr = [49, 27];
[brr[1], brr[0]] = [brr[0], brr[1]];
console.log(brr);
var userObj = {
name: "小王",
age: 8,
sex: "女",
height: 100
}
// 1. 对象的完全解构
// (1): 左右两边模式 {} = {} 模式要匹配
// (2): key对应的, 右侧key的值, 对应赋予给左边同名key的变量
// var {name: theName, age: theAge, sex: theSex, height: theHeight} = userObj;
// 2. 不完全解构
var {age: theAge} = userObj;
console.log(theAge);
// 3. 解构失败
var {address: theAddress} = userObj;
console.log(theAddress); // undefined
// 4. 解构变量的默认值使用
// 变量在值为undefined的时候, 才会被默认值覆盖
var {address: theAddress = '北京'} = userObj;
console.log(theAddress);
// 5. 嵌套结构
var theObj = {
name: "老李",
age: 58,
family: {
firstName: "老",
lastName: "李"
}
}
// 嵌套结构, 不用变量接受了, 而是又用一个嵌套的模式来接收
var {family: {firstName, lastName}} = theObj;
console.log(firstName, lastName);
// 4. 使用场景
// (1): 取值方便
var obj = {
info: {
uname: "小明",
age: 18,
sex: "男",
hobby: "抬杠"
}
};
// 以前:
console.log(obj.info.uname + "今年" + obj.info.age + "岁" + "性别" + obj.info.sex + "爱好是" + obj.info.hobby);
// 现在:
var {uname, age, sex, hobby} = obj.info;
console.log(uname + "今年" + age + "岁, 性别:" + sex + "爱好是" + hobby);
// (2): 配合默认值, 省略很多判断
// 计算对象里3个value的和(threeNum未定义)
let obj8 = {
oneNum: 10,
twoNum: 20
}
// 直接非法运算了
console.log(obj8.oneNum + obj8.twoNum + obj8.threeNum); // NaN
// 用if判断/条件运算符
// let oneNum = obj8.oneNum === undefined ? 0 : obj8.oneNum;
// let twoNum = obj8.twoNum === undefined ? 0 : obj8.twoNum;
// let threeNum = obj8.threeNum === undefined ? 0 : obj8.threeNum;
// console.log(oneNum + twoNum + threeNum); // 30
// 解构赋值+默认值写法
let { oneNum = 0, twoNum = 0, threeNum = 0 } = obj8;
console.log(oneNum + twoNum + threeNum); // 30
作用: 定义匿名函数, 简化回调函数和普通函数的写法
例如: arr.filter(function(val){
return val >= 18;
})
箭头函数: arr.filter(val => val >= 18);
使用:
// 1. 无形参, 无返回值的
var fn1 = () => {
console.log("我是无形参无返回值的箭头函数");
}
fn1();
// 2. 有形参, 无返回值
var fn2 = (x) => {
console.log(x * 5);
}
fn2(100);
// 3. 有形参, 有返回值
var fn3 = (x, y) => {
return x + y;
}
var result = fn3(5, 9);
console.log(result);
// 注意: 就是把function 变成了 =>, =>千万不要加空格, 其他的使用和之前学的函数一样
// 优势1: 如果形参只有一个, 可以省略小括号
var fn = x => {
return x * 2;
}
// 优势2: 如果函数体只有一行代码, 可以省略大括号, 默认返回这个表达式的结果
var fn = x => x * 2;
console.log(fn4(10));
var obj = {
a: function(){
console.log(this); // obj
},
b: () => {
console.log(this); // window
},
c: function(){
setTimeout(function(){
console.log(this); // window
}, 1000);
},
d: function(){ // this:obj
setTimeout(() => {
console.log(this); //obj
}, 1000);
},
e: () => {// this:window
setTimeout(() => {
console.log(this); // this:window
}, 2000);
},
f: function(){ // this的值: obj
return () => {
console.log(this); // obj
}
},
g: function(){//obj
var d = (function(){//window
return {
o: function(){ //d
setTimeout(() => {
console.log(this); //d
}, 1000);
}
}
})();// d = {o: function}
d.o();
}
}
obj.g()
obj.a();
obj.b();
obj.c();
obj.d();
obj.e();
var d = obj.f();
d();
允许我们在function / 箭头函数中, 对形参赋予默认值
形参的默认值----当不传入参数的时候/形参的值是undefined, 会被默认值覆盖掉
// 默认值作用: 防止在不传递参数时, 有问题
// 以前做法:
// function fnOne(a, b){
// a = a || 0;
// b = b || 0;
// console.log(a + b);
// }
// fnOne();
// 其实想在不传数字时, 默认是0+0, 但是现在是undefined相加(结果是NaN了)
// 使用默认值解决问题
function fnOne(a = 0, b = 0) {
console.log(a + b);
}
fnOne();
fnOne(10, 20);
// 箭头函数体也能使用默认值
let fnTwo = (a = 0, b = 0) => {
console.log(a + b);
}
fnTwo();
fnTwo(100, 200);
// 1. 箭头函数不能被用作构造函数使用, 不能用new来调用
// 原因箭头函数里没有自己的this指向, 所以也改不了
// var Person = (theName, theAge) => {
// this.name = theName;
// this.age = theAge;
// }
// var p = new Person();
// console.log(p);
// 2. 箭头函数不能使用arguments
// var fn = () => {
// console.log(arguments);
// }
// fn(10, 20, 56);
…剩余参数运算符 -> 函数形参上, 为了 接收所有的参数
// 使用 ...来替代arguments
// 注意: ...变量名, 变量名随便起, 用于接收参数 (参数接收每个位置要对应)
// 作用: 接收个数不确定的, 参数值, 形成一个数组使用
function youSum(a, ...arg){
console.log(a, arg);
}
youSum(5, 10);
youSum(100, 200, 300);
youSum(100, 200, 300, 8);
var mySum = (...arg) => {
console.log(arg);
}
mySum(10, 20, 30, 40, 50);
…spread展开运算符 -> 数组/对象上, 为了快速拷贝一个数组/对象
// ...变量名: 在函数的形参上, 可以接受剩余参数的值, 形成一个数组
// ...变量名: 用在了函数形参以外的地方, 展开运算
// 1. 数组的展开使用
var arr = [10, 5, 8];
console.log(arr[0], arr[1], arr[2]);
console.log(...arr); // 上下是等价的
console.log(Math.max(...arr)); // 10
// 2. 对象的展开使用
var obj = {
age: 18,
sex: "不知道",
address: "泰国"
}
console.log({...obj}); // 不需要forin来拷贝对象了
// 既然是拷贝内容, 那是深还是浅拷贝呢?
// ...只是第一层内容的拷贝(所以是浅拷贝)
var obj2 = {
age: 18,
grade: [90, 100, 98],
family: {
fName: "李"
}
}
var newObj = {...obj2}; // ...常用于内容的复制(浅拷贝)
console.log(newObj);
newObj.grade[0] = 1000;
console.log(obj2); // 发现里面的grade第一个值为1000, 被影响了, 证明...内容拷贝是浅拷贝
// 如果只有一层, 直接用...复制内容过来就ok
// 但是如果有多层, 调用deepCopy方法即可深拷贝
无序不重复的value集合体
let set = new Set([1, 3, 3, '9', true, 2, 3, 4, 1, 5, true, '9']);
场景: 去除数组中重复的元素
区别: 数组是有序可重复的, Set是无序不重复的
备注: 可使用Array.from() 再转回成数组
无序不重复的多个key-value的集合体
let map = new Map([['bbcc', 12],[25, 'age']]);
区别: Object对象的key只能是字符串, 而Map的key可以是任意类型
let x = 10;
let y = 20;
let obj = {
x,
y, // 相当于 y: y // 前面的y是key的名字, 后面y是变量名, 会使用20
uname: 'uname', // 这个value不是 变量名, 所以不能省略
getPosition(){ // 省略了:function 相当于 getPosition: function(){return obj.x}
return obj.x
}
}
对象调用的方法 | 作用 | 返回值 |
---|---|---|
array.forEach(function(value, index, array){}) | 遍历 | 无 |
array.map(function(value, index, array){}) | 遍历&返回 | 新数组 |
array.filter(function(value, index, array){ return 条件 }) | 过滤&返回 | 新数组 |
array.every(function(value, index, array){ return 条件}) | 判断&返回 | 布尔值 |
array.some(function(value, index, array){return 条件}) | 判断&返回 | 布尔值 |
Array.from(伪数组) | 转换 | 真数组 |
var arr = [1, 5, 7, 3, 10, 2, 4];
// 1. forEach() - 单纯遍历 - 无返回值
arr.forEach(function(value, index){
console.log(index + "--" + value);
})
// 2. map() - 遍历 - 收集每次return结果 - 返回全新数组
var result2Arr = arr.map(function(value, index){
return value + 1;
})
console.log(result2Arr);
// 3. filter() - 过滤 - 收集每次return结果 - 返回全新数组
var result3Arr = arr.filter(function(value, index){
return value >= 5;
})
console.log(result3Arr);
// 4. every() - 找到不符合条件的 - 直接返回false - 不继续循环
var arr5 = [20, 21, 1, 18, 19, 23];
var result5 = arr5.every(function(value, index){
return value > 18;
})
console.log(result5);
// 5. some() - 找到符合条件的 - 直接返回true - 不继续循环
var result6 = arr5.some(function(value, index){
return value < 18;
})
console.log(result6);
// 总结:
// map() - 循环 - 处理每个元素 - 返回新数组
// every() - 找到第一个不符合条件的, 找到就返回false
// some() - 找到第一个符合条件的, 找到就返回true
// 需求: 点击小多选框, 都勾选时, 全选框也勾选
// 思路: 声明个变量, 遍历每个小多选框, 如有一个未选中, 则变量直接保存false, 赋予给全选框, 否则全选框设置为true
// 1. 获取标签
var checkAll = document.getElementById("checkAll");
var ckList = document.querySelectorAll(".ck"); // 所有小多选框
// 2. 全选影响所有小的
checkAll.onclick = function(){
var allChecked = this.checked;
Array.from(ckList).map(function(el){
el.checked = allChecked;
})
}
// 3. 小影响多
var ckArr = Array.from(ckList);
ckArr.map(function(el){
el.onclick = function(){
var isAll = ckArr.every(function(el){return el.checked == true}); // 筛选是否有不符合条件的返回false
checkAll.checked = isAll == false ? false : true;
}
})
学完以后, 多使用ES6的语法来编写代码, 数组循环以后多用方法, 少用for了
都是遍历数组的, 都接收一个函数体, 都有3个形参(value, index, array), 函数里this都指向window
forEach方法没有返回值, 而map方法会返回一个全新数组, 里面是函数体每次执行, 最后return的值
新增声明命令let和const, let 表示变量、const 表示常量
模板字符串
一对反引号(`)标识,也可以当作普通字符串使用,也可以用来定义多行字符串,也可以在字符串中嵌入变量,js表达式或函数,变量、js表达式或函数需要写在${ }中
函数的扩展
函数的默认参数
ES6为参数提供了默认值。在定义函数时便初始化了这个参数,以便在参数没有被传递进去时使用。
箭头函数
在es6中,提供了一种简洁的函数写法,我们称作“箭头函数”
写法:函数名=(形参)=>{……} 当函数体中只有一个表达式时,{}和return可以省略,当函数体中形参只有一个时,()可以省略。
特点:箭头函数中的this始终指向箭头函数定义时外层函数作用域 this的值
对象的扩展
属性的简写。当key和value变量名同名时, 可以只写key(运行时, 这个key既当做键也当做值来调用), 当值是个函数体时, 可以省略:和funciton
解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值(Destructuring)
数组中的值会自动被解析到对应接收该值的变量中,数组的解构赋值要一一对应 如果有对应不上的就是undefined
对象的key对应上以后, 把右侧的 value值, 赋予给=左边的key所对应的变量上即可
set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数
应用:数组去重
class
class类的继承ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念, 更加专业化, 写法更加简便
…
展开运算符可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
let 和 const 是块级作用域, 不会挂载到window上, 没有变量提升, 同一个作用域下不能重复声明
var 是函数级作用域
const必须有初始值, 之后无法重新赋予
用了箭头函数,this指向要看定义箭头函数外层函数作用域 this的值是谁
不能够使用arguments对象
不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定
在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)就能解决
应用场景Set用于数据重组,Map用于数据储存
Set:
成员不能重复
只有值没有键名,类似数组
可以遍历,方法有add, delete,has
Map:
本质上是健值对的集合,类似集合, 键可以是任意类型
可以遍历,可以跟各种数据格式转换
let s = new Set();
s.add([1]);s.add([1]);
console.log(s.size);
/两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,因此并不是相同的值都能存储到Set结构中,所以size为2/
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!