ES6基础1-2(手写深拷贝、Vue数据劫持、Object.defineProperty...)

1.var都要改为let和const
  1. var声明的变量 (污染全局变量)
  2. 使用var导致变量提升的问题
  3. var可以被重复声明;let可以解决重复定义的问题
  4. 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.展开运算符...
  1. 数组和对象都可以使用
  2. ...只能拷贝一层,是浅拷贝
  3. 浅拷贝(拷贝之后还有关)
  4. 深拷贝(拷贝之后无关)
  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 {  }
11.weakMap和Map的区别(与回收机制有关
  1. weakMap是弱链接,可以清空,对象删除了也不会有引用关系。
  2. 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();

你可能感兴趣的:(ES6基础1-2(手写深拷贝、Vue数据劫持、Object.defineProperty...))