乐趣区

关于前端:JS与TS那些奇奇怪怪的符号你知道有那些

结尾

随着 ES6+Typescript 的席卷到来,Javascript 的世界里曾经是符号满天飞的时代了,感觉当初是略微不努力学习,就跟上不了,看他人写的代码如同看天书般。

看了最近我的项目中的一个函数,Em…… 直呼,好家伙!!!

为了更有效率的搬砖,本章目标就是摸索那些可能让你看不懂的神奇符号和写法,当然,如果你有遇到更令人惊奇的操作,欢送评论区留言抢座(没有其实也能够抢 ^〇^)。

(所有为了更好的摸鱼,加油!!!)

JS

解构赋值(写法)

变量的解构赋值ES6 带来的一个新个性,这玩意是个好货色,用起来那是相当的爽,总之就很 Nice!!!置信各位用得都游刃有余了,这里就不再解说它的根本用法班门弄斧,咱们次要来看看上面这些有意思的写法,看看能不能给你一些新的领会,学到一点新技巧。

console 大法

调试代码是个很要害的环节,尽管当初调试的形式目不暇接,然而用来用去,还是 console 大法最简略、实用。然而不晓得你会不会有那么一瞬间感觉写 console.log() 很繁琐呢?那么你能够略微尝试这样子应用看看:

({log: window.log, error: window.error, warn: window.warn} = console);
log('一般');
warn('揭示');
error('危险');

好不好用自行品味食用 ⊙ω⊙ 逃~

数组是非凡的对象

数组用对象来解构,这是有多闲啊?(T_T)

var arr = ['L', 'O', 'V', 'E'];
var {0: l, 1: o, 2: v, 3: e} = arr;
console.log(l, o, v, e); // L O V E

取数组第一项和最初一项

var arr = ['first', 'second', 'last'];
var {[0]: first, [arr.length - 1]: last} = arr;
console.log(first, last); // first last

这叫啥?这叫不走寻常路!!!等等,这里细品咱们能发现一点小常识,比方:

var arr = ['first', 'second', 'last'];
var {[arr.length <= 1 ? 0 : arr.length - 1]: last} = arr;
console.log(last); // last

Em… 这其中能用表达式,这能做的事件就多了吧?嘿嘿!!!

字符串也解构

var [a1, a2, a3, a4] = 'YYDS';
console.log(a1, a2, a3, a4); // Y Y D S
var [lastname, ...name] = '橙某人前端';
console.log(lastname, name); // 橙 某, 人, 前, 端

尽管应用场景可能少,然而说不定哪天能有用上呢。

变量值替换

var x = 1;
var y = 2;
[x, y] = [y, x];

这就没啥好说的,反正就再也不必定义长期变量了。

默认值之默认值

这是我实在在我的项目中看到的,大抵简化下来就是这样子,各位看官大老爷就细细品。

var upperValue = ''; // 某个接口的值
let {value: newValue = upperValue || '上一个接口没有值'} = {value: undefined}; // 另一接口可能会返回的值
console.log(newValue)

莎士比亚说过:There are a thousand Hamlets in a thousand people’s eyes.(一千个观众眼中有一千个哈姆雷特)

我想写代码也是如此吧。。。(⊙o⊙)

模板字符串之标签模板(fn“)

console.log` 橙某人 `; // ['橙某人']
// 等同于
console.log(['橙某人']); // ['橙某人']

这其实是函数的一种非凡调用模式,尽管可能这辈子你都未必会用到,但也不能排除其他人会怎么写,知己知彼,更多用法请点击 文档。

数值分隔符(_)

ES6 中容许的数值应用下划线(_)作为分隔符,有了这个货色就再也不必放心本人数零数晕圈了。

let num1 = 137_9083_7051; // 手机号码
console.log(num1); // 13790837050
let num2 = 1_000_000_000; // 大额数字
console.log(num2); // 1000000000
let num3 = 1.000_000_000_1; // 多位小数
console.log(num3); // 1.0000000001

rest 参数(…)

rest 参数(模式为 ... 变量名),用于接管函数的多余参数,该参数以数组的模式寄存多余的参数。

function fn(val, ...vals) {console.log(val, vals);
}
fn(1, 2, 3, 4, 5); // 1 [2, 3, 4, 5]

它更好的替换了 arguments 参数,arguments 参数的不透明性、暗藏性以及它是以伪数组模式存在的,不能间接应用数组相干办法等这些因素都给人带来了较多麻烦;当然,更重要的是它能服务于箭头函数,咱们晓得箭头函数外部是没有所谓的 this 的,更加不会有 arguments 参数。

它也不仅仅只能放在函数参数上应用,也能够配合解构赋值一起游玩:

var [a, ...rest] = [1, 2, 3, 4];
console.log(a); // 1
console.log(rest); // [2, 3, 4]

rest 参数应用留神点:

  • rest 参数只能放在所有参数的最初一位,否则会报错。
  • rest 参数不计入函数的 length 属性。

    (function(a) {}).length  // 1
    (function(...a) {}).length  // 0
    (function(a, ...b) {}).length  // 1

扩大运算符(…)

扩大运算符 也是个好玩意,但要留神它和 rest 参数是不一样的,不要搞混了哦。

console.log(...[1, 2, 3]); // 1 2 3
console.log({...{a: 1, b: 2, c: 3}}); // {a: 1, b: 2, c: 3}
console.log([...document.querySelectorAll('div')]); // [div, div, div]
console.log(...new Set([1, 2, 3])); // 1 2 3
console.log(...new Map([['name', '橙某人'], ['age', 18]])); // ["name", "橙某人"] ["age", 18]

扩大运算符算是比拟好了解、好应用的,可读性也十分的棒,然而即便这样,也架不住各位大神千奇百怪的骚操作,很容易就写出令人揪脑袋的写法。

console.log({...['橙', '某', '人']}); // {0: "橙", 1: "某", 2: "人"}
console.log({...'橙某人'}); // {0: "橙", 1: "某", 2: "人"}
function fn(...[a, b, c]) {console.log(a, b, c);
}
fn(1, 2, 3); // 1, 2, 3
fn(1, 2, 3, 4); // 1, 2, 3  这里只是扩大运算符,可不是 rest 参数哦

指数运算符(**)

指数运算符(** 这玩意和 Math.pow() 是一样的,不过就是写法变简洁了。

console.log(2 ** 2); // 2*2=4
console.log(2 ** 3); // 2*2*2=8
等同于
console.log(Math.pow(2, 2)); // 4
console.log(Math.pow(2, 3)); // 3

不过要略微留神一下,这家伙是从左边开始计算的:

console.log(2 ** 3 ** 2); // 2 ** (3*3) = 2 ** 9 = 2*2... = 512

链判断运算符(.?)

不晓得你是否有写过这样的语句:

var response = {}
console.log(response && response.data && response.data.name); // undefined

咱们为了在读取对象外部的某个属性控制台不报错,往往咱们须要判断一下,属性的下层对象是否存在,这样写齐全没有问题,然而一旦读取属性数量多了,写起来就繁琐了。这个时候它来了,ES2020 带着链判断运算符向咱们走来了。

console.log(response?.data?.name); // undefined

是不是就很简洁,真是太棒了,有没有(-^〇^-)?

链判断运算符原理是判断左侧的对象是否为 nullundefined。如果是的,就不再往下运算,而是返回undefined

空值判断运算符(??)

有时咱们须要判断一个变量是否为空,如果为空,则设置默认值,否则则为原值。那咱们大略会怎么写:

// var value = '';
// var value = 0;
// var value = undefined;
// var value = null;

console.log(value ? value : 'value 为空值'); // '' 和 0 也会算成空
console.log(value !== undefined || value !== null ? value : 'value 为空值'); // 写法比拟繁琐

ES2020 引入了一个新的 Null 判断运算符 ??,只有运算符左侧的值为 nullundefined 时,才会返回右侧的值,否则返回左侧的值。

console.log(value ?? 'value 没值');

globalThis

JavaScript 能够运行在不同环境中,如浏览器、WorkerNode 等等,只管都是 JS,语法根本也都雷同,但它们却存在不同的全局对象。

  • 浏览器全局对象是 window
  • Web Worker 全局对象是 self
  • Node 全局对象是 global

对于这种状况,为了在不同环境中都应用对立的全局对象,ES2020 规范引入了 globalThis。

// browser
console.log(globalThis);    // => Window {...}

// node
console.log(globalThis);    // => Object [global] {...}

// web worker
console.log(globalThis);    // => DedicatedWorkerGlobalScope {...}

TS

理解完 JS 中的各种符号,接下来就是 TS 中的一些符号了,不过 TS 奇怪的符号不多,大多也是和 JS 一样或者演变过去的,上面咱们就持续来观摩观摩。

(为了展现报错成果,上面代码根本都会应用截图来代替,应该也不必代码了吧,都是很简略的代码,目标就是为了阐明每个符号的作用和意义)

非空断言操作符(!)

先来介绍第一个符号 非空断言操作符,然而要展现该符号的作用,咱们还须要对 tsconfig.json 进行一些批改,这个文件是 TS 的配置文件,个别的 TS 我的项目你都能在根目录下看到它,你也能够通过 npx tsc --init 的命令来被动生成它。

而后咱们须要把文件的 "strictNullChecks": true 配置项关上,这是为什么呢?次要是因为 nullundefinedTS 中的根底类型,别离具备值 nullundefined,默认状况下它们是所有类型的子类型,即能够赋值给任意类型;当咱们关上该选项后,它们就不能随便赋值给其余类型了,只能赋值给自身类型。

配置项未关上之前:

配置项关上之后:

如上图,nickname 有可能是 undefined 所以不能间接赋值给 realname,否则就会报错。但如果通过一些操作使得 nickname 曾经是确定有值的,例如这样:

咱们要怎么通知 TS 呢?让它不报错呢?这个时候就能够用到 非空断言操作符 了。

这样子就行了,应该也比较简单好了解吧。。。

可选属性(?:)

可选属性 这能够说是 接口(interface)身上的一个性质,接口一个非常灵活的概念,咱们平时能够用它来限度一个对象,例如:

图中定义了一个人类的接口,别离有姓名、年龄和喜好,它能够用来限度对象,然而有时候,有些对象没有喜好(好吧,一个人没有喜好那就太惨了︶︿︶)这一项,就会像图中的小明一样,只能报错,这要怎么办?

这个时候就能够用到接口的可选属性了,如:

链判断运算符(.?)

该运算符与 JS 版本中的可选链运算符成果是一样的,其实就是 TS 版本的实现而已。同样是判断左侧的对象是否为 nullundefined。如果是的,就不再往下运算,而是返回undefined

var obj: any = undefined;
if(obj?.name) {}
// 等同于
if(obj && obj.name) {}

这样写的目标是避免控制台报错,因为 obj 类型可能咱们是不确定的。

空值判断运算符(??)

该运算符与 JS 版本中的空值判断运算符成果是一样的,也是 TS 版本的实现而已。同样是只有运算符左侧的值为 nullundefined 时,才会返回右侧的值,否则返回左侧的值

var value: string | undefined | null = '';
console.log(value ?? 'value 为空值'); // 当 value 为空值时,取默认值

穿插类型运算符(&)

这玩意就好比运算符 &&),但它是作用于类型定义上的,它也只有单个符号(&)。应用穿插类型运算符能够将多种类型叠加在一起,造成一个新类型,新类型会蕴含所需的所有类型的个性,缺一不可。

它也能够用在接口上:

这就没有什么好说的了,当然,它在应用上也有须要留神的中央,例如这样子:

当穿插的两个类型都具备雷同属性,但属性类型定义不一样时,所失去的类型就会变成 never 类型。这是因为 string & number 这种类型显然是不存在的,所以它会变成 never 类型。

联结类型分隔符(|)

这个也是很简略啦,就和 或者||)作用差不多,也是老样子它只有单个符号(|),可不要写错了哦。

这些例子很简略,就不多说了。然而,很多时候应用联结类型分隔符的时候,会遇到一类问题,比方:

图中,不论咱们读取 name 还是 ageTS 都是不容许,TS 无奈确定 obj 身上是否存在这两个属性;要解决这类问题的形式有很多,咱们把这些形式统称为“类型爱护”,其实简略来了解就是先确定好图中 obj 对象的类型,再进行后续操作。

(这里就顺便略微提了一下“类型爱护”,更多内容能够再去自行查阅相干材料,咱们这里就点到即止了)

类型断言

类型断言 这玩意有点像是 类型转换 的象征,但也不算是,毕竟它也没去转换,只是 ” 蒙骗 ” 过了 TS 的类型查看。它有两种写法,上面咱们别离来瞧瞧。

TS 不容许咱们把一个未知的类型或者其余类型,赋值给一个明确类型的变量,然而有时咱们无可奈何就是须要那么干,像图中的状况这要怎么做呢?

as 语法

<> 语法

是不是也很好了解?

装璜器(@xxx)

什么是装璜器?记住它,实质就是调用一个函数,就是一个语法糖,没什么大不了的。它的作用是容许向一个现有的对象增加新的性能,同时又不扭转其构造。

要想应用它,你还是得去 tsconfig.json 中开启它的相干配置项才行,把 "experimentalDecorators": true"emitDecoratorMetadata": true 关上即可。

话不多说,咱们先写一个小例子来观摩观摩:

function classFn(target: any) {console.log('类装璜器:', target);
}

@classFn
class Person{constructor() {console.log('实例化');
  }
}

var p = new Person(); 

这是执行后控制台打印的后果:

呃 … 是不是也挺乏味,学过 Java 的小伙伴可能就比拟熟了,这和 Java 的装璜器模式差不多。。。(⊙o⊙)

装璜器也能传递参数,而且是程序执行的:

function classFn(target: any) {console.log('类装璜器:', target);
  return function(params: any) {console.log('自定义类装璜器参数:', params)
  }
}

@classFn('橙某人 -1')
@classFn('小明同学 -2')
class Person{constructor() {console.log('实例化');
  }
}

var p = new Person();

装璜器不仅仅只有类装璜器,它有下来分类:

  • 类装璜器(Class decorators)
  • 属性装璜器(Property decorators)
  • 办法装璜器(Method decorators)
  • 参数装璜器(Parameter decorators)

Em…… 这玩意波及的内容如同不少,这里就不再一一去介绍了,不想内容太多你们看着烦,本章宗旨是带你理解各种符号,免得接手我的项目时齐全抓瞎,大抵晓得一下语法就差不多啦。(哈哈哈,终于水完了 …… 逃,大快人心)


至此,本篇文章就写完啦,撒花撒花。

心愿本文对你有所帮忙,如有任何疑难,期待你的留言哦。
老样子,点赞 + 评论 = 你会了,珍藏 = 你精通了。
原文首发于掘金,欢送来踩。

退出移动版