defineProperty和Proxy、Reflect、Symbol

Object.defineProperty()

get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
set:一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。

let obj = {
    _a:'',
    get a(){
        // todo ...
        return this._a
    },
    set a(value){
        this._a = value
    }
}
obj.a = 100;
console.log(obj.a); // 需要借用一个第三方变量来中转
给每个对象都添加一个 getter和setter 当值变化可以 可以实现更新视图的功能
let obj = {}
let val = '';  // 也需要借用一个第三方变量来中转
Object.defineProperty(obj,'a',{
    configurable:true, // 是否可删除
    // writable:true, // 是否可写,
    enumerable:true, // for in 原型上的方法
    get(){
        return val
    },
    set(value){
        val = value;
    }
    // 默认设置的值是不可枚举的
})
delete obj.a
console.log(obj);
let obj = {
  a: 1,
  b: 2,
}; 
vue中的做法
function observer(obj) {
  // 缺陷就是无法监控数组的变化
  if (typeof obj !== "object" || obj == null) {
    return;
  }
  for (let key in obj) {
    // 因为defineProperty 需要一个公共的值去修改
    defineReactive(obj, key, obj[key]);
  }
}
let updateView = () => {
  // 更新方法
  console.log("更新");
};
// obj  => {a:1,b:2}  key=> a / b  value = 1/2
function defineReactive(obj, key, value) {
	// 第三方用来中转的变量
  // Object.defineProperty
  observer(value); // 递归增加getter和setter
  Object.defineProperty(obj, key, {
    get() {
      return value;
    },
    set(val) {
      updateView();
      value = val;
    }
  });
}
observer(obj);
obj.a.a = 100;
console.log(obj.a); 
Proxy(代理)

Proxy 也就是代理,可以帮助我们完成很多事情,例如对数据的处理,对构造函数的处理,对数据的验证,说白了,就是在我们访问对象前添加了一层拦截,可以过滤很多操作,而这些过滤,由你来定义。

  • 语法
let p = new Proxy(target, handler);

target :需要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler: 一个对象,其属性是当执行一个操作时定义代理的行为的函数(可以理解为某种触发器)。具体的handler相关函数请查阅官网
  • 方法
get(target,key){}
target:目标对象
key:被获取的属性名
set(target,key,value){}
target:目标对象
key:将被设置的属性名
value:新属性值
例:
let obj={
	a:1,
	b:2
}
let proxy=new Proxy(obj,{
    get(target,key){
        // return target[key]
        // 使用Reflect优化写法
        return Reflect.get(target,key)
    },
    set(target,key,value){
        // target[key]=value
        // 使用Reflect优化写法
        return Reflect.set(target,key,value)
    }
})
proxy.c=100
console.log(obj)  // {a:1,b:2,c:100}
let obj = {
    a:{a:2}
}
let handler = {    // 只能代理当前这个对象 1层
    get(target,key){  
        // return target[key]
        if(typeof target[key] === 'object'){
            return new Proxy(target[key],handler);   
            // 如果是对象 就返回这个对象的代理
        }
       return Reflect.get(target,key);
    },
    set(target,key,value){ 
        // target[key] = value;
        if(key === 'length') return true;
        console.log('update');
        return  Reflect.set(target,key,value);
    }
}
let proxy = new Proxy(obj,handler)
proxy.a.a = 100
console.log(obj.a.a);
// 支持数组 可以直接更改数组 达到拦截的目的
反射机制

反射机制指的是程序在运行时能够获取自身的信息
例如:对于一个正在使用的对象,能够知道它有哪些方法和属性

JavaScript 中利用for(...in...)语句实现反射
例如:
for(var p in obj){
        if(typeof(obj[p]=='function')){
                obj[p]();
           }else{
          alert(obj[p]);
       }
}
这里var p 表示声明的一个变量,用以存储对象的属性或方法。遍历obj对象的所有属性和方法,遇到属性则弹出它的值,遇到方法则立即执行。在后面可以看到,在面向对象的JavaScript程序设计中,反射机制是很重要的一种技术,它在实现类的继承中发挥了很大的作用。
用反射机制传递样式参数:

function setStyle(_style){
  //得到要改变样式的界面对象
  var el = getElement();
  for(var p in _style){
      el.style[p] = _style[p];
  }
}
 程序中遍历 _style的每个属性,得到属性名称,然后再使用方括号语法将el.style中对应的属性赋值为_style中的相应属性的值。从而,el中仅改变指定的样式,而其他样式不会改变,得到了所要的结果。
Reflect ---- JS的反射对象

Reflect是一个内建的对象,用来提供方法去拦截JavaScript的操作。Reflect不是一个函数对象,所以它是不可构造的,也就是说它不是一个构造器,你不能通过new操作符去新建或者将其作为一个函数去调用Reflect对象。Reflect的所有属性和方法都是静态的。

为什么需要Reflect对象?
  • 1:将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。
  • 2:更加有用的返回值:Reflect有一些方法和ES5中Object方法一样样的, 比如: Reflect.getOwnPropertyDescriptor和Reflect.defineProperty, 不过, Object.defineProperty(obj, name, desc)执行成功会返回obj, 以及其它原因导致的错误, Reflect.defineProperty只会返回false或者true来表示对象的属性是否设置上了, 如下代码可以重构:
try {
  Object.defineProperty(obj, name, desc);
  // property defined successfully
} catch (e) {
  // possible failure (and might accidentally catch the wrong exception)
}
重构成这样:
if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
  // failure
}
  • 3:函数操作:如果要判断一个obj有定义或者继承了属性name, 在ES5中这样判断:name in obj; 或者删除一个属性 :delete obj[name], 虽然这些很好用, 很简短, 很明确, 但是要使用的时候也要封装成一个类;有了Reflect, 它帮你封装好了, Reflect.has(obj, name), Reflect.deleteProperty(obj, name);
  • 4:Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为
  • 5:Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined
  • 6:Reflect.set方法设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver
Reflect.set(target,Key, value[, receiver])
target:设置属性的目标对象。
Key:设置的属性的名称。
value:设置的值。
receiver:如果遇到 setter,receiver则为setter调用时的thisvar myObject = {
  foo: 4,
  set bar(value) {
    return this.foo = value;
  },
};

var myReceiverObject = {
  foo: 0,
};

Reflect.set(myObject, 'bar', 1, myReceiverObject);
myObject.foo // 4
myReceiverObject.foo // 1
  • 7:可变参数形式的构造函数: 想象一下, 你想通过不确定长度的参数实例化一个构造函数, 在ES5中, 我们可以使用扩展符号, 可以这么写:
var obj = new F(...args)
不过在ES5中, 不支持扩展符啊, 所以, 我们只能用F.apply,或者F.call的方式传不同的参数, 可惜F是一个构造函数, 这个就坑爹了, 不过有了Reflect, 我们在ES5中能够这么写:
var obj = Reflect.construct(F, args)
  • 8:控制访问器或者读取器的this: 在ES5中, 想要读取一个元素的属性或者设置属性要这样:
	var name = ... // get property name as a string
	obj[name] // generic property lookup
	obj[name] = value // generic property update
Reflect.get和Reflect.set方法允许我们做同样的事情, 而且他增加了一个额外的参数reciver, 允许我们设置对象的setter和getter的上下thisvar name = ... // get property name as a string
	Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
	Reflect.set(obj, name, value, wrapper)
访问器中不想使用自己的方法,而是想要重定向this到wrapper:
	var obj = {
	    set foo(value) { return this.bar(); },
	    bar: function() {
	        alert(1);
	    }
	};
	var wrapper = {
	    bar : function() {
	        console.log("wrapper");
	    }
	}
	Reflect.set(obj, "foo", "value", wrapper);
  • 9: 避免直接访问 __proto__ES5``提供了Object.getPrototypeOf(obj),去访问对象的原型,ES6提供也提供了Reflect.getPrototypeOf(obj)Reflect.setPrototypeOf(obj, newProto)```, 这个是新的方法去访问和设置对象的原型.
Reflect 的一些方法
1、Reflect.has方法对应name in obj里面的in运算符。
2、Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性。
该方法返回一个布尔值。如果删除成功,或者被删除的属性不存在,返回true;删除失败,被删除的属性依然存在,返回false3、Reflect.construct方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。
function Greeting(name) {
  this.name = name;
}
// new 的写法
const instance = new Greeting('张三');
// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['张三']);
4、Reflect.defineProperty方法基本等同于Object.defineProperty,用来为对象定义属性。未来,后者会被逐渐废除,请从现在开始就使用Reflect.defineProperty代替它。
5、观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。
下面,使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}
上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者
Symbol

ES6中新增的基础数据类型:Symbol它的功能类似于一种标识唯一性的ID

通常情况下,我们可以通过调用Symbol()函数来创建一个Symbol实例:

Symbol([description])
description:(可选)可选的,字符串类型。对symbol的描述
let s1 = Symbol()

方法:

Symbol.for(key)
使用给定的key搜索现有的symbol,如果找到则返回该symbol。否则将使用给定的key在全局symbol注册表中创建一个新的symbol
Symbol.keyFor(sym)
从全局symbol注册表中,为给定的symbol检索一个共享的key
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值