概述
解构赋值是对赋值运算符的扩展。
他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
解构模型
在解构中,有下面两部分参与:
- 解构的源,解构赋值表达式的右边部分。
- 解构的目标,解构赋值表达式的左边部分。
补充:ES6新增:let关键字声明变量;const 常量声明
1.let 特点:
(1)let声明的变量较var而言,不会变量提升;
在for(){}循环中如果使用var 声明变量 i;会导致变量 i 泄露;
(2)let不可声明重复变量名;
(3)存在块级作用域 {} ,如果let声明的变量存在于代码块中,只在代码块中有效,代码块外部无法访问 ;而var声明的变量即使是在代码块中,代码块外部依然可以获取数据;
(4)不影响作用域链;
小案例分析 var 声明变量的变量泄露;
// 变量 i 泄露的问题小案例:
<body>
<h1>点击变背景色</h1>
<div>1</div>
<div>2</div>
<div>3</div>
</body>
<script>
/* 用标签选择器选取div,使用var申明变量,for循环内绑定监听点击事件,点击后div变背景色为黑色 */
//let 产生的临时死区,阻止变量i 泄露
var divs = document.querySelectorAll('div');
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener("click", function () {
this.style.backgroundColor = "#000"
})
}
//阻止变量 i 泄露的方法二
for (var i = 0; i < divs.length; i++) {
(function (a) {
divs[i].addEventListener("click", function () {
this.style.backgroundColor = "#000"
})
})(i)
}
</script>
2.const 常量声明特点:
(1)声明的同时必须赋值,不可以先声明后赋值;
(2)通常常量名要大写(大家都这么写,小写也不会报错);
(3)常量值赋值完之后,不可修改;
(4)存在块级作用域;,如果const声明的常量存在于代码块中,只在代码块中有效,代码块外部无法访问 ;
(5)引用类型(对象和数组)使用const 声明赋值后,修改引用类型是没有打破(3)所说的规定不可修改;
因为:const 声明的引用类型,事实上是声明了栈内存里的地址为常量,不可修改栈内存中的地址;栈内存地址指向堆内存中的值;修改的是堆内存中的内容;
//引用类型使用const 声明后,其实是内存地址不允许修改,引用类型指向的堆内存内容可以修改
const obj = {};
const arr = [];
obj.name = '张三'; //原理:此时修改的是堆里的内存,栈对应的地址并没有修改
obj.age = 18;
arr.push(1);
arr.push(2);
3.js执行机制(事件循环)
(1)先执行执行栈中的同步任务;
(2)异步任务(回调函数)放入任务队列中;
(3)当执行栈中的所有同步任务执行完毕,系统会按次序读取任务队列中的异步任务,于是异步任务结束等待状态,进入执行栈,开始执行;
简单理解:js从上到下依次执行,遇到同步的就直接执行,遇到异步的,就将异步的代码放到最后再执行。
console.log(1);
setTimeout(function () {
console.log(2); //放到最后执行
}, 1)
console.log(3);
//输出 1,3,2
二、ES6解构赋值
数组模型的结构赋值(Array)
1.基本解构赋值;
//ES5赋值
var a, b, c = 1;
console.log(a, b, c);//a,b返回undefined,c返回1
//ES6解构赋值
let [a, b, c] = [1, 2, 3];
console.log(a, b, c);
2.使用结构赋值交换变量;
//1.ES6解构赋值交换两个变量的值
let [x, y] = [1, 2];
console.log(x, y); //--> 1,2
//直接进行交换值,不需要中间空变量来实现交换;
[y, x] = [x, y];
console.log(x, y); // --> 2,1
3.数组结构赋值的可以读取数组元素;
//2.解构赋值数组
const F4 = ['小沈阳', '六哥', '赵本山', '大壮'];
let [xiao, liu, zhao, da] = F4;
console.log(xiao, liu, zhao, da); //就可以输出数组元素
4.嵌套
let [x1, [y1, z1]] = [1, [2, 3]]
console.log(x1, y1, z1);//---> 1 ,2,3
5.不完全匹配
//4.不完全匹配
let [aa, bb] = [1, 2, 3]
console.log(aa, bb); //--> 1,2
6.可忽略解构
let [a, , b] = [1, 2, 3];
console.log(a, b);//-->1 ,3
7.不完全解构
let [a = 1, b] = [];
console.log(a, b);//-->1 , undefined;
let [a, b] = [1];
console.log(a, b);//--> 1 , undefined;
8.剩余运算符 ...
//剩余运算符 ...
let [a, ...b] = [1, 2, 3];
console.log(a, b);//---> 1 , [2,3]
字符串的解构赋值:
1.在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据。
let [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e); //--> h e l l o;
//解构字符串的属性,把length 的属性交给 len
let {
length: len
} = 'why';
console.log(len); // --> 返回3 ;表示字符串长度3
//直接用length来接收;
let {
length
} = 'why';
console.log(length);//---> 返回 3 ;表示字符串长度3
函数的解构:
1.函数返回值,进行解构赋值
function a() {
return [1, 2, 3]
}
let [m, n, l] = a();
console.log(m, n, l);//--> 1 , 2 ,3
2.函数传参,进行解构赋值
function add([a, b, c]) {
return a + b + c;
}
const arr = [1, 2, 3];
console.log(add(arr)); // ---> 6
数组转对象的小例子:
//change函数,数组转对象
onst params = ['name=张三', 'age=18'];
function change([name, age]) {
return {
name: name.split('=')[1],
age: age.split('=')[1]
}
}
console.log(change(params));
对象模型的解构;
注意:
对象的解构赋值,只认属性名,不认位置;只要对应的属性名相同,就可获得对象的名字
因为对象的里的元素的无序的;只要 key = value 键值对;
1.基本解构
let {
b,
a
} = {
a: 1,
b: 2
};
console.log(a, b);// ---> 1, 2
//a,b的解构顺序不会影响值
2.对象解构赋值小例子:
解构完可以直接使用,不需要再像ES5一样使用对象实例来获取属性和方法;
//对象的解构赋值
const obj = {
name: 'jack',
age: 18,
say: function () {
console.log('hello world');
}
};
let {
name,
age,
say
} = obj;
//相比ES5而言,可以直接调用名字和方法;
console.log(name, age);
say();
3.Math对象解构;
//解构内置对象 Math对象
let {
min,
max,
random,
pow
} = Math;
console.log(min(2, 3, -5, 99));
console.log(max(2, 3, -5, 99));
console.log(pow(2, 3));
console.log(random());
4.对象解构的应用场景;
如:与java同学进行数据交互的时候,传来的对象名不符合规则
比如:驼峰命名的userName ,但是返回的是 username;
//比如:后端的数据传回来没有使用驼峰命名法,我们需要纠正命名
let {
username: userName
} = {
username: 'jack'
};
console.log(userName);
5.函数形参添加默认值;即:当调用函数时没有传入实参,就让形参使用默认值输出;
//ES5 的方法,在没有传参时,使用默认值;
function show(a, b) {
var a = a || 10;
var b = b || 20;
return [a, b]
}
console.log(show()); //-->[10,20]
//ES6的方法,在没有传参时,使用默认值的
//在 a 没有被传参,或者传入 undefined 的时候,a == 10(默认值) ;有传参时,a == 传参值
function show(a = 10, b = 20) {
return [a, b]
}
console.log(show()); //--> [10,20]
console.log(show(5)); //--> [5,20]
console.log(show(5, 6)); //--> [5,6]
箭头函数(Arrow Functions):
箭头函数在语法上比普通函数简洁多。箭头函数就是采用箭头=>来定义函数,省去关键字function。
函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中。
var getPrice = function() {
return 4.55;
};
// Implementation with Arrow Function
var getPrice = () => 4.55;
//当形参只有一个的时候,形参的小括号可以省略;
const show2 = name => console.log('你是:' + name);
show2('jack')
当然,箭头函数不仅仅是让代码变得简洁,
函数中 this 绑定总是指向对象自身。具体可以看看下面几个例子:
注意 : 定时器 setInterval(){} 实际上是 window.setInterval(){} ,所以在定时器里的this指向是 window
function Person() {
this.age = 0;
//setInterval(){} 实际上是 window.setInterval(){} ,所以在定时器里的this指向是 window
setInterval(function growUp() {
// 在非严格模式下,growUp() 函数的 this 指向 window 对象
this.age++;
}, 1000);
}
var person = new Person();
我们经常需要使用一个变量来保存 this,然后在 growUp 函数中引用:
function Person() {
var self = this;
self.age = 0;
setInterval(function growUp() {
self.age++;
}, 1000);
}
而使用箭头函数可以省却这个麻烦:
注意:
箭头函数没有自己的this,它的this是静态的,他的this指向自己父级的this
也就是函数的this,而函数的this是谁调用函数,函数的this就指向谁。
function Person(){
this.age = 0;
setInterval(() => {
// |this| 指向 person 对象
this.age++;
}, 1000);
}
var person = new Person();
3. 函数参数默认值
ES6 中允许你对函数参数设置默认值:
let getFinalPrice = (price, tax=0.7) => price + price * tax;
getFinalPrice(500); // 850