每日一题: 细说es6中的Reflect

每日,一题,细说,es6,reflect · 浏览次数 : 10

小编点评

## Content Generation Guidelines **Content Type:** This content is a collection of articles and other informative content. **Guidelines:** * **Keep the content concise and easy to read.** * **Use clear and simple language.** * **Include relevant images and other visuals.** * **Structure the content logically.** * **Follow the content type guidelines.** * **Provide appropriate metadata for each article.** **Specific Content:** * **Articles:** Include articles with relevant titles and content. * **Other informative content:** Include other informative content like tutorials, guides, reports, etc. * **News:** Include news articles with relevant titles and content. * **Blog posts:** Include blog posts with relevant titles and content. **Additional Notes:** * Use proper formatting and indentation for readability. * Include relevant metadata for each article. * Provide proper indexing for the content. * Consider providing alternative content formats for users with disabilities. **Remember:** These are just guidelines, and you can customize them based on your specific content and audience.

正文

1、Reflect是什么,有什么作用?

Reflect是ES6为了操作对象而新增的API,Reflect对象是一个全局的普通的对象,Reflect的原型就是Object.

作用:将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上,那么以后我们就可以从Reflect对象上可以拿到语言内部的方法。

2、实际例子

2.1 Reflect.get(target, name, receiver)

该方法是用来读取一个对象的属性。

参数如下解析:

target: 目标对象

name: 是我们要读取的属性。

receiver(可选): 可以理解为上下文this对象。

const obj2 = {
  name: 'kongzhi2',
  age: 30,
  get xxx() {
    console.log(this.name);
    console.log('----xxxx---');
    return 0;
  }
};
console.log(Reflect.get(obj2, 'xxx', {name: 'happy2'}));
/*
  先执行 obj2 该对象中的 xxx 方法,指定了第三个参数作为该上下文对象,
  因此会打印 happy2, 然后继续打印 ----xxxx---, 最后我们可以看到
  有返回值为0,因此打印0了 
*/

2.2 Reflect.set(target,name,value,receiver)

上面的get方法是获取对象中的值,那么set就是设置该对象的属性值了,参数解析简单如下:

target: 我们需要操作的对象。

name: 我们需要设置该对象的属性名。

value: 我们要设置的属性值。

receiver: 可以理解为上下文this对象。如果我们在设置值的时候遇到setter函数,该参数就指向与setter中上下文this对象。
该函数会返回一个Boolean的值,代表在目标对象上设置属性是否成功。

const obj = {
  age: 30,
  set name(name) {
    console.log(this); 
    console.log('-------');
  }
};

const res = Reflect.set(obj, 'age', 31);
console.log(res); // true
console.log(obj); // {age: 31, set name:function} 这样的
console.log(obj.age); // 打印 31

/*
 如下代码,设置 obj对象中的name属性,因此打印 console.log(this)
 返回 {age: 31, set name:function} 这样的, console.log(res2)返回true,设置成功
*/
const res2 = Reflect.set(obj, 'name', 'xxxx');
console.log(res2); // true

/*
 先执行 set 中的name方法,打印 console.log(this);this就指向了第四个参数 {test: 'test'}
 然后会打印 '-----'; 
*/
const r2 = Reflect.set(obj, 'name', 'dreamapple', {test: 'test'}); // this: -->  { test: 'test' }
console.log(r2); // true
console.log(obj); // { name: [Setter], age: 31 }

2.3 Reflect.apply(target,thisArg,args)

该方法的含义是:通过指定的参数列表对该目标函数的调用。该方法类似于我们之前的 Function.prototype.apply 方法的。

参数解析如下:

target: 我们的目标函数.

thisArg: target函数调用的时候绑定的this对象。

args: 就是函数参数列表。

// 查找数组里面最小的元素值

const arrs = [1, 2, 3, 4];
// ES6 的语法如下
const min = Reflect.apply(Math.min, arrs, arrs);

console.log(min); // 1

// ES5的语法如下:

const min2 = Math.min.apply(arrs, arrs);
console.log(min2); // 1

// 或者我们使用 Finction.prototype 代码如下演示

const min3 = Function.prototype.apply.call(Math.min, arrs, arrs);
console.log(min3); // 1

// 下面是截取字符串的方法演示下 

const strs = 'kongzhi';

// 使用ES6的语法 代码演示如下:

const str1 = Reflect.apply(String.prototype.slice, strs, [0, 3]);
console.log(str1); // 打印 kon

// 使用 ES5的语法 
const str2 = strs.slice(0, 3);
console.log(str2); // 打印 kon

// 或者我们使用 String.prototype 代码如下演示
const str3 = String.prototype.slice.apply(strs, [0, 3]);
console.log(str3); // kon

2.4 Reflect.construct(target,args[, newTarget])

该方法的作用和 new AAA() 创建一个实列方法作用类似,那么使用该方法,我们就可以提供一种不使用new来调用构造函数的方法,

参数含义如下:

target: 被运行的目标函数。

args: 调用构造函数传递的参数数组或伪数组。

newTarget: 也是构造函数,表示使用 Reflect.construct后生成的实列对象是谁的实列。如果没有该参数,默认生成的实列对象就和target构造函数是一样的。

function XXXX(name) {
  this.name = name;
}

XXXX.prototype.getName = function() {
  return this.name;
}

function YYYY(age) {
  this.age = age;
}

YYYY.prototype.getAge = function() {
  return this.age || 31;
}

// 使用 XXXX函数作为构造函数, 那么构造函数就指向了 XXXX函数
const xxxx = Reflect.construct(XXXX, ['xx']);
console.log(xxxx);  // 打印 XXXX {name: xx}
console.log(xxxx.getName()); // 打印 xx

结果如下:
image
使用 YYYY 函数作为构造函数,那么构造函数就指向了 YYYY函数

const yyyy = Reflect.construct(XXXX, ['30'], YYYY);

console.log(yyyy); // 打印 YYYY {name: 30}
console.log(yyyy.name); // 30
console.log(yyyy.age); // undefined
console.log(yyyy instanceof YYYY); // true
console.log(yyyy instanceof XXXX);  // false
console.log(yyyy.getAge()); // 31

如上demo所示:当const xxxx = Reflect.construct(XXXX, ['xx']); 没有第三个参数的时候,那么构造函数指向了 XXXX 函数。
我们继续看第二个demo,const yyyy = Reflect.construct(XXXX, ['30'], YYYY); 有第三个参数,因此 yyyy的实列指向了 YYYY.
如上代码打印的信息看到 console.log(yyyy instanceof YYYY); 返回true, console.log(yyyy instanceof XXXX); 返回false.
但是呢 console.log(yyyy.getAge()); 返回的是 31. 如果我们没有默认的 31值的话,那么就应该返回undefined了,可以看到,请看下面的注意总结:

注意:如果有第三个参数的话,那么我们的实列由两部分组成,实列的属性部分由第一部分构造函数生成。实列的方法由第三个参数对象生成。

比如上面打印的 console.log(yyyy); // 打印 YYYY {name: 30} 看到只返回了 XXXX中的name属性,XXXX中的getName方法并没有拿到。
同理如上 console.log(yyyy.age); 为undefined, console.log(yyyy.getAge()); 返回了31. 如下图所示:
image

2.5 Reflect.defineProperty(target,name,desc)

该方法与Object.defineProperty方法类似的,不过唯一的区别是 Reflect.defineProperty返回值是一个Boolean的值。
比如如下基本的代码比较:

const obj = {};
// 使用 Object.defineProperty
try {
  Object.defineProperty(obj, 'a', {
    value: 22
  })
} catch(e) {
  console.log('define property failed');
}

// 使用 Reflect.defineProperty

const res = Reflect.defineProperty(obj, 'b', {
  configurable: true,
  enumerable: true
});

console.log(res); // true

因此总结一下:如果使用Object.defineProperty的属性定义失败了,就会抛出一个错误,成功的话就会返回这个对象;
Reflect.defineProperty如果定义属性失败的话就会返回false,如果成功定义的话,就会返回true。
但是如果使用Reflect.defineProperty函数,它的第一个参数不是对象的话,也会抛出错误。

2.6

该方法用于删除一个对象上的属性,它和delete操作符类似的。

参数如下:

target: 表示要操作的对象。

name: 表示要删除该对象上的属性。

该函数返回值是一个Boolean的值,如果成功的话,返回true,失败的话返回false。比如如下demo演示:

const obj = {
  name: 'kongzhi',
  age: 30
};

let test1 = Reflect.deleteProperty(obj, 'name');
console.log(test1);  // true
console.log(obj); // {age: 30}

// 如果删除对象上不存在的属性的话,也是返回true的
let test2 = Reflect.deleteProperty(obj, 'xx');
console.log(test2); // true
console.log(obj); // {age: 30}

let test3 = Reflect.deleteProperty(obj, 'age');
console.log(test3); // true
console.log(obj); // {}

2.7 Reflect.has(target,name)

该方法的含义是:检查一个对象上是否含有特定的属性。相当于es5中的in操作符。
那么参数 target: 就是改对象哦,name的含义是:该对象上的属性。
具体的demo演示如下:

// 一般的对象

const obj = {
  name: 'kongzhi',
  age: 30
};

console.log(Reflect.has(obj, 'name')); // true
console.log(Reflect.has(obj, 'username')); // 该对象上没有 username属性  返回false
console.log(Reflect.has(obj, 'age')); // true

// 函数的实列

function Obj(name) {
  this.name = name;
}

Obj.prototype.getName = function() {
  return this.name;
}

const test = new Obj();

// 使用in操作符测试
console.log('name' in test); // true
console.log('getName' in test); // true

// 使用Reflect.has 测试
console.log(Reflect.has(test, 'name')); // true
console.log(Reflect.has(test, 'getName')); // true

2.8 Reflect.ownKeys(target)

该函数的作用是:返回由目标对象自身的属性键组成的数组(类似于Object.keys?)。如果这个目标对象不是一个对象的话,那么该函数就会抛出一个异常。
target参数:它是一个对象。如下代码演示:

const obj = {
  name: 'kongzhi',
  age: 30
};

console.log(Reflect.ownKeys(obj)); // ['name', 'age'];

2.9 Reflect.preventExtensions(target)

该方法的作用是 阻止新的属性添加到对象中去(类似于Object.preventExtensions?)。target参数必须是一个对象,否则的话会抛出一个异常。
如下代码演示:

const obj = {};
// 判断该对象是否可以扩展,使用 Reflect.isExtensible 该方法
const t1 = Reflect.isExtensible(obj);
console.log(t1);  // true

// 使用 Reflect.preventExtensions 来阻止该对象扩展

Reflect.preventExtensions(obj);

// 再来扩展下该对象,看是否可以
const t2 = Reflect.isExtensible(obj);

console.log(t2); // false

2.10 Reflect.isExtensible(target)

该方法的作用是检查一个对象是否可以扩展的,也就是说对象里面是否可以添加新的属性或方法。
target参数表示目标对象。如果该目标对象不是一个对象的话,那么函数会抛出一个异常。
该函数会返回一个Boolean值,如果为true的话,说明该对象可以扩展,否则的话返回false,表示该对象不可以扩展。
如下demo来演示下:

const obj = {};
// 判断该对象是否可以扩展,使用 Reflect.isExtensible 该方法
const t1 = Reflect.isExtensible(obj);
console.log(t1);  // true

// 使用 Reflect.preventExtensions 来阻止该对象扩展
Reflect.preventExtensions(obj);

// 再来扩展下该对象,看是否可以
const t2 = Reflect.isExtensible(obj);

console.log(t2); // false

2.11 Reflect.getOwnPropertyDescriptor(target, name)

该方法的参数如下解析:

target: 表示的是目标对象。

name: 表示目标对象的属性

该方法的具体含义是:如果目标对象中的属性描述符存在的话,就返回这个属性描述符,如果不存在,就返回undefined。
如下demo演示:

const obj = {};

Reflect.defineProperty(obj, 'name', {
  configurable: true,
  enumerable: true,
  writable: true,
  value: '30'
});

const test1 = Reflect.getOwnPropertyDescriptor(obj, 'name');
/*
 打印值如下:
 {
  configurable: true
  enumerable: true
  value: "30"
  writable: true
 }
*/
console.log(test1);

const test2 = Reflect.getOwnPropertyDescriptor(obj, 'age');
console.log(test2); // undefined

// 如果第一个参数不是对象
const test3 = Object.getOwnPropertyDescriptor('kkkk', 'name');
console.log(test3); // undefined

// 使用 try catch 包围,会执行 catch方法内部代码
try {
    const test4 = Reflect.getOwnPropertyDescriptor('kkkk', 'name');
    console.log(test4);
} catch (e) {
    console.log('error');
}

2.12 Reflect.getPrototypeOf(target)

该方法是返回一个对象的原型的,也就是说内部的 [[Prototype]] 属性的值。来看如下代码:

function testA() {};

testA.prototype.xxx = function() {};

const a = new testA();

console.log(Object.getPrototypeOf(a));

打印 如下图所示:
image

2.13 Reflect.setPrototypeOf(target, prototype)

该方法的作用是设置一个对象的原型。如果设置成功的话,这个对象就返回一个true,如果设置失败的话,这个对象就返回一个false。
比如如下代码:

const obj = {};
const test1 = Reflect.setPrototypeOf(obj, Object.prototype);
console.log(test1); // true

let test2 = Reflect.setPrototypeOf(Object.freeze({}), null);
console.log(test2); // false

与每日一题: 细说es6中的Reflect相似的内容:

每日一题: 细说es6中的Reflect

1、Reflect是什么,有什么作用? Reflect是ES6为了操作对象而新增的API,Reflect对象是一个全局的普通的对象,Reflect的原型就是Object. 作用:将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上

每日一题:通过css变量来控制主题

1、定义不同主题颜色 :root{ --theme-color: blue; --font-size: 18px;; } html[theme="dark"]{ --theme-color: #000; 2、通过切换html自定义属性来控制主题

每日一题:探究响应式本质,以最简单的方式理解响应式

1、响应式本质 就是把数据和函数相关联起来,当数据变化时,函数自动执行。当然这对于函数和数据也是有要求的 函数必须是以下几种: render computed watch watchEffect 数据必须是以下几种: 响应式数据 在函数中用到的数据 2、例子 2.1

每日一题:吃透大文件上传问题(附可运行的前后端源码)

大文件上传 前言 在日常开发中,文件上传是常见的操作之一。文件上传技术使得用户可以方便地将本地文件上传到Web服务器上,这在许多场景下都是必需的,比如网盘上传、头像上传等。 但是当我们需要上传比较大的文件的时候,容易碰到以下问题: 上传时间比较久 中间一旦出错就需要重新上传 一般服务端会对文件的大小

每日一题:无感刷新页面(附可运行的前后端源码,前端vue,后端node)

1、前言 想象下,你正常在网页上浏览页面。突然弹出一个窗口,告诉你登录失效,跳回了登录页面,让你重新登录。你是不是很恼火。这时候无感刷新的作用就体现出来了。 2、方案 2.1 redis设置过期时间 在最新的技术当中,token一般都是在Redis服务器存着,设置过期时间。只要在有效时间内,重新发出

每日一题:AJAX进度监控(附可运行源码)

1、什么是AJAX AJAX(Asynchronous JavaScript and XML)是一种用于在网页上进行异步通信的技术。它允许在不刷新整个页面的情况下,通过在后台与服务器进行数据交换来更新部分网页内容。 传统的网页开发中,当用户与网页进行交互时,需要刷新整个页面才能获取最新的内容。而使用

每日一题:vue3自定义指令大全(呕心沥血所作,附可运行项目源码)

1.VUE常用指令大全 本项目所有指令均为全局注册,使用时直接在组件中使用即可。 指令目录:src/directives 页面目录:src/views 具体可查看源码 1.1 权限指令 封装一个权限指令,在模板中根据用户权限来控制元素的显示或隐藏。 permission.js import { re

每日一题:如何判断是否是数组,一个既简单又复杂的问题。(不要再用Object.prototype.toString.call、instance of判断了!!!)

1、不要使用Object.prototype.toString.call() 正常情况下: const arr = [1,2,3,4,5] const obj = {} console.log(Object.prototype.toString.call(arr))//[Object,Array]

文心一言 VS 讯飞星火 VS chatgpt (112)-- 算法导论10.2 3题

三、用go语言,用一个单链表L实现一个队列。要求操作 ENQUEUE 和 DEQUEUE 的运行时间仍为O(1)。 文心一言: 在 Go 语言中,使用单链表实现队列的操作,包括入队(ENQUEUE)和出队(DEQUEUE),并保持操作的时间复杂度为 O(1),需要利用两个指针,一个指向队头,另一个指

文心一言 VS 讯飞星火 VS chatgpt (110)-- 算法导论10.2 1题

一、用go语言,单链表上的动态集合操作 INSERT 能否在 O(1)时间内实现?DELETE 操作呢? 文心一言: 在单链表中,INSERT 和 DELETE 操作的时间复杂度通常是 O(n),其中 n 是链表中的元素数量。这主要是因为当你插入或删除元素时,你需要遍历链表来找到正确的插入或删除位置