2

现代JavaScript的高级概念和用法

 1 year ago
source link: https://www.fly63.com/article/detial/11846
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

更新日期: 2022-07-03阅读: 35标签: 代码分享

扫一扫分享

JavaScript语言不断发布一些新特性,感觉要上天的节奏啊。本文搜集整理了一些它的高级概念和用法,来看看你是否都了解?代码这样写是不是更优雅了?

闭包是Javascript中的一项重要技术,内部函数始终可以访问其外部函数的变量和参数,即使在外部函数返回后也是如此。我们使用闭包来保护我们不想向外部范围公开的数据

//
<button onclick=”increaseCounter()”>Increase Counter</button>

//1. 全局变量,变量会被意外修改
let counter = 0;
function increaseCounter() {
  counter++;
}
//2. 局部变量,每次调用都重置为0
function increaseCounter() {
  let counter = 0;
  counter++;
}
//3. 闭包函数,符合要求
const increaseCounter = (function() {
  let counter = 0;
 
  return function() {
    counter = counter + 1;
    console.log(counter);
  };
})();

2. 函数绑定

在上下文丢失时,this将无法被确定,可以通过函数绑定解决。

// 1. 与预期不符,得到undefined
let book = {
  title: ‘Learn JavaScript’,
  printTitle() {
    console.log(`Book’s title: ${this.title}`);
  }
}
setTimeout(book.printTitle, 1000); // Book’s title: undefined

// 2. 利用函数绑定,符合预期
let book = {
  title: ‘Learn JavaScript’,
  printTitle() {
    console.log(`Book’s title: ${this.title}`);
  }
}
let printTitle = book.printTitle.bind(book);
setTimeout(printTitle, 1000); // Book’s title: JavaScript

3.使用命名空间

使用命名空间可以防止代码冲突。

// 1. move、jump函数在animal命名空间下,需要通过animal.move()来调用
let animal = {
  move: () => {
    console.log(‘Move!’);
  },
  jump: () => {
    consle.log(‘Jump!’);
  }
};

// 2. 真实项目中,可能会按如下方式使用命名空间
if (typeof APP === "undefined") {
  APP = {};
  APP.ANIMAL = {};
}
APP.ANIMAL.move = () => {
  console.log(‘Move’);
};
APP.ANIMAL.jump = () => {
  console.log(‘Jump’);
};
APP.ANIMAL.move(); // Move
APP.ANIMAL.jump(); // Jump

4. 判断属性是否存在

使用in关键字可以判断对象中是否存在某个属性。

const person = {
  id: "123",
  name: "张三"
}

console.debug("id" in person) //true
console.debug("age" in person) //false

5. 解构赋值

利用解构赋值表达式,可以将属性、值从对象、数组中取出,赋值给其它变量,非常方便。

const { address: addressLine } = { address: "长安街20号", postcode: "518118" };
console.warn(addressLine); // 长安街20号
const [first, second] = [1, 2, 3, 4]
console.warn(first, second) // 1 2

//动态解构
const extractKey = "postcode"
const { [extractKey]: youbian } = { address: "长安街20号", postcode: "518118" };
console.log(youbian) //518118

6.遍历对象属性

使用Object.entries可以遍历对象的属性和值。

const data = { address: "长安街20号", postcode: "518118" };
Object.entries(data).forEach(([key,value]) => {
  if (["address", "postcode"].includes(key)) {
    console.log('key:', key , 'value:', value)
  }
})
//输出结果如下
key: address value: 长安街20号
key: postcode value: 518118

7. 过滤数组

利用数组的filter、some对数组进行筛选。

const fruits = ["apple", null, "mongo", undefined, ""]
const filted = fruits.filter(Boolean)
console.debug(filted) //(2) ["apple", "mongo"]

const any = fruits.some(Boolean)
console.log(any) //true

8. 消除重复值

const fruits = ["apple", null, "mongo", "apple", ""]
const uniqued = [...new Set(fruits)]
console.debug(uniqued) //(4) ["apple", null, "mongo", ""]

9. 判断是否数组

利用Array.isArray,而不是typeof判断。

const fruits = ["apple", null, "mongo", "apple", ""]
console.debug(typeof fruits) //object
console.error(Array.isArray(fruits)) //true

10. 转换数字和字符串

const text = "12345"
console.debug("text:", +text, "typeof:", typeof(+text)) //text:12345 typeof:number
const num = 123456
console.debug("number:", num+"", "typeof:", typeof(num+"")) //number:123456 typeof:string

11. 转换为boolean

利用!!运算符可以将其它类型转换为Boolean类型。

console.log(!!null, typeof(!!null)) //false, boolean
console.log(!!"", typeof(!!"")) //false, boolean
console.log(!!undefined, typeof(!!undefined)) //false, boolean
console.log(!!null, typeof(!!null)) //false, boolean
console.log(!!true, typeof(!!true)) //true, boolean
console.log(!!false, typeof(!!false)) //false, boolean
console.log(!!{id:"", name:""}, typeof(!!{id:"", name:""})) //true, boolean

12. 可选链

可选链 ?. 是一种访问嵌套对象属性的安全的方式,可避免在对象或属性不可用时抛出异常。由于JavaScript不是类型化语言,该特性还是很有用。

//未使用可选链接,将抛出异常
const contactInfos = { address: "长安街20号" };
console.warn(contactInfos.user.phoneNumber)
// 以上语句将报错:Cannot read properties of undefined (reading 'phoneNumber')


//使用可选链接,将打印undefined
const contactInfos = { address: "长安街20号" };
console.warn(contactInfos.user?.phoneNumber) // undefined

13. 合并运算符

合并运算符的写法为两个问号 ??,对于该运算符连接的两个参数,如果第一个参数不是 null,也不是undefined,则返回第一个参数,否则返回第二个参数。

const contactInfos = { address: "长安街20号" };
console.warn(contactInfos.user?.phoneNumber ?? "") // ""

const contactInfos = { address: "长安街20号", addressNumber: 0 };
console.warn(contactInfos.addressNumber || undefined) // undefined
console.warn(contactInfos.addressNumber ?? undefined) // 0

14. 有条件地添加属性

使用...扩展语法,可以仅当某个条件成立时,才为对象添加某个属性。

const moreInfos = { info: "请开车前往." }
return {
  address: "长安街20号",
  postcode: "518118",
  ...(moreInfos !== undefined && { moreInfos }) //仅当moreInfos不是undefined时,才添加moreInfos属性
}

15. 异步调用异常捕获

以下写法让处理异步调用异常的代码变得更为简洁。

const results = await getPosts().catch((err) => {
  return {
    type: "error",
    message: err.message
  }
});
console.warn(results) // { type: "error", message: "cannot get posts from this endpoint" }

16. 弱引用Map

Weakmap不同于Map,它的键必须是引用对象,不能是基础类型,如果没有对该键对象引用时,该对象将被从Map和内存中移除。

const videoSegments = new WeakMap()
let options = { id: "1234", timeStart: 1653831957378, size: 10000 }
const segment = { data: new Uint8Array(200) }
videoSegments.set(options, segment)
console.warn(videoSegments.get(options)) // { data: new Uint8Array(200) }

//以下当options被赋值为null后,该对象将被移除和回收
options = null
console.warn(videoSegments.has(options)) // false, the `options` key object is deleted from the WeakMap

17. 反射

Reflect是一个全局对象,它为元编程提供了一些有用的静态方法。

const person = { 
  name: 'Bob', 
  [Symbol('email')]: '[email protected]' 
};

Reflect.get(person, 'name'); // = Bob
Reflect.has(person, 'email'); // = true
Reflect.has(person, 'phone'); // = false
Reflect.getPrototypeOf(person); // = { constructor ... }
Reflect.getOwnPropertyDescriptor( person, 'name'); // = { value: 'Bob', writable: true, enumerable: true, configurable: true }
Reflect.ownKeys(person); // name, Symbol(email)

Reflect.defineProperty(person, 'phone', { writable: true });
Reflect.has(person, 'phone'); // = true
Reflect.set(person, 'phone', '123456789');

Reflect.deleteProperty(person, 'phone');
Reflect.has(person, 'phone'); // = false

18. 柯里化

柯里化(Currying)是一种关于函数的高阶技术,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。柯里化不会调用函数,它只是对函数进行转换。

// 完成银行转账交易函数,余额+转入金额-费用
const transaction = (fee, balance, amount) => (
  balance + amout - fee;
);

// 简单实现的柯里化函数
const curry = (fn, ...args) => (
  (..._arg) => (
    fn(...args, ..._arg)
  )
);

// 复用了transaction函数的免交易费函数
const freeTransaction = curry(transaction, 0);

freeTransaction(10, 90); // = 100

19. 组合

组合是一种技术,其中一个函数的结果被传递到下一个函数,该函数被传递到下一个函数,依此类推......直到执行最终函数并计算出一些结果。函数组合可以由任意数量的函数组成。

//f 和 g 都是函数,x 是在它们之间通过“管道”传输的值
var compose = function(f,g) {
  return function(x) {
    return f(g(x));
  };
};

var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };

var shout = compose(exclaim, toUpperCase);
shout("send in the clowns"); //=> "SEND IN THE CLOWNS!"
// 组合函数
const compose = (...fns) => x => fns.reduce((y, f) => f(y), x); 
// 原函数
const addFee = amount => amount + 2;
const addDiscount = amount => amount - 5;
// 函数组合
const composition = compose(addFee, addDiscount)(100);
console.log(composition) //97

链接: https://www.fly63.com/article/detial/11846


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK