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调用时的this值
var 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的上下this:
var 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;删除失败,被删除的属性依然存在,返回false。
3、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