开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
let,const,var区别
① 是否变量提升 ② 是否有块级作用域 ③ 能否重复声明 ④ 能否修改声明的变量
/**
* var:
* 顶级变量(window对象的属性);
* 可以变量提升;
* 一个变量可以多次声明,后面会覆盖前面的值;
* 函数内的var同一个变量名时候,这个变量就是局部变量,不用var的话它就是全局变量
*/
var a = 10;
console.log(window.a)
console.log(b) // undefined
var b = 20
var c = 30
var c = 40
console.log(c) // 40
var d = 60
function fun(){
var d = 70 // 这是局部变量
}
fun()
console.log(d) // 60
/**
* let:
* 有代码块概念(块级作用域{});
* 不会变量提升;
* 不可以同一个作用域下重复声明;
*/
{
let num = 100
console.log(num) // 100
}
console.log(num) // 报错
console.log(num2) // 报错
let num2 = 200
/**
* const:
* 声明一个只读常量,常量的值不能改变;
* const定义必须赋值;
* 其他跟let一样
*/
const sum = 500
sum = 600
console.log(sum)
const定义的对象属性能修改吗?
可以。
const obj = {
name:'lily',
age:22,
sex:0
}
obj.name = 'cici'
console.log(obj.name) // cici
/**
* const定义基本数据类型(常量)的值不能改动;
* const定义的引用类型数据(对象,数组)的指向内存地址不能修改,但对对象的属性值是能修改的
*/
ES6中的箭头函数
你是怎么理解:
1、this是静态的,写代码时候就确定this指向,而且不可以改变;
2、this始终指向函数声明时所在的作用域下的this;
3、箭头函数不能做构造函数(因为new过程this是动态的),不能用new命令;
4、没有argument对象,即不能用伪数组去接受参数,但可以用rest参数代替
var name = 'lily'
var obj = {
name: 'cici'
}
function fun() {
console.log(this.name)
}
var fun1 = () => {
console.log(this.name)
}
fun()
fun1()
// 同一个作用域下声明和执行,指向一样,都是输出lily
console.log('------------')
fun.call(obj) // 改变了this指向,输出cici
fun1.call(obj) // this指向不能改变,依然是lily
function fn() {
let fun2 = () => {
console.log(this.name)
} // 里面的箭头函数指向跟fn指向一样,想要改变箭头函数this就要改变fn的this
fun2()
}
fn() // fn指向window,即箭头函数this指向也是window,输出lily
console.log('============')
fn.call(obj) // 改变fn的this,也改变箭头函数this指向obj,输出cici
// arguments 使用
function fn3() {
console.log(arguments) // arguments 是作为普通函数的内置对象,不需要定义
}
// let fn4 = ()=>{
// console.log(arguments) // arguments is not defined
// }
let fn4 = (...rest) => {
console.log(rest)
}
fn3(1, 2, 3) // [Arguments] { '0': 1, '1': 2, '2': 3 }
fn4(4, 5, 6, ) // [ 4, 5, 6 ]
箭头函数适用场景:
与this无关的回调,例如:定时器,数组方法回调不适用场景:
与this有关的回调,例如:事件回调,对象方法回调
<style>
.box {
height: 100px;
width: 100px;
border: 3px solid pink;
}
.newbox {
height: 100px;
width: 100px;
border: 3px solid pink;
background-color: aquamarine;
}
</style>
</head>
<body>
<div class="box">点击盒子魔法</div>
</body>
<script>
let box = document.querySelector('.box')
// 普通函数
// box.addEventListener('click', function () {
// var that = this;
// // 定时器的this指向window,先把外面的this保存给that,在定时器里面that才能指向box
// setTimeout(function () {
// that.className = 'newbox'
// }, 1000);
// })
// 箭头函数
box.addEventListener('click', function () {
// 定时器用箭头函数的话,它的this跟外层的function的作用域一样,谁调用指向谁(指向box)
setTimeout(() => {
this.className = 'newbox'
}, 1000);
})
</script>
可以new一个箭头函数吗?\
不行,会报错!
考察内容:new关键字的执行过程;箭头函数知识点
new关键字的执行过程:
① new 构造函数会在内存中创建一个空对象;
② this指向①中的空对象;
③ 执行构造函数中代码,给空对象添加属性和方法
④ 返回③中的新对象(所以构造函数不需要return,new做了返回这一步)
箭头函数特点:this是静态且不能改变!
扩展运算符作用
... 扩展运算符
第一个作用:复制
// ...
let arr = [1, 2, 3, 4, 5, 6]
console.log(...arr) // 1 2 3 4 5 6 以空格分割数组
// 复制:对象里面的元素是:基本数据类型--深拷贝;引用类型--浅拷贝
// 1、数组的复制
var arr1 = [1, 2, 3, [20]]
var arr2 = [...arr1]
console.log(arr2) //[ 1, 2, 3, [ 20 ] ]
arr2[0] = 10
arr2[3][0] = 200
console.log('-------------')
console.log(arr1) // [ 1, 2, 3, [ 200 ] ]
console.log(arr2) // [ 10, 2, 3, [ 200 ] ]
// 2、对象的复制
var obj1 = {
name: 'lily',
age: 22,
friend: {
name: 'cici'
}
}
var obj2 = {
...obj1
}
obj2.age = 18
obj2.friend.name = 'choi'
console.log(obj1) // { name: 'lily', age: 22, friend: { name: 'choi' } }
console.log(obj2) // { name: 'lily', age: 18, friend: { name: 'choi' } }
拷贝出来的对象改变属性,原对象也会被修改,为什么?
对于引用类型数据来说,拷贝出来的是一个真正的副本,在内存堆栈中它们的地址指向还是同一个,动其一全部动。
// 怎么实现深拷贝?
var obj3 = {
...obj1,
// 深拷贝:给它另开一个新地址
friend: {
...obj1.friend
}
}
obj3.friend.name = 'jessi'
console.log(obj1.friend.name) // cici
console.log(obj3.friend.name) //jessi
扩展运算符另一个作用:合并
// 合并
// 数组合并
var arr5 = [7,8,9]
var arr6 = [77,88,99]
var arr7 = [...arr5,...arr6]
console.log(arr7) // [ 7, 8, 9, 77, 88, 99 ]
// 对象合并
var obj5 = {name:'grace'}
var obj6 = {age:6}
var obj7 = {name:'lili',sex:'女'} // 属性一样后面会覆盖前面的值
var obj8 = {...obj5,...obj6,...obj7}
console.log(obj8) // { name: 'lili', age: 6, sex: '女' }
// 字符串变成数组
var myStr = [...'hello']
console.log(myStr) // [ 'h', 'e', 'l', 'l', 'o' ]
rest参数
处理参数数量不确定的情况
// rest参数:获取多余的参数,整合为一个数字组形式
// rest参数一定要放在形参的最后
function sum(...rest) {
console.log(rest) // [ 1, 2, 3, 4, 5, 6 ]
return rest.reduce((pre, cur) => pre + cur)
}
console.log(sum(1, 2, 3, 4, 5, 6)) // 21
console.log(sum(1, 69,78)) // 148
Object.assign的用法
// Object.assign : 用于对象合并,将源对象的可枚举属性复制到目标对象
/**
* 特点:
* 会影响目标对象;
* 同名属性,后面的值会覆盖前面的值;
* 如果只有一个参数,返回本身;
* 如果参数不是对象,先转为对象再返回;
* 复制合并是浅拷贝
*/
let obj = {
a: 1,
c: 20
}
let obj2 = {
b: 2,
c: 30
}
Object.assign(obj, obj2)
console.log(obj) // { a: 1, c: 30, b: 2 }
console.log(Object.assign(100)) // Number {100}
// 常常用来给对象添加属性以及方法
function Test(name, age, hobby) {
Object.assign(this, { name, age, hobby });
}
const test = new Test('lily', 22, '跑步');
Object.assign(Test.prototype, {
run() {
console.log('跑起来');
},
});
console.log(test.name) // lily
test.run(); // 跑起来
对象和数组的解构
解构:一种提取数据的模式
数组解构:以元素的下标为匹配条件
对象解构:以属性名称为匹配条件
// 数组结构
let [a, b, c] = [1, 2, 3]
// 相当于let a = arr[0]
console.log(a, b, c) // 1 2 3
// 结构第一第三个,第二个留空
let [d, , f] = [4, 5, 6]
console.log(d, f) // 4 6
// 对象结构
let obj = {
name: 'lily',
age: 20,
friend: {
name1: 'cici',
age: 21
}
}
let {
name:nn, // 结构重命名
age,
friend: {
name1, // 嵌套结构
sex='女' // 属性值如果没有就取默认值,有的话被覆盖
}
} = obj
console.log(`我叫${nn},今年${age}岁,有个朋友叫${name1},是${sex}的`) // 我叫lily,今年20岁,有个朋友叫cici,是女的
对象的结构赋值
object-assign的用法
for...of 和 for...in 区别
var obj = {
0: 1,
1: 2,
2: 3,
length: 3
}
obj = Array.from(obj)
console.log(obj) // 变成真数组:[ 1, 2, 3 ]
for (let value of obj) {
console.log(value) // 1,2,3
}
/**
* ① 遍历对象是类数组对象,使用Arrary.from()转为数组
* ② 遍历对象不是数组,给对象添加symbol.iterator属性,指向迭代器
*/
/**
* iterator遍历过程:
* 1、创建一个指针对象,指向当前数据结构的起始位置;
* 2、第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员;
* 3、以此类推,不断调用指针对象的next方法,直到它指向数据结构的结束位置;
* (每次调用next方法,都会返回数据结构的当前成员的信息,一个包含value和done两个属性的对象
* value:当前对象的值
* done:布尔值,表示遍历是否结束)
*/
var person = {
name: 'lily',
age: 22,
height: 165,
wight: 100,
sex: '女'
}
// for(let p of person){ // TypeError: person is not iterable
// console.log(p)
// }
for(let p in person){
console.log(p) // 返回对象的属性名
}
// 给person添加一个Symbol.iterator属性,可以用for...of遍历
person[Symbol.iterator] = function () {
// 拿到对象的所有key值
var keys = Object.keys(this) // 谁调用this指向谁,这里就是person
// 定义数组的下标值
var index = 0
return {
next() {
if (index < keys.length) {
return {
value: person[keys[index++]], // 链表,每次循环index+1
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
for(let p of person){
console.log(p) // 拿到person的各个属性值
}
for...of 和 for...in 区别
-
for...of: 允许遍历一个有iterator接口的数据结构(数组,对象)
获取对象键值
只遍历当前对象
对于数组遍历,返回数组下标对应的属性值 -
for...in:
获取对象键名;
遍历对象整个原型链
对于数组遍历,返回所有可枚举的属性
// 对于对象遍历
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.height = 120
var pp = new Person('lily', 22)
pp[Symbol.iterator] = function () {
var keys = Object.keys(pp)
var index = 0
return {
next() {
if (index < keys.length) {
return {
value: pp[keys[index++]],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
for (let value of pp) {
console.log(value)
}
// lily
// 22
for (let key in pp) {
console.log(key)
}
// name
// age
// height pp原型链上的属性
// 对于数组遍历
var arr = [1,2,3,4,5,6]
for(let i in arr){
console.log(i) // 下标
}
console.log('---------')
for(let i of arr){
console.log(i) // 值
}
for...in 主要为了遍历对象而产生,不适用遍历数组;
for...of 可以遍历类数组,类数组对象,字符串,对象,数组...