作者:easonruan,腾讯 CSIG 前端开发工程师

1. 装饰器的样子

我们先来看看 Decorator 装饰器长什么样子,大家可能没在项目中用过 Decorator 装饰器,但多多少少会看过下面装饰器的写法:

/* Nest.Js cats.controller.ts */
import { Controller, Get } from '@nestjs/common';@Controller('cats')
export class CatsController {@Get()findAll(): string {return 'This action returns all cats';}
}

摘自《Nest.Js》官方文档

上述代码大家可以不着急去理解,主要是让大家对装饰器有一个初步了解,后面我们会逐一分析 Decorator 装饰器的实现原理以及具体用法。

2. 为什么要理解装饰器

2.1 浅一点来说,理解才能读懂 VS Code 源码

Decorator 装饰器是 ECMAScript 的语言提案,目前还处于 stage-2 阶段,但是借助 TypeScript 或者 Babel,已经有大量的优秀开源项目深度用上它了,比如:VS Code, Angular, Nest.Js(后端 Node.js 框架), TypeORM, Mobx(5) 等等

举个例子:https://github.com/microsoft/vscode/blob/main/src/vs/workbench/services/editor/browser/codeEditorService.ts#L22

作为一个有追求的程序员,你可能会问:上面代码的装饰器代表什么含义?去掉装饰器后能不能正常运行?

如果没弄懂装饰器,很难读懂 VS Code 这些优秀项目源码的核心思想。所以说你不需要熟练使用装饰器,但一定要理解装饰器的用法。

2.2 深一点来说,理解才能弄懂 AOP , IoC, DI 等优秀编程思想

1.AOP 即面向切面编程 (Aspect Oriented Programming)

AOP 主要意图是将日志记录,性能统计,安全控制,异常处理等代码从业务逻辑代码中划分出来,将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

简而言之,就是“优雅”的把“辅助功能逻辑”从“业务逻辑”中分离,解耦出来。

图摘自《简谈前端开发中的 AOP(一) -- 前端 AOP 的实现思路》

2.IoC 即 控制反转 (Inversion of Control),是解耦的一种设计理念
3.DI 即 依赖注入 (Dependency Injection),是 IoC 的一种具体实现

使用 IoC 前:

使用 IoC 后:

图摘自《两张图让你理解 IoC (控制反转)》

IoC 控制反转的设计模式可以大幅度地降低了程序的耦合性。而 Decorator 装饰器在 VS Code 的控制反转设计模式里,其主要作用是实现 DI 依赖注入的功能和精简部分重复的写法。由于该步骤实现较为复杂,我们先从简单的例子为切入点去了解装饰器的基本原理。

3. 装饰器的概念区分

在理解装饰器之前,有必要先对装饰器的 3 个概念进行区分。

3.1 Decorator Pattern (装饰器模式)

是一种抽象的设计理念,核心思想是在不修改原有代码情况下,对功能进行扩展。

3.2 Decorator (装饰器)

是一种特殊的装饰类函数,是一种对装饰器模式理念的具体实现。

3.3 @Decorator (装饰器语法)

是一种便捷的语法糖(写法),通过 @ 来引用,需要编译后才能运行。理解了概念之后可以知道:装饰器的存在就是希望实现装饰器模式的设计理念。

说法 1:在不修改原有代码情况下,对功能进行扩展。也就是对扩展开放,对修改关闭。

说法 2:优雅地把“辅助性功能逻辑”从“业务逻辑”中分离,解耦出来。(AOP 面向切面编程的设计理念)

4. 装饰器的实战:记录函数耗时

现在有一个 关羽(GuanYu) 类,它有两个函数方法:attack(攻击)run(奔跑)

class GuanYu {attack() {console.log('挥了一次大刀')}run() {console.log('跑了一段距离')}
}

而我们都是优秀的程序员,时时刻刻都有着经营思维 (性能优化),因此想给 关羽(GuanYu) 的函数方法提前做好准备:记录关羽的每一次 attack(攻击)run(奔跑) 的执行时间,以便于后期做性能优化。

4.1 做法一:复制粘贴,不用思考一把梭就是干

拿到需求,不用多想,立刻在函数前后,添加记录函数耗时的逻辑代码,并复制粘贴到其他地方:

class GuanYu {attack() {
+   const start = +new Date()console.log('挥了一次大刀')
+   const end = +new Date()
+   console.log(`耗时: ${end - start}ms`)}run() {
+   const start = +new Date()console.log('跑了一段距离')
+   const end = +new Date()
+   console.log(`耗时: ${end - start}ms`)}
}

但是这样直接修改原函数代码有以下几个问题:

  1. 理解成本高

统计耗时的相关代码与函数本身逻辑并无关系,对函数结构造成了破坏性的修改,影响到了对原函数本身的理解

  1. 维护成本高

如果后期还有更多类似的函数需要添加统计耗时的代码,在每个函数中都添加这样的代码非常低效,也大大提高了维护成本

4.2 做法二:装饰器模式,不修改原代码扩展功能

4.2.1 装饰器前置基础知识

在开始用装饰器实现之前必须掌握以下基础:

  1. Object.getOwnPropertyDescriptor()

返回指定对象上一个自有属性对应的属性描述符

var a = { b: () => {} }
var descriptor = Object.getOwnPropertyDescriptor(a, 'b')
console.log(descriptor)
/*** {*   configurable: true,  // 可配置的*   enumerable: true,    // 可枚举的*   value: () => {},     // 该属性对应的值(数值,对象,函数等)*   writable: true,      // 可写入的* }*/

这里要注意一个点是:value 可以是 JavaScript 的任意值,比如函数方法,正则,日期等

  1. Object.defineProperty()

在一个对象上定义或修改一个属性的描述符:

const object1 = {};Object.defineProperty(object1, 'property1', {value: 'ThisIsNotWritable',writable: false
});object1.property1 = 'newValue';
// throws an error in strict modeconsole.log(object1.property1);
// expected output: 'ThisIsNotWritable'
4.2.2 【重点】手写一个装饰器函数

有了上面的两个基础后,我们开始利用装饰器模式的设计理念,用纯函数的形式写一个装饰器,实现记录函数耗时功能。为了让大家更深刻理解装饰器的原理,我们先不用 @Decorator 这个语法糖。

下面代码是本文的重点,大家可以放慢阅读速度,理解后再继续往下看:

// 装饰器函数
function decoratorLogTime(target, key) {const targetPrototype = target.prototype// Step1 备份原来类构造器上的属性描述符 Descriptorconst oldDescriptor = Object.getOwnPropertyDescriptor(targetPrototype, key)// Step2 编写装饰器函数业务逻辑代码const logTime = function (...arg) {// Before 钩子let start = +new Date()try {// 执行原来函数return oldDescriptor.value.apply(this, arg) // 调用之前的函数} finally {// After 钩子let end = +new Date()console.log(`耗时: ${end - start}ms`)}}// Step3 将装饰器覆盖原来的属性描述符的 valueObject.defineProperty(targetPrototype, key, {...oldDescriptor,value: logTime})
}class GuanYu {attack() {console.log('挥了一次大刀')}run() {console.log('跑了一段距离')}
}
// Step4 手动执行装饰器函数,装饰 GuanYu 的 attack 函数
decoratorLogTime(GuanYu, 'attack')
// Step4 手动执行装饰器函数,装饰 GuanYu 的 run 函数
decoratorLogTime(GuanYu, 'run')const guanYu = new GuanYu()
guanYu.attack()
// 挥了一次大刀
// 耗时: 0ms
guanYu.run()
// 跑了一段距离
// 耗时: 0ms

以上就是装饰器的具体实现方法,其核心思路是:

  1. Step1 备份原来类构造器 (Class.prototype) 的属性描述符 (Descriptor)

利用 Object.getOwnPropertyDescriptor 获取

  1. Step2 编写装饰器函数业务逻辑代码

利用执行原函数前后钩子,添加耗时统计逻辑

  1. Step3 用装饰器函数覆盖原来属性描述符的 value

利用 Object.defineProperty 代理

  1. Step4 手动执行装饰器函数,装饰 Class(类) 指定属性

从而实现在不修改原代码的前提下,执行额外逻辑代码

5. @Decorator 装饰器语法糖

但上一步 4.2.2 手写的装饰器函数存在两个可优化的点:

  1. 是否可以让装饰器函数更关注业务逻辑?

Step1, Step2 是通用逻辑的,每个装饰器都需要实现,简单来说就是可复用的。

  1. 是否可以让装饰器写法更简单?

纯函数实现的装饰器,每装饰一个属性都要手动执行装饰器函数,详见 Step4 步骤。针对上述优化点,装饰器草案中有一颗特别甜的语法糖,也就是 @Decorator ,它能够帮你省去很多繁琐的步骤来用上装饰器。

只需要在想使用的装饰器前加上@符号,装饰器就会被应用到目标上。

5.1 @Decorator 语法糖的便捷性

下面我们用 @Decorator 的写法,来实现同样的功能,看看代码量可以精简多少:

// Step2 编写装饰器函数业务逻辑代码
function logTime(target, key, descriptor) {const oldMethed = descriptor.valueconst logTime = function (...arg) {let start = +new Date()try {return oldMethed.apply(this, arg) // 调用之前的函数} finally {let end = +new Date()console.log(`耗时: ${end - start}ms`)}}descriptor.value = logTimereturn descriptor
}class GuanYu {// Step4 利用 @ 语法糖装饰指定属性@logTimeattack() {console.log('挥了一次大刀')}// Step4 利用 @ 语法糖装饰指定属性@logTimerun() {console.log('跑了一段距离')}
}const guanYu = new GuanYu()
guanYu.attack()
// [LOG]: 挥了一次大刀
// [LOG]: 耗时: 3ms
guanYu.run()
// [LOG]: 跑了一段距离
// [LOG]: 耗时: 3ms

为了让更直观了解上述代码是否可以编译后正常执行,

我们可以从 TypeScript Playground 直接看到编译后的代码以及运行结果,

注意!为了方便理解,记得关闭配置 emitDecoratorMetadata 禁止输出元数据,

元数据是另一个比较复杂的知识点,我们本篇文章先跳过

关闭后编译的代码会更简单

我们打开上面代码的在线 Playground 链接,点击 Run 运行按钮,即可看到其正常运行和输出结果:

对比纯手写的装饰器,用 @Decorator 语法糖可以省去 2 个重复的步骤:

  • Step1 备份原来类构造器 (Class.prototype) 的属性描述符 (Descriptor)

    const oldDescriptor = Object.getOwnPropertyDescriptor(targetPrototype, key)
    
  • Step3 用装饰器函数覆盖原来属性描述符的 value

    Object.defineProperty(targetPrototype, key, {...oldDescriptor,value: logTime
    })
    

开发者仅需两步即可实现装饰器的功能,可以更专注于装饰器本身的业务逻辑:

  • Step2 编写装饰器函数业务逻辑代码

    function logTime(target, key, descriptor) {const oldMethed = descriptor.valueconst logTime = function (...arg) {let start = +new Date()try {return oldMethed.apply(this, arg) // 调用之前的函数} finally {let end = +new Date()console.log(`耗时: ${end - start}ms`)}}descriptor.value = logTimereturn descriptor
    }
    
  • Step4 利用 @ 语法糖装饰指定属性

    @logTime
    attack() {console.log('挥了一次大刀')
    }
    

5.2 【重点】分析 @Decorator 语法糖编译后的代码

@Decorator 语法糖很甜,但却不能直接食用。因为装饰器目前仅仅是 ECMAScript 的语言提案,还处于 stage-2 阶段,无论是最新版的 Chrome 浏览器还是 Node.js 都不能直接运行带有 @Decorator 语法糖的代码。我们需要借助 TypeScript 或者 Babel 的能力,将源码编译后才能正常运行。而在 TypeSciprt Playground 上,我们可以直接看到编译后代码。

为了更清晰容易理解,我们把编译后的业务代码先注释掉,只看装饰器实现的相关代码:

"use strict";
// Part1 装饰器工具函数(__decorate)的定义
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};function logTime(target, key, descriptor) {// ...
}class GuanYu {// ...
}// Part2 装饰器工具函数(__decorate)的执行
__decorate([logTime], GuanYu.prototype, "attack", null);
__decorate([logTime], GuanYu.prototype, "run", null);// ...

上述代码核心点是两个部分,一个是定义,一个是执行。定义部分较为复杂,我们先从执行入手:Part2 装饰器工具函数(__decorate)的执行会传入以下 4 个参数:

  1. 装饰器业务逻辑函数

  2. 类的构造器

  3. 类的构造器属性名

  4. 属性描述符(可以为 null)

为了方便理解 Part1 装饰器工具函数 __decorate 的定义,我们需要精简 __decorate 的函数代码,让它变成最简单的样子,而精简代码的前提是收集条件:

  • 条件 1 (this && this.__decorate) 可删除

这里的 this 是指 window 对象,这一步的含义是避免重复定义 __decorate 函数,属于辅助代码,可删掉。

  • 条件 2 c < 3 === false

Part1 的 c = arguments.length 代表参数的个数,由 Part2 我们知道工具函数会传入 4 个参数,因此在本次案例中 c < 3 参数个数小于 3 的情况不存在,即 c < 3 === false,

  • 条件 3 c > 3 === true

本次案例中 c > 3 参数大于 3 的情况存在,即 c > 3 === true 。

  • 条件 4 desc === null

同时在 Part1 我们知道第四个参数 desc 传入的值就是 null ,即 desc === null

  • 条件 5 typeof Reflect !== "object"

Reflect 反射是 ES6 的语法,本文为了更容易理解,暂不引入新的 ES6 特性和语法,让环境默认为 ES5,即不存在 Reflect 对象,即 typeof Reflect !== "object",有了上述条件后,我们可以进一步精简 __decorate 的方法

  • 代码片段 1:

r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc// 根据 c < 3 === false , desc === null 条件
// 精简后r = desc = Object.getOwnPropertyDescriptor(target, key)
// r 和 desc 此时代表的是属性的描述符 Descriptor
  • 代码片段 2:

if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;// 根据 c < 3 === false , c > 3 === true 条件
// 精简后if (d = decorators[i]) r = d(target, key, r) || r;
  • 代码片段 3:

if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);// 为了方便理解,本案例暂认为 Reflect 不存在
// 精简后// 空
  • 代码片段 4:

return c > 3 && r && Object.defineProperty(target, key, r), r;// 根据 c > 3 === true, r 是属性描述符,必定存在
// 精简后Object.defineProperty(target, key, r)
return r;
  • 精简后整体代码:

var __decorate = function (decorators, target, key, desc) {var c = arguments.length;// Step1 备份原来类构造器 (Class.prototype) 的属性描述符 (Descriptor)var r = desc = Object.getOwnPropertyDescriptor(target, key),var d;for (var i = decorators.length - 1; i >= 0; i--) {// d 为装饰器业务逻辑函数if (d = decorators[i]) {// 执行 d,并传入 target 类构造器,key 属性名,r 属性描述符r = d(target, key, r) || r;}}// Step3 用装饰器函数覆盖原来属性描述符Object.defineProperty(target, key, r)return r;
};

代码经过精简之后核心原理还是和我们 4.2.2 手写一个装饰器函数的原理是一样的。

  1. Step1 备份原来类构造器 (Class.prototype) 的属性描述符 (Descriptor)

利用 Object.getOwnPropertyDescriptor 获取

  1. **Step3 用装饰器函数覆盖原来属性描述符的 value **

利用 Object.defineProperty 代理

TypeScript 对装饰器编译后的代码,只不过是把装饰器可复用的逻辑抽离成一个工具函数,方便复用而已。分析到这里,是不是对 @Decorator 装饰器最根本的实现有了更深入的了解?从上面的例子,我们也进一步验证了:

  1. Decorator Pattern 装饰器模式的设计理念:在不修改原有代码情况下,对功能进行扩展

  2. Decorator 装饰器的具体实现,本质是函数,参数有 target, key, descriptor

  3. @Decoretor 是装饰器的一种语法糖,只是一种便捷写法,编译后本质还是一个函数

6. 带参数的装饰器:装饰器工厂函数

在上面的「记录函数耗时」例子中,如果我们希望在日志前面加个可变的标签,如何实现?

答案是使用带参数的装饰器

重点:logTime 是个高阶函数,可以理解成装饰器工厂函数,其接收参数执行后,返回一个装饰器函数

function logTime(tag) { // 这是一个装饰器工厂函数return function(target, key, descriptor) {  // 这是装饰器const oldMethed = descriptor.valueconst logTime = function (...arg) {let start = +new Date()try {return oldMethed.apply(this, arg)} finally {let end = +new Date()console.log(`【${tag}】耗时: ${end - start}ms`)}}descriptor.value = logTimereturn descriptor}
}class GuanYu {@logTime('攻击')attack() {console.log('挥了一次大刀')},@logTime('奔跑')run() {console.log('跑了一段距离')}
}// ...

编译后:

// ...__decorate([logTime('攻击')], GuanYu.prototype, "attack", null);
__decorate([logTime('奔跑')], GuanYu.prototype, "run", null);// ...

看了编译后的代码,我们就很容易知道带参数装饰器的具体实现原理,无非是直接先执行装饰器工厂函数,此时传入对应参数,然后返回一个新的装饰器业务逻辑的函数。

7. 五种装饰器:类、属性、方法、参数、访问器

我们上面学了那么多装饰器的内容,其实只学了一种装饰器:方法装饰器,而装饰器一共有 5 种类型可被我们使用:

  1. 类装饰器

  2. 属性装饰器

  3. 方法装饰器

  4. 访问器装饰器

  5. 参数装饰器

先来个全家福,然后我们逐一攻破

// 类装饰器
@classDecorator
class GuanYu {// 属性装饰器@propertyDecoratorname: string;// 方法装饰器@methodDecoratorattack (// 参数装饰器@parameterDecoratormeters: number) {}// 访问器装饰器@accessorDecoratorget horse() {}
}

7.1 类装饰器

类型声明:

// 类装饰器
function classDecorator(target: any) {return // ...
};
  • @参数:只接受一个参数

target: 类的构造器

  • @返回:如果类装饰器返回了一个值,她将会被用来代替原有的类构造器的声明

    因此,类装饰器适合用于继承一个现有类并添加一些属性和方法。例如我们可以添加一个 addToJsonString 方法给所有的类来新增一个 toString 方法

function addToJSONString(target) {return class extends target {toJSONString() {return JSON.stringify(this);}};
}@addToJSONString
class C {public foo = "foo";public num = 24;
}console.log(new C().toJSONString())
// [LOG]: "{"foo":"foo","num":24}"

7.2 方法装饰器

已经在上面章节介绍过利用方法装饰器来实现「记录函数耗时」功能,现在我们重新复习下

类型声明:

// 方法装饰器
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {return // ...
};
  • @参数:

  1. target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链

  2. propertyKey: 属性的名称

  3. descriptor: 属性的描述器

  • @返回:如果返回了值,它会被用于替代属性的描述器。

  • 利用方法装饰器我们可以实现更多的具体场景,比如「打印 Request 的请求参数和结果」功能:

    function loggerParamsResult(target, propertyKey, descriptor) {const original = descriptor.value;descriptor.value = async function (...args) {let resultlet errortry {result = await original.call(this, ...args);} catch(e) {error = new Error(e)}if (error) {console.error('请求失败!')console.error('请求参数: ', ...args)console.error('失败原因: ', error)} else {console.log('请求成功!')console.log('请求参数', ...args)console.log('请求结果: ', result)}return result;}
    }class App {@loggerParamsResultrequest(data) {return new Promise((resolve, reject) => {const random = Math.random() > 0.5if (random) {resolve(random)} else {reject(random)}})}
    }const app = new App();
    app.request({ url: 'https://www.tencent.com'});// [LOG]: "请求成功!"
    // [LOG]: "请求参数",  {
    //   "url": "https://www.tencent.com"
    // }
    // [LOG]: "请求结果: ",  true// [ERR]: "请求失败!"
    // [ERR]: "请求参数: ",  {
    //   "url": "https://www.tencent.com"
    // }
    // [ERR]: "失败原因: ",  false
    

    总结:

    无论是「记录函数耗时」还是「打印 Request 的请求参数和结果」,本质都是在实现 Before / After 钩子,因此我们只需要记住方法装饰器可以实现与 Before / After 钩子 相关的场景功能。

    课后题:

    除了上述两个例子,大家还能想到方法装饰器有什么好的应用场景吗?

    7.3 属性装饰器

    类型声明:

    // 属性装饰器
    function propertyDecorator(target: any, propertyKey: string) {}
    
    • @参数: 只接受两个参数,少了 descriptor 描述器

    1. target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链

    2. propertyKey: 属性的名称

  • @返回: 返回的结果将被忽略

  • 利用属性装饰器,我们可以实现一个非常简单的属性监听功能 ,当属性改变时触发指定函数:

    function observable(fnName) {  // 装饰器工厂函数return function (target: any, key: string): any {  // 装饰器let prev = target[key];Object.defineProperty(target, key, {set(next) {target[fnName](prev, next);prev = next;}})}
    }class Store {@observable('onCountChange')count = -1;onCountChange(prev, next) {console.log('>>> count has changed!')console.log('>>> prev: ', prev)console.log('>>> next: ', next)}
    }const store = new Store();
    store.count = 10// [LOG]: ">>> count has changed!"
    // [LOG]: ">>> prev: ",  undefined
    // [LOG]: ">>> next: ",  -1
    // [LOG]: ">>> count has changed!"
    // [LOG]: ">>> prev: ",  -1
    // [LOG]: ">>> next: ",  10
    

    7.4 访问器装饰器

    访问器装饰器总体上讲和方法装饰器很接近,唯一的区别在于第三个参数 descriptor 描述器中有的 key 不同:

    方法装饰器的描述器的 key 为:

    • value

    • writable

    • enumerable

    • configurable

    访问器装饰器的描述器的 key 为:

    • get

    • set

    • enumerable

    • configurable

    类型声明:

    // 访问器装饰器
    function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {return // ...
    };
    

    例如,我们可以将某个属性在赋值的时候做一层代理,额外相加一个值:

    function addExtraNumber(num) {  // 装饰器工厂函数return function (target, propertyKey, descriptor) { // 装饰器const original = descriptor.set;descriptor.set = function (value) {const newObj = {}Object.keys(value).forEach(key => {newObj[key] = value[key] + num})return original.call(this, newObj)}}
    }class C {private _point = { x: 0, y: 0 }@addExtraNumber(2)set point(value: { x: number, y: number }) {this._point = value;}get point() {return this._point;}
    }const c = new C();
    c.point = { x: 1, y: 1 };console.log(c.point)// [LOG]: {
    //   "x": 3,
    //   "y": 3
    // }
    

    7.5 参数装饰器

    类型声明:

    // 参数装饰器
    function parameterDecorator(target: any, methedKey: string, parameterIndex: number) {}
    
    • @参数:接收三个参数

    1. target: 对于静态成员来说是类的构造器,对于实例成员来说是类的原型链

    2. methedKey: 方法的名称,注意!是方法的名称,而不是参数的名称

    3. parameterIndex: 参数在方法中所处的位置的下标

  • @返回:返回的值将会被忽略

  • 单独的参数装饰器能做的事情很有限,它一般都被用于记录可被其它装饰器使用的信息。

    function Log(target, methedKey, parameterIndex) {console.log(`方法名称 ${methedKey}`);console.log(`参数顺序 ${parameterIndex}`);
    }class GuanYu {attack(@Log person, @Log dog) {console.log(`向 ${person} 挥了一次大刀`)}
    }// [LOG]: "方法名称 attack"
    // [LOG]: "参数顺序 0"
    

    7.6 装饰器参数总结

    8. 装饰器顺序

    8.1 同种装饰器组合顺序:洋葱模型

    如果同一个方法有多个装饰器,其执行顺序是怎样的?

    答案:

    以方法装饰器为例,同种装饰器组合后,其顺序会像剥洋葱一样,

    先从外到内进入,然后由内向外执行。和 Koa 的中间件顺序类似。

    function dec(id){console.log('装饰器初始化', id);return function (target, property, descriptor) {console.log('装饰器执行', id);}
    }class Example {@dec(1)@dec(2)method(){}
    }// 装饰器初始化 1
    // 装饰器初始化 2
    // 装饰器执行 2
    // 装饰器执行 1
    
    I2b3cL.png

    其原理,看编译后的代码就非常清楚:

    重点:

    1. dec(1), dec(2) 初始化时就执行

    2. for (var i = decorators.length - 1; i >= 0; i--) 是从右向左,倒叙执行

    // 由于本段代码不存在 c < 3 (参数少于3个) 的情况,为了方便理解已精简了部分不可能执行的代码
    var __decorate = function (decorators, target, key, desc) {var c = arguments.length,r = desc = Object.getOwnPropertyDescriptor(target, key),d;for (var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = d(target, key, r) || r;Object.defineProperty(target, key, r)return r;
    };function dec(id) {console.log('装饰器初始化', id);return function (target, property, descriptor) {console.log('装饰器执行', id);};
    }
    class Example {method() { }
    }
    __decorate([dec(1),dec(2)
    ], Example.prototype, "method", null);// 装饰器初始化 1
    // 装饰器初始化 2
    // 装饰器执行 2
    // 装饰器执行 1
    

    8.2 不同类型装饰器顺序:有规则有规律

    1. 实例成员:(参数 > 方法) / 访问器 / 属性 装饰器 (按顺序)

    2. 静态成员:(参数 > 方法) / 访问器 / 属性 装饰器 (按顺序)

    3. 构造器:参数装饰器

    4. 类装饰器

    多种装饰器优先级为:

    实例成员最高,内部成员里面的装饰器则按定义顺序执行,

    依次排下来,类装饰器最低

    function f(key: string): any {// console.log("初始化: ", key);return function () {console.log("执行: ", key);};
    }@f("8. 类")
    class C {@f("4. 静态属性")static prop?: number;@f("5. 静态方法")static method(@f("6. 静态方法参数") foo) {}constructor(@f("7. 构造器参数") foo) {}@f("2. 实例方法")method(@f("1. 实例方法参数") foo) {}@f("3. 实例属性")prop?: number;
    }// "执行: ",  "1. 实例方法参数"
    // "执行: ",  "2. 实例方法"
    // "执行: ",  "3. 实例属性"
    // "执行: ",  "4. 静态属性"
    // "执行: ",  "6. 静态方法参数"
    // "执行: ",  "5. 静态方法"
    // "执行: ",  "7. 构造器参数"
    // "执行: ",  "8. 类"
    

    9. 装饰器总结

    9.1 应用场景

    装饰器很像是组合一系列函数,类似于高阶函数和类。

    合理利用装饰器对一些非内部逻辑相关的代码进行封装提炼,

    能够帮助我们快速完成重复性的工作,节省时间,极大提高开发效率。

    1. 类装饰器

    可添加额外的方法和属性,比如:扩展 toJSONString 方法

    1. 方法装饰器

    可实现 Before / After 钩子功能,比如:记录函数耗时,打印 request 参数结果,节流防抖

    1. 属性装饰器

    可监听属性改变触发其他事件,比如:实现 count 监听器

    1. 访问器装饰器

    2. 参数装饰器

    当然,还有更多可以使用装饰器的场景等着我们去发现

    • 运行时类型检查

    • 依赖注入

    9.2 优点

    • 在不修改原有代码情况下,对功能进行扩展。也就是对扩展开放,对修改关闭。

    • 优雅地把“辅助性功能逻辑”从“业务逻辑”中分离,解耦出来。(AOP 面向切面编程的设计理念)

    • 装饰类和被装饰类可以独立发展,不会相互耦合

    • 装饰模式是 Class 继承的一个替代模式,可以理解成组合

    9.3 缺点

    但是糖再好吃,也不要吃太多,容易坏牙齿的,滥用过多装饰器会导致很多问题:

    • 理解成本:过多带业务功能的装饰器会使代码本身逻辑变得扑朔迷离

    • 调试成本:装饰器层次增多,会增加调试成本,很难追溯到一个 Bug 是在哪一层包装导致的

    9.4 注意事项

    1. 装饰器的功能逻辑代码一定是辅助性的

    比如日志记录,性能统计等,这样才符合 AOP 面向切面编程的思想,如果把过多的业务逻辑写在了装饰器上,效果会适得其反。

    1. 装饰器语法尚未定案以及未被纳入 ES 标准,标准化的过程还需要很长时间

    由于装饰器语法未来制定的标准可能与当前的装饰器实现方案有所不同,Mobx 6 出于兼容性的考虑,放弃了装饰器的用法,并建议使用 makeObservable / makeAutoObservable 代替。

    详情请查看:https://zh.mobx.js.org/enabling-decorators.html

    装饰器提案目前进度:https://github.com/tc39/proposal-decorators

    最近热文:

    5000 万行以上大型代码仓库工程实践

    带你快速了解 Docker 和 Kubernetes

        

                       “科技公益,向善而生”

                2021科技公益-系列技术直播 来啦!

    扫描海报下方二维码,一键预定直播,精彩不容错过!

    点击下方【阅读原文】也可直接预约直播!

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 被低估的.net(中) - 广州.net俱乐部2019年纲领

    这是被低估的.net系列的中篇。上篇在这里&#xff1a;被低估的.net(上) - 微软MonkeyFest 2018广州分享会活动回顾 中篇本来不是这样的&#xff0c;中篇的草稿大纲其实在写上篇之前就写好了&#xff0c;嗯&#xff0c;当时给张队长看过了。然而却因为被.net 粉丝的热情震惊和感…...

    2024/4/21 13:31:13
  2. 全栈前端学习路线

    全栈前端学习路线 第一阶段&#xff1a; HTMLCSS&#xff1a; HTML进阶、CSS进阶、divcss布局、HTMLcss整站开发。 JavaScript基础&#xff1a; Js基础教程、js内置对象常用方法、常见DOM树操作大全、ECMAscript、DOM、BOM、定时器和焦点图。 JS基本特效&#xff1a; 常见特…...

    2024/4/21 13:31:11
  3. 山东大学软件开发解决方案复习提纲

    写在前面&#xff1a; 期末又到了&#xff0c;这是一份新鲜的软件开发解决方案复习提纲&#xff0c;所有问题均来自于老师最后一节课划重点时使用的ppt&#xff0c;请大家放心使用~ 另外&#xff0c;由于这门课程教的知识与企业中真正用的东西严重脱节&#xff0c;所以针对部…...

    2024/4/27 2:11:13
  4. Docker快速搭建Taiga敏捷开发项目管理平台

    Taiga.io , Open Source, full featured project management platform for startups and agile developers &... Created by Taiga Agile, LLCDocker快速搭建Taiga敏捷开发项目管理平台PS:根据转载来源没有快速搭建起来,不过给出下面代码可以成功搭建,单前提您得先安装va…...

    2024/4/21 3:48:59
  5. 地球椭球体(Ellipsoid)、大地基准面(Datum)及地图投影(Projection)三者的基本概念

    地球椭球体(Ellipsoid) 众所周知我们的地球表面是一个凸凹不平的表面&#xff0c;而对于地球测量而言&#xff0c;地表是一个无法用数学公式表达的曲面&#xff0c;这样的曲面不能作为测量和制图的基准面。假想一个扁率极小的椭圆&#xff0c;绕大地球体短轴旋转所形成的规则椭…...

    2024/4/20 5:04:32
  6. 第二眼——AngularJS方法扩展之依赖注入

    依赖注入 DI&#xff08;Dependency Injection&#xff09; 依赖注入主要是将一个功能注入到另一个不想跟的模块里面去&#xff0c; 让这个功能变成这个模块的一部分&#xff0c;叫做依赖注入&#xff0c;类似在一个功能模块中调用另一个模块&#xff08;模块化&#xff09; 只…...

    2024/4/25 16:53:28
  7. 为什么要用AngularJS

    在学习一门技术之前&#xff0c;总得清楚这个技术解决了什么问题&#xff0c;也即它产生的背景和应用的市场。就像Spring这么火的框架&#xff0c;我觉得它的IOC和AOP是一个非常不错的理念&#xff0c;提出了依赖注入&#xff08;解决对象依赖关系的管理&#xff09;和面向切面…...

    2024/4/20 15:55:58
  8. erdas中怎样给无坐标系统的数据定义坐标系统

    转自&#xff1a;http://hi.baidu.com/hello3s/item/fcefb827c0084957c28d59d5 坐标系统与投影变换及在桌面产品中的应用 本文共可分为如下几个部分组成&#xff1a; 地球椭球体(Ellipsoid) 大地基准面&#xff08;Geodetic datum&#xff09; 投影坐标系统&#xff08;Project…...

    2024/4/19 20:30:08
  9. 上海市最好的双眼皮医院哪家最好的医院

    ...

    2024/4/21 13:31:09
  10. 汕头割双眼皮哪个好问曙光

    ...

    2024/4/21 13:31:08
  11. Angular 禁用按钮 2s后启用按钮

    html文件按钮的设置&#xff08;按钮的初始状态为禁用&#xff09; <button [disabled]"!allow">测试按钮</button>ts文件下类的定义 export class MyownComponent{allowfalse;constructor(){setTimeout(()>{this.allowtrue},2000)} }两秒后按钮改为…...

    2024/4/21 13:31:07
  12. Windows10下VirtualBox中安装Ubuntu只有32bit的解决方法

    新电脑想安装个Linux的虚拟机,然后选择了在virtualbox中Ubuntu,但却一直只有32bit的,无法安装64bit的。这样查了一下网上的说法,一般有以下几种:1,未开始虚拟化技术,需要到BIOS中打开。这个可以在任务管理器的CPU中查看。我的电脑本来就已经打开虚拟化技术了,所以不是这…...

    2024/4/21 13:31:05
  13. Angular2 AsyncPipe

    今天我们来介绍一下 Angular 2 中 AsyncPipe (异步管道) &#xff0c;使用 AsyncPipe 我们可以直接在模板中使用 Promise 和Observable 对象&#xff0c;而不用通过定义一个类的成员属性来存储返回的结果。使用async管道不需要再执行 可观察对象的 订阅 或是 promise的then来获…...

    2024/4/21 13:31:05
  14. 做双眼皮能用多少钱啊

    ...

    2024/4/21 13:31:03
  15. 双眼皮埋线哪好询广州紫 馨

    ...

    2024/4/21 13:31:02
  16. 封装vue.js 组件库

    组件化开发 开源组件库&#xff1a;element-yi&#xff0c;iview CDD CDD(component-Driven Development)自下而上从组建级别开始&#xff0c;到页面级别结束 好处&#xff1a; 组建在最大程度被重用并行开发可视化测试 处理组件的边界情况 root−−通过root -- 通过root−…...

    2024/4/21 13:31:01
  17. 0基础菜鸟学前端之Vue.js

    简介&#xff1a;0基础前端菜鸟&#xff0c;啃了将近半月前端VUE框架&#xff0c;对前端知识有了初步的了解。下面总结一下这段时间的学习心得。 文章结构 前端基础Vue.js简介Vue.js常用指令Vue.js组件Vue.js之vue-router插件Vue.js实战一、前端基础 前端发展历史和趋势 什么是…...

    2024/4/30 20:20:02
  18. Vue 小结

    本次串讲的主要目的在于给我们移动端的同学揭秘下目前前端开发的现状&#xff0c;和一些典型框架或者说是库的产生背景、以及设计思想和解决了什么样的问题。以 Vue.js 为例。此次讲解围绕以下几个方面展开&#xff1a; MV* 框架模式Vue.js 的概述Vue MVVM 的实现Vue 与 React …...

    2024/4/21 13:30:59
  19. javascript校验多个输入框input的值是否相等(前段校验)

    function saveDict(){//没有选择类型列表if(document.Form1.keyword.value"jerrynew"){if(Trim(document.Form1.keywordname.value)""){alert("请输入类型名称");return false;}var allkeywords document.Form1.keyword;for(var i0;i<allkeyw…...

    2024/4/21 13:30:59
  20. 小程序input提交后如何清空输入框数据:小程序与Vue的数据绑定方式

    前言 一直觉得,小程序与Vue有着神似之风。这一点在我第一天开始接触小程序时就这么认为,或者说,任何前端相关的语言,不管是node.js、Vue、angular、小程序,都和js有着千丝万缕的联系。 而最让我“着迷”的,莫过于曾让我夜夜辗转反侧的“ 数据传递(数据绑定) ”了。趁着…...

    2024/4/21 13:30:57

最新文章

  1. LT6911UXB HDMI2.0 至四端口 MIPI DSI/CSI,带音频 龙迅方案

    1. 描述LT6911UXB 是一款高性能 HDMI2.0 至 MIPI DSI/CSI 转换器&#xff0c;适用于 VR、智能手机和显示应用。HDMI2.0 输入支持高达 6Gbps 的数据速率&#xff0c;可为4k60Hz视频提供足够的带宽。此外&#xff0c;数据解密还支持 HDCP2.2。对于 MIPI DSI / CSI 输出&#xff0…...

    2024/5/2 23:15:56
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组 题解

    VP比赛链接 : 数据加载中... - 蓝桥云课 1 . 九进制 转 十进制 直接模拟就好了 #include <iostream> using namespace std; int main() {// 请在此输入您的代码int x 22*92*81*9;cout << x << endl ;return 0; } 2 . 顺子日期 枚举出每个情况即可 : …...

    2024/4/30 1:59:34
  4. docker进行jenkins接口自动化测试持续集成实战

    文章目录 一、接口功能自动化测试项目源码讲解二、接口功能自动化测试运行环境配置1、下载jdk&#xff0c;maven&#xff0c;git&#xff0c;allure并配置对应的环境变量2、使用docker安装jenkins3、配置接口测试的运行时环境选择对应节点4、jenkins下载插件5、jenkins配置环境…...

    2024/5/1 13:12:35
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57