1.var都要改为let和const
- var声明的变量 (污染全局变量)
- 使用var导致变量提升的问题
- var可以被重复声明;let可以解决重复定义的问题
- var作用域的问题(全局作用域);let(函数作用域)
2.常见的作用域
全局作用域
函数作用域
块作用域{} + let
3.let
问题:暂存死区
let a = 100;
{
console.log(a); //这里会报错:a is not defined
//当前作用域有a,就不会去外面找;但是let不会变量提升,所以a在打印的时候还未被声明,不会是undefined
let a = 200;
}
4.const 常量 不会变的量(地址不变即可)
const的值习惯性大写
const PI = { r: '3.14' };
PI.r = 3.15; // 此时不会报错,地址没有变
5.展开运算符...
- 数组和对象都可以使用
- ...只能拷贝一层,是浅拷贝
- 浅拷贝(拷贝之后还有关)
- 深拷贝(拷贝之后无关)
- Object.assign也是浅拷贝
6.不靠谱的深拷贝尝试:
我们可以把对象先转化成字符串 在把字符串转换成对象。会有一个问题,完全无法处理fn
Object.assign = ...
let school = {name:'zfpx',fn:function(){},aa:undefined,b:null,arr:[1,2,3,[4,5]]};
let my = {age:{count:18},name:'jw'};
let all = JSON.parse(JSON.stringify({...school,...my}));
my.age.count = 100;
console.log(all);
7.自己实现一个深拷贝方法(递归拷贝)
先掌握类型判断:
- typeof
- instanceof
- Object.prototype.toString.call
- constructor
小技巧:
- [].constructor 返回一个 fn,即:Array();new Array()返回[]
- {}.constructor 返回一个fn,即:Object();new Object()返回{}
- new obj.constructor会返回一个[]或者{}
注意:
- function直接返回;typeof function() {} == 'function'
- Symbol不是对象,直接返回;typeof Symbol == 'function'
- null == undefined true
- 用weakMap来解决“循环引用”
function deepClone(obj, hash = new WeakMap()) {
if (obj == null) return obj; // 判断obj是null还是undefined
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== 'object') return obj; // 不是对象就不用拷贝了
if (hash.has(obj)) return hash.get(obj); // 如果weakmap中有对象就直接返回之前拷贝过得obj
let cloneObj = new obj.constructor(); // 适配数组和对象
// 如果是对象把他放到weakMap中,如果再拷贝这个对象这个对象就存在了,直接返回这个对象即可
hash.set(obj, cloneObj); // 防止循环调用问题,(程序会崩溃)
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // for in会遍历出原型上的属性
// 只拷贝私有属性,不考虑原型链上的属性
// 如果赋予的值是对象 我就把这个对象放到weakmap中
cloneObj[key] = deepClone(obj[key], hash); // 实现深拷贝
}
}
return cloneObj;
}
8.set
- set是集合;不能重复的东西,否则就白放了;没顺序
- Set有symbol.iterator能被迭代,能被...展开
- add,delete 添加和删除
- 基础类型 string number boolean undefined object symbol ???
let s = new Set([1, 2, 3, 4, 1, 2, 3, 4]);
console.log(s); // Set { 1, 2, 3, 4 }
console.log(typeof s); // object
s.add('5');
s.delete('5');
let arr = [...s]; // 有迭代,才能被展开
console.log(arr); // [ 1, 2, 3, 4 ]
集合:并集、交集、差集
let s01 = [1, 2, 3, 1, 2, 6];
let s02 = [3, 4, 5, 1, 2];
// 并集:
function union() {
let s1 = new Set(s01);
let s2 = new Set(s02);
console.log([...new Set([...s1, ...s2])]);
}
union(); // [ 1, 2, 3, 6, 4, 5 ]
// 交集:
function intersection() {
return [...new Set(s01)].filter(function (item) {
// filter返回true表示留下
return new Set(s02).has(item);
});
}
console.log(intersection()); // [ 1, 2, 3 ]
// 差集: 要看谁差谁,顺序不同结果也不同
function diff() {
return [...new Set(s01)].filter(function (item) {
return !new Set(s02).has(item); // 跟intersection相比就加了一个!
});
}
console.log(diff()); // [6]
9.Map
- map 映射表
- 跟set唯一的不同是:map有key。
- map也不能放重复的
- 在Map里销毁obj,有引用关系。
let m = new Map();
m.set('name', '123');
m.set('name', '111');
let obj = { age: 1 };
m.set(obj, '123');
m.set(obj, '456'); // 这个obj的引用的空间被set所引用
obj = null; // 把obj清空,但是这个空间还是在的
console.log(m.obj); // undefined
console.log(m); // Map { 'name' => '123', { age: 1 } => '456' }
10.WeakMap
- WeakMap的key,必须是对象类型,否则会报错:Invalid value used as weak map key;
- 作为对象key的对象,其应用关系会被销毁掉。
- 在WeakMap里可以销毁obj,因为没有引用关系,也不会再引用原来的对象。
let m = new WeakMap();
let obj = { age: 1 };
m.set(obj, '123');
obj = null; // 把obj清空,但是这个空间还是在的
console.log(m.obj); // undefined
console.log(m); // WeakMap { <items unknown> }
11.weakMap和Map的区别(与回收机制有关)
- weakMap是弱链接,可以清空,对象删除了也不会有引用关系。
- Map即便删除了,还是会有引用关系,会继续占用空间,不会被销毁。
12.Object.defineProperty
ES5语法,使用场景很多:vue,react...
- 通过Object.defineProperty定义属性,可以增加拦截器;
- 定义的属性是隐藏属性,不可枚举,不能用for...in遍历到。
- 通过Object.defineProperty定义属性 可以增加拦截器
- 只能用在对象上,数组是不行的
let obj = {};
let other = '';
// 不可枚举 函数的原型 Array.protoype
Object.defineProperty(obj, 'name', {
enumerable: true, // 是否可以枚举
configurable: true, // 是否可以配置: 能不能删除这个属性
//writable:true, // 是否可以重写
get() { // 读取方法
console.log('----');
return other;
},
set(val) { // 设置方法
other = val;
}
});
// delete obj.name; // 如果configurable为false,那么会删不掉对象的属性
obj.name = 456;
console.log(obj.name); // 456
13.对象的setter和getter(Vue中的数据双向绑定)
let obj = {
other: '123',
get name() {
return this.other;
},
set name(val) {
this.other = val;
}
};
obj.name = 456;
console.log(obj.name);
14.vue的数据劫持 (把所有的属性都改成 get和set方法)
function update() { // 模拟的更新方法
console.log('更新视图');
}
let data = {
name: 'zfpx',
age: 18,
address: {
location: '回龙观'
}
};
function observer(obj) {
// Object.defineProperty只能用在 对象上 (数组也不识别)
if (typeof obj !== 'object') return obj;
for (let key in obj) {
defineReactive(obj, key, obj[key]);
}
}
// 定义响应式
function defineReactive(obj, key, value) {
observer(value); // 递归,嵌套的数据也会被观测到
Object.defineProperty(obj, key, {
get() {
return value;
},
set(val) {
if (val !== value) {
observer(val);
update();
value = val;
}
}
});
}
observer(data);
data.address = [1, 2, 3];
let methods = ['push', 'slice', 'pop', 'sort', 'reverse', 'unshift'];
methods.forEach((method) => {
// 面相切片开发 装饰器
let oldMethod = Array.prototype[method];
Array.prototype[method] = function () {
update();
oldMethod.call(this, ...arguments);
};
});
data.address.push(4);
data.address.push(4);
15.箭头函数
- 没有this,没有arguments
- 53min开始举的this例子没看懂???
- obj是一个对象,不是作用域,也就没有this
- let声明的变量,不属于window属性;属于自己window下的作用域???
let a = 1;
let obj = { //obj是一个对象,不是作用域
a: 2,
fn: () => {
setTimeout(() => {
console.log(this.a); // undefined
// 因为定时器是window在调用,let的a不会挂载到window上
});
}
};
obj.fn();