文章页正文上
这篇文章主要讲解了“怎么解决JavaScript相关的问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么解决JavaScript相关的问题”吧!一、如何在 window 对象上显式设置属性对于使用过 JavaScript 的开发者来说,对于 window.MyNamespace = window.MyNamespace || {}; 这行代码并不会陌生。为了避免开发过程中出现冲突,我们一般会为某些功能设置独立的命名空间。然而,在 TS 中对于 window.MyNamespace = window.MyNamespace || {}; 这行代码,TS 编译器会提示以下异常信息:以上异常信息是说在 Window & typeof globalThis 交叉类型上不存在MyNamespace 属性。那么如何解决这个问题呢?最简单的方式就是使用类型断言:虽然使用 any 大法可以解决上述问题,但更好的方式是扩展 lib.dom.d.ts 文件中的Window 接口来解决上述问题,具体方式如下:下面我们再来看一下 lib.dom.d.ts 文件中声明的 Window 接口:在上面我们声明了两个相同名称的 Window 接口,这时并不会造成冲突。TypeScript 会自动进行接口合并,即把双方的成员放到一个同名的接口中。二、如何为对象动态分配属性在 JavaScript 中,我们可以很容易地为对象动态分配属性,比如:以上代码在 JavaScript 中可以正常运行,但在 TypeScript 中,编译器会提示以下异常信息:{} 类型表示一个没有包含成员的对象,所以该类型没有包含 name 属性。为了解决这个问题,我们可以声明一个 LooseObject 类型:该类型使用 索引签名 的形式描述 LooseObject 类型可以接受 key 类型是字符串,值的类型是 any 类型的字段。有了 LooseObject 类型之后,我们就可以通过以下方式来解决上述问题:对于 LooseObject 类型来说,它的约束是很宽松的。在一些应用场景中,我们除了希望能支持动态的属性之外,也希望能够声明一些必选和可选的属性。比如对于一个表示开发者的 Developer 接口来说,我们希望它的 name 属性是必填,而 age 属性是可选的,此外还支持动态地设置字符串类型的属性。针对这个需求我们可以这样做:其实除了使用 索引签名 之外,我们也可以使用 TypeScript 内置的工具类型 Record来定义 Developer 接口:三、如何理解泛型中的 对于刚接触 TypeScript 泛型的读者来说,首次看到语法会感到陌生。其实它没有什么特别,就像传递参数一样,我们传递了我们想要用于特定函数调用的类型。参考上面的图片,当我们调用 identity(1) ,Number 类型就像参数 1 一样,它将在出现 T 的任何位置填充该类型。图中内部的 T 被称为类型变量,它是我们希望传递给 identity 函数的类型占位符,同时它被分配给 value 参数用来代替它的类型:此时 T 充当的是类型,而不是特定的 Number 类型。其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:K(Key):表示对象中的键类型;V(Value):表示对象中的值类型;E(Element):表示元素类型。其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。比如我们引入一个新的类型变量 U,用于扩展我们定义的 identity 函数:除了为类型变量显式设定值之外,一种更常见的做法是使编译器自动选择这些类型,从而使代码更简洁。我们可以完全省略尖括号,比如:对于上述代码,编译器足够聪明,能够知道我们的参数类型,并将它们赋值给 T 和 U,而不需要开发人员显式指定它们。四、如何理解装饰器的作用在 TypeScript 中装饰器分为类装饰器、属性装饰器、方法装饰器和参数装饰器四大类。装饰器的本质是一个函数,通过装饰器我们可以方便地定义与对象相关的元数据。比如在 ionic-native 项目中,它使用 Plugin 装饰器来定义 IonicNative 中 Device 插件的相关信息:在以上代码中 Plugin 函数被称为装饰器工厂,调用该函数之后会返回类装饰器,用于装饰 Device 类。Plugin 工厂函数的定义如下:通过观察 Plugin 工厂函数的方法签名,我们可以知道调用该函数之后会返回 ClassDecorator 类型的对象,其中 ClassDecorator 类型的声明如下所示:类装饰器顾名思义,就是用来装饰类的。它接收一个参数 —— target: TFunction,表示被装饰器的类。介绍完上述内容之后,我们来看另一个问题 @Plugin({…}) 中的 @ 符号有什么用?其实 @Plugin({…}) 中的 @ 符号只是语法糖,为什么说是语法糖呢?这里我们来看一下编译生成的 ES5 代码:通过生成的代码可知,@Plugin({…}) 和 @Injectable() 最终会被转换成普通的方法调用,它们的调用结果最终会以数组的形式作为参数传递给 __decorate 函数,而在 __decorate 函数内部会以 Device 类作为参数调用各自的类型装饰器,从而扩展对应的功能。此外,如果你有使用过 Angular,相信你对以下代码并不会陌生。在 Injectable 类装饰器修饰的 HttpService 类中,我们通过构造注入的方式注入了用于处理 HTTP 请求的 HttpClient 依赖对象。而通过 Inject 参数装饰器注入了API_URL 对应的对象,这种方式我们称之为依赖注入(Dependency Injection)。关于什么是依赖注入,在 TS 中如何实现依赖注入功能,出于篇幅考虑,这里阿宝哥就不继续展开了。感兴趣的小伙伴可以阅读 “了不起的 IoC 与 DI” 这篇文章。五、如何理解函数重载的作用5.1 可爱又可恨的联合类型由于 JavaScript 是一个动态语言,我们通常会使用不同类型的参数来调用同一个函数,该函数会根据不同的参数而返回不同的类型的调用结果:由于 TypeScript 是 JavaScript 的超集,因此以上的代码可以直接在 TypeScript 中使用,但当 TypeScript 编译器开启 noImplicitAny 的配置项时,以上代码会提示以下错误信息:该信息告诉我们参数 x 和参数 y 隐式具有 any 类型。为了解决这个问题,我们可以为参数设置一个类型。因为我们希望 add 函数同时支持 string 和 number 类型,因此我们可以定义一个 string | number 联合类型,同时我们为该联合类型取个别名:在定义完 Combinable 联合类型后,我们来更新一下 add 函数:为 add 函数的参数显式设置类型之后,之前错误的提示消息就消失了。那么此时的 add 函数就完美了么,我们来实际测试一下:在上面代码中,我们分别使用 ‘semlinker’ 和 ‘ kakuqo’ 这两个字符串作为参数调用 add 函数,并把调用结果保存到一个名为 result 的变量上,这时候我们想当然的认为此时 result 的变量的类型为 string,所以我们就可以正常调用字符串对象上的 split 方法。但这时 TypeScript 编译器又出现以下错误信息了:很明显 Combinable 和 number 类型的对象上并不存在 split 属性。问题又来了,那如何解决呢?这时我们就可以利用 TypeScript 提供的函数重载。5.2 函数重载函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。在以上代码中,我们为 add 函数提供了多个函数类型定义,从而实现函数的重载。在 TypeScript 中除了可以重载普通函数之外,我们还可以重载类中的成员方法。方法重载是指在同一个类中方法同名,参数不同(参数类型不同、参数个数不同或参数个数相同时参数的先后顺序不同),调用时根据实参的形式,选择与它匹配的方法执行操作的一种技术。所以类中成员方法满足重载的条件是:在同一个类中,方法名相同且参数列表不同。下面我们来举一个成员方法重载的例子:这里需要注意的是,当 TypeScript 编译器处理函数重载时,它会查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面。另外在 Calculator 类中,add(a: Combinable, b: Combinable){ } 并不是重载列表的一部分,因此对于 add 成员方法来说,我们只定义了四个重载方法。六、interfaces 与 type 之间有什么区别6.1 Objects/Functions接口和类型别名都可以用来描述对象的形状或函数签名:接口类型别名6.2 Other Types与接口类型不一样,类型别名可以用于一些其他类型,比如原始类型、联合类型和元组:6.3 Extend接口和类型别名都能够被扩展,但语法有所不同。此外,接口和类型别名不是互斥的。接口可以扩展类型别名,而反过来是不行的。Interface extends interfaceType alias extends type aliasInterface extends type aliasType alias extends interface6.4 Implements类可以以相同的方式实现接口或类型别名,但类不能实现使用类型别名定义的联合类型:6.5 Declaration merging与类型别名不同,接口可以定义多次,会被自免费云主机、域名动合并为单个接口。七、object, Object 和 {} 之间有什么区别7.1 object 类型object 类型是:TypeScript 2.2 引入的新类型,它用于表示非原始类型。7.2 Object 类型Object 类型:它是所有 Object 类的实例的类型,它由以下两个接口来定义:Object 接口定义了 Object.prototype 原型对象上的属性;ObjectConstructor 接口定义了 Object 类的属性。Object 类的所有实例都继承了 Object 接口中的所有属性。7.3 {} 类型{} 类型描述了一个没有成员的对象。当你试图访问这样一个对象的任意属性时,TypeScript 会产生一个编译时错误。但是,你仍然可以使用在 Object 类型上定义的所有属性和方法,这些属性和方法可通过 JavaScript 的原型链隐式地使用:八、数字枚举与字符串枚举之间有什么区别8.1 数字枚举在 JavaScript 中布尔类型的变量含有有限范围的值,即 true 和 false。而在 TypeScript 中利用枚举,你也可以自定义相似的类型:No 和 Yes 被称为枚举 NoYes 的成员。每个枚举成员都有一个 name 和一个 value。数字枚举成员值的默认类型是 number 类型。也就是说,每个成员的值都是一个数字:除了让 TypeScript 为我们指定枚举成员的值之外,我们还可以手动赋值:这种通过等号的显式赋值称为 initializer。如果枚举中某个成员的值使用显式方式赋值,但后续成员未显示赋值, TypeScript 会基于当前成员的值加 1 作为后续成员的值。8.2 字符串枚举除了数字枚举,我们还可以使用字符串作为枚举成员值:8.3 数字枚举 vs 字符串枚举数字枚举与字符串枚举有什么区别呢?这里我们来分别看一下数字枚举和字符串枚举编译的结果:数字枚举编译结果
相关推荐: php中魔术方法是什么
这篇文章主要介绍了php中魔术方法是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。类中的魔术方法PHP 魔术方法指的是在某些时刻会自动被调用的内置函数,它们以两个连续的下划线开头。类的构造函…
文章页内容下