Reflect & Symbol

Reflect

反射 Object.definedProperty
里面有部分Object的方法 放到了Reflect 功能基本一致
Proxy中能代理的方法 Reflect都可以实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const obj = {};
// 赋值
Reflect.set(obj, 'name', '加菲'); // 等同于 obj.name = '加菲';
console.log(obj); // { name: '加菲' }
// 取值
console.log(Reflect.get(obj, 'name')); // 加菲
//是否在对象中
console.log('a' in {a: 1});
console.log(Reflect.has( {a: 1}, 'a'));


Object.defineProperty;
Reflect.defineProperty;
//如下:
const obj = {a: 1};
Object.freeze(obj); // 冻结
Object.defineProperty(obj, 'a', {
value: 100
});
console.log(obj);
// Cannot redefine property: a 报错


const obj = {a: 1};
Object.freeze(obj); // 冻结
// 可以不让别人修改get set方法
// 应用场景:vue里面有个叫数据劫持 把对象上的属性get set都给重写了,重写会增加性能,如果有些对象不需要更改,可以直接用freeze冻结,
let reflect = Reflect.defineProperty(obj, 'a', {
value: 100
});
console.log(obj, reflect); // { a: 1 } false
// 用Reflect就不会报错,且reflect可以获取是否设置成功


const obj = {a: 1};
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
console.log(Reflect.getOwnPropertyDescriptor(obj, 'a'));
// { value: 1, writable: true, enumerable: true, configurable: true }


const obj = {
a: 1,
[Symbol()]: 2
};

console.log(Object.getOwnPropertyNames(obj)); // [ 'a' ] // 获取自身的属性
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol() ] // 获取Symbol
// Object这2个key都是obj上的私有属性,但是这里只能分开获取
console.log(Reflect.ownKeys(obj)); // [ 'a', Symbol() ]


Object.setPrototypeOf; // obj.__proto__ 设置原型链
Object.getPrototypeOf; // obj.__proto__ 获取原型链
Reflect.setPrototypeOf;
Reflect.getPrototypeOf;

call、apply可以改变this指向

1
2
3
4
5
const fn = function(a, b){
console.log(this, a, b);
}
fn.apply(1,[2,3]); // [Number: 1] 2 3
fn.call(1,2,3); // [Number: 1] 2 3

假设fn本身有一个apply方法

1
2
3
4
5
6
7
8
9
10
11
12
13
const fn = function (a, b){
console.log(this, a, b);
}
// fn本身有一个apply方法
fn.apply = function (a, b){
console.log('fn-apply', a, b);
}
fn.apply(1,[2,3]); // fn-apply 1 [ 2, 3 ] 那这个调用的就是本身的方法 没有调原型上的apply方法
//
// Function.prototype.apply.call(fn); // 这里意思是我调用原型上的apply方法,并用call改变this相当于
// fn.apply();
Function.prototype.apply.call(fn,1,[2,3]); // [Number: 1] 2 3 // 这样调的就是原型上的apply
Reflect.apply(fn, 1,[2,3]); // [Number: 1] 2 3 这行和上面这行是一样的

如何调取原型上的apply方法

1
2
3
4
5
6
7
8
9
10
11
const fn = function (a, b){
console.log(this, a, b);
}
// fn本身有一个apply方法
fn.apply = function (a, b){
console.log('fn-apply', a, b);
}
// Function.prototype.apply.call(fn); // 这里意思是我调用原型上的apply方法,并用call改变this相当于
// fn.apply();
Function.prototype.apply.call(fn,1,[2,3]); // [Number: 1] 2 3 // 这样调的就是原型上的apply
Reflect.apply(fn, 1,[2,3]); // [Number: 1] 2 3 这行和上面这行是一样的

其他的一些用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class XXX{
constructor(name){
this.name = name;
}
}
let xxx = Reflect.construct(XXX, ['haha']);
//let xxx = new XXX('hah'); 和这一行是一样的
console.log(xxx);
// XXX { name: 'haha' }

Reflect.deleteProperty //delete obj.a 返回是否删除成功


// 对象属性不能扩展(不能添加属性) 、是否可扩展
const obj = {};
Reflect.preventExtensions(obj); // Object.preventExtensions 和它的用法一样
obj.a = 1;
console.log(obj); // {}
console.log(Reflect.isExtensible(obj)); // false 是否可扩展 和Object.isExtensible是一样的

Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 一般可以用作常量
const s1 = Symbol('加菲'); // 里面放string 、number
console.log(s1); // Symbol(加菲)
//
const s1 = Symbol('加菲');
const s2 = Symbol('加菲');
console.log(s1 === s2); // false
//
let s1 = Symbol.for('加菲');
let s2 = Symbol.for('加菲'); // 如果Symboly已经有值了,就将这个值返回
console.log(s1 === s2); // true
// 和上面line4-5一样,唯一的不同是 已经有值了,就将这个值返回

const obj = {
[s1]: 1, // es6写法,[]的含义是将s1结果取出来作为key
}
//对应的取值
console.log(obj[s1]); // 1 不能直接obj.s1这样

其他功能:

元编程 可以改变js源代码的功能

1)instanceof     可以判断某个人是否是谁的类的实例

1
2
3
4
5
6
let o = {
name: 1
};
let obj = {};
console.log(o instanceof obj); // Right-hand side of 'instanceof' is not callable
// 我们现在希望的是让o是obj的实例

现在就重写下这个行为

1
2
3
4
5
6
7
8
9
let o = {
name: 1
};
let obj = {
[Symbol.hasInstance](){
return 'name' in o
}
};
console.log(o instanceof obj); // true 这里默认会调obj的[Symbol.hasInstance]方法

和之前讲过的Symbol.iterator 在迭代的时候默认会调用一样
2)toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let obj = {a: 1}; // valueOf toString  面试可能会遇上
console.log(obj+1, typeof (obj+1));
// [object Object]1 string

// toString
let obj = {a: 1};
console.log(obj.toString()); // [object Object]


let obj = {
get [Symbol.toStringTag](){
return '123';
}
}
console.log(obj.toString()); // [object 123]
// 上面这个obj.toString() 其实调用的是Object.Prototype.toString
衍生对象
1
2
3
4
5
6
7
8
9
10
11
12
// 衍生对象
class MyArray extends Array{
constructor(...args){
super(...args);
}
}
let myarr = new MyArray(1,2,3);
let newarr = myarr.map( item=>item*2); // 衍生出来的结果是当前类的实例
// instanceof 原理 .__proto__.__proto__一级一级在原型链上找
console.log(myarr instanceof MyArray); // true
console.log(newarr instanceof MyArray); // true
console.log(newarr instanceof Array); // true

newarr明明不是new出来的 怎么会是MyArray 和Array的实例呢 改变如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyArray extends Array{
constructor(...args){
super(...args);
}
static get [Symbol.species](){
return Array; // 控制衍生对象的类的构造函数 这样衍生出来的时候就是Array的实例 不是MyArray的实例了
}
}
let myarr = new MyArray(1,2,3);
let newarr = myarr.map( item=>item*2); // 衍生出来的结果是当前类的实例

console.log(myarr instanceof MyArray); // true
console.log(newarr instanceof MyArray); // false
console.log(newarr instanceof Array); // true