12

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。

前言BB

这节标题超长了,难受箭头函数、set、map、proxy、symbol、reflect、generator

箭头函数

之前声明函数使用 function funName(arg1,...args){return args}
ES6 增加了新的声明函数方式 (arg1,...args)=>args

箭头函数的特点

  1. 匿名函数。通过上面例子可以看出,匿名函数没有名字。用法等价于之前的函数表达式
  2. 简洁[1,2,3,4,0,-1,-2,-3].map(v=>Math.sign(v))

    1. 入参的省略。如果只有一个入参,那么括号可以省略。没有入参、多个入参、rest 参数(...)、解构({status, response})都需要加括号。image.png
    2. 函数体的省略。如果没有花括号,那么只能传入一个表达式,表达式的结果会 return。如果有花括号,就需要显式的return,当然也可以不写默认 returnundefined
  3. this 指向

    1. 箭头函数的 this 对象指向定义时所在的对象,而不是使用时所在的对象。
    2. 箭头函数的 this 对象是不可变的。bind、call、apply、赋值等操作都无法改变。
  4. 不可以当做构造函数。因为上面的 this 问题。
  5. 没有 arguments
  6. 不能用作 Generator 函数。不可以使用 yield 命令

箭头函数的一些错误用法

  1. 不应该在声明对象的时候使用。因为这个时候里面的 this 指向定义时的环境。

    obj = {
      name: 'app',
      jumps: () => {
        console.log(this.app);
      }
    }

Set、WeakSet

Set 特点是值唯一有序。类似于数组,但是成员的值都是唯一的,没有重复的值,遍历的话是插入顺序。Array.from(new Set([1,2,3,1,1,2,3,1])) 依赖值唯一的特性实现去重。
image.png

Set 构造函数

new Set([iterable]); 使用可迭代的数组来创建。

  1. new Set([1,2,3,4,2,1,1,1,12,3])
  2. new Set('123123')
  3. new Set(['123123',1,2,3])
  4. new Set(document.querySelectorAll('a'))
  5. image.png

属性和方法

  1. Set.prototype.size:返回总数,等价于(Array.prototype.length
  2. Set.prototype.add(value):添加,返回当前对象可链式操作。
  3. Set.prototype.delete(value):删除,返回一个布尔值,表示删除是否成功。
  4. Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
  5. Set.prototype.clear():清除所有成员,没有返回值。
  6. Set.prototype.keys():返回键(key)遍历器 Iterator
  7. Set.prototype.values():返回值(value)遍历器 Iterator
  8. Set.prototype.entries():返回键值对([key, value])遍历器 Iterator
  9. Set.prototype.forEach():使用回调函数(value, key, this)=>遍历每个成员

WeakSet

  1. 只能存放对象的集合
  2. 弱引用方式保存。弱引用就是可以被垃圾回收(如果放入数组,那么你不知道什么实际是可以被回收的,因为数组是强引用),
  3. 没有总数
  4. 不可枚举

image.png

Map、WeakMap

Map 其实 和 JavaScript 的对象(Object)一样,本质上是键值对的集合Hash 结构)。
但是传统上 Object 只能用字符串当作键,在使用上有很大的局限性。

Map 的数据结构也可以理解为对象,是键值对的集合
但是 key 的范围不限于字符串,可以是其他类型(包括对象)都可以当作键。是一种更完善的 Hash 结构实现。

userinfo = {name: 'lilnong.top'}
map = new Map()
map.set(userinfo, {age: 123})
map.has(userinfo)
map.get(userinfo)

image.png

构造函数

map = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);
map.forEach(
  (key, value) => console.log(key, value)
);

image.png

属性和方法

  1. size 返回记录总数。(Object 可是没有这个属性的哟)
  2. set(key, value) 赋值操作
  3. get(key) 取值操作
  4. has(key) 判断当前 key 是否存在于集合中。
  5. delete(key) 删除操作。返回 true,表明删除成功。返回 false 表明删除失败,比如你删除了一个不存在的 key
  6. clear() 清空所有。
  7. 遍历方法。(插入顺序)

    • Map.prototype.keys():返回键名的遍历器。
    • Map.prototype.values():返回键值的遍历器。
    • Map.prototype.entries():返回所有成员的遍历器。
    • Map.prototype.forEach():遍历 Map 的所有成员。

weakMap

WeakMap 只接受对象作为键名null除外),不接受其他类型的值作为键名。
WeakMap键名所指向的对象,不计入垃圾回收机制
因为是弱引用,所以没有遍历方法、没有 size,不支持 clear()。

proxy

Proxy 用于修改操作(getset)的默认行为
说到这里是不是想到了 Object.defineProperty ?其实也和 JavaBean 那种操作 getter、setter 差不多。

等同于在语言层面做出修改,对编程语言进行编程,属于一种“元编程”(meta programming )。

Proxy 可以理解成,在目标对象之前架设个代理,对该对象的访问,都必须先通过代理,这样可以对外界的访问进行过滤和改写

vue 2.x 基于 Object.defineProperty 来实现的,但是会有一些场景监测不到(array.lenth、$set)。
vue 3.x 使用了 Proxy 来做了,可以监测到更多的场景。当然,基于兼容性考虑还是可以回退到 Object.defineProperty

var obj = new Proxy({}, {
  get: function (target, propKey, receiver) {
    console.log(`getting ${propKey}!`);
    return Reflect.get(target, propKey, receiver);
  },
  set: function (target, propKey, value, receiver) {
    console.log(`setting ${propKey}!`);
    return Reflect.set(target, propKey, value, receiver);
  }
});

从下面这个例子我们可以看到,我们可以只设置读的代理,不管读取什么都返回 www.lilnong.top。然后我们没有设置写的代理,值已经被真正的写入了。
image.png

构造函数

var proxy = new Proxy(target, handler);

支持代理的操作

key arguments 触发时机 demo
get (target, propKey, receiver) 读取属性时触发 proxy.titleproxy['title']
set (target, propKey, value, receiver) 设置属性时触发 proxy.title = 'lilnong.top'proxy['title'] = 'lilnong.top'
has (target, propKey) in 时触发 title in proxy
deleteProperty (target, propKey) delete时触发 delete proxy.title
ownKeys (target) 获取 key 合集时触发 Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in
getOwnPropertyDescriptor (target, propKey) 读取属性时触发 Object.getOwnPropertyDescriptor(proxy, propKey)
defineProperty (target, propKey, propDesc) 读取属性时触发 Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs)
preventExtensions (target) 读取属性时触发 Object.preventExtensions(proxy)
getPrototypeOf (target) 读取属性时触发 Object.getPrototypeOf(proxy)
isExtensible (target) 读取属性时触发 Object.isExtensible(proxy)
setPrototypeOf (target, proto) 读取属性时触发 Object.setPrototypeOf(proxy, proto)
apply (target, object, args) proxy 为函数被调用时触发 proxy(...args)proxy.call(object, ...args)proxy.apply(...)
construct (target, object, args) proxy 为实例被New 时触发 new proxy(...args))

注意事项

  1. 严格模式下,set 代理执行完毕应该返回 true,否则会报错。

symbol

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过 Symbol 函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

s = Symbol();
typeof s // "symbol"

s1 = Symbol('foo');
s1 // Symbol(foo)
s1.toString() // "Symbol(foo)"

s2 = Symbol('bar');
s2 // Symbol(bar)
s2.toString() // "Symbol(bar)"

reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。

Reflect对象的设计目的有这样几个。

(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在ObjectReflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。

(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
....
.... https://es6.ruanyifeng.com/#d...

generator

Generator 函数也是 ES6 提供的一种异步编程解决方案。现在一般都是 Promise 或者 await/async,就不展开说了。

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍 Generator 函数的语法和 API,它的异步编程应用请看《Generator 函数的异步应用》一章。

Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

....
.... https://es6.ruanyifeng.com/#d...

微信公众号:前端linong

clipboard.png

参考文献

  1. 前端培训目录、前端培训规划、前端培训计划
  2. https://developer.mozilla.org/zh-CN/
  3. http://es6.ruanyifeng.com/

linong
29.2k 声望9.5k 粉丝

Read-Search-Ask