Symbol原理

用来生成一个独一无二的值

Symbol是ES6规范中的一种原生数据类型,也就是说所有支持ES6规范的JavaScript都应该实现Symbol这一基本数据类型

那么对于V8而言,就应该在底层实现Symbol对象

src/builtins/builtins-symbol.cc 17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ES #sec-symbol-constructor
BUILTIN(SymbolConstructor) {
HandleScope scope(isolate);
if (!args.new_target()->IsUndefined(isolate)) { // [[Construct]]
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotConstructor,
isolate->factory()->Symbol_string()));
}
// [[Call]]
Handle<Symbol> result = isolate->factory()->NewSymbol();
Handle<Object> description = args.atOrUndefined(isolate, 1);
if (!description->IsUndefined(isolate)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, description,
Object::ToString(isolate, description));
result->set_description(String::cast(*description));
}
return *result;
}

我们可以看到这个最终的函数返回结果是 result,

result = isolate -> factory() -> NewSymbol();

src/torque/earley-parser.h 437

1
2
3
4
5
6
Symbol* NewSymbol(std::initializer_list<Rule> rules = {}) {
auto symbol = std::make_unique<Symbol>(rules);
Symbol* result = symbol.get();
generated_symbols_.push_back(std::move(symbol));
return result;
}

可以看出Symbol 是从 make_unique 函数中创建的

参考 https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
namespace detail {
template<class>
constexpr bool is_unbounded_array_v = false;
template<class T>
constexpr bool is_unbounded_array_v<T[]> = true;

template<class>
constexpr bool is_bounded_array_v = false;
template<class T, std::size_t N>
constexpr bool is_bounded_array_v<T[N]> = true;
} // namespace detail

template<class T, class... Args>
std::enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>>
make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template<class T>
std::enable_if_t<detail::is_unbounded_array_v<T>, std::unique_ptr<T>>
make_unique(std::size_t n)
{
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]());
}

template<class T, class... Args>
std::enable_if_t<detail::is_bounded_array_v<T>> make_unique(Args&&...) = delete;

可以看出

make_unique 是从unique_ptr中派生出来的

参考 https://en.cppreference.com/w/cpp/memory/unique_ptr

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope.

也就是说unique_ptr是一个对象.

看一下Symbol在pollyfill中的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
function Symbol(description) {
if (!(this instanceof Symbol)) return new Symbol(description, secret);
if (this instanceof Symbol && arguments[1] !== secret) throw TypeError();

var descString = description === undefined ? undefined : String(description);

set_internal(this, '[[SymbolData]]', unique(128));
set_internal(this, '[[Description]]', descString);

symbolMap[this] = this;
return this;
}

可以看出,就是一个构造函数, 只是这个函数在使用的时候有点特殊,return 了一个Symbol自身的实例,也就是一个对象.

问题一: 那么一个对象,是不能在对象中作为key来使用的

1
2
3
4
5
6
7
Object.defineProperty(Symbol.prototype, 'toString', {
value: function toString() {
var s = strict(this);
var desc = s['[[Description]]'];
return 'Symbol(' + (desc === undefined ? '' : desc) + s['[[SymbolData]]'] + ')';
},
configurable: true, writeable: true, enumerable: false });

在一下返回值,'Symbol(' + (desc === undefined ? '' : desc) + s['[[SymbolData]]'] + ')';
这个只是一个以Symbol字符串description开头的字符串, 按道理是应该没有后面的s['[[SymbolData]]'],为了保持唯一性就加上了这个,唯一标示.

问题二, Object.getPropertyNames 是不是会打印出 Symbol,答案 是 ,所以需要改写 getPropertyNames

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
define(
Object, 'getOwnPropertyNames',
function getOwnPropertyNames(o) {
if (Object.prototype.toString.call(o) === '[object Window]') {
// Workaround for cross-realm calling by IE itself.
// https://github.com/inexorabletash/polyfill/issues/96
try {
return $getOwnPropertyNames(o).filter(isStringKey);
} catch (_) {
return $window_names.slice();
}
}
return $getOwnPropertyNames(o).filter(isStringKey);
}, !nativeSymbols);

// 19.1.2.8 Object.getOwnPropertySymbols ( O )
define(
Object, 'getOwnPropertySymbols',
function getOwnPropertySymbols(o) {
return $getOwnPropertyNames(o).filter(symbolForKey).map(symbolForKey);
}, !nativeSymbols);

问题三 JSON在序列化的时候,不能序列化Symbol,但是目前而言,它只是一个String,也就是说会被序列化.

没有办法, JSON.stringify 后 会变成 一个随机字符串

问题四 typeof symbol 怎么被改写

没有办法, typeof 是一个不能被改写的操作, 如此依赖 typeof symbol === ‘object’

Symbol 不是能是一个构造函数,它没有 prototype,

同时 symbol实例是没有__proto__属性的,因此 instanceof 不能被修改

Map and WeekMap and 对象

区别

  1. Map可以接受任意类型的值为键,Object只支持字符串,weekMap只支持对象

  2. 垃圾回收Map不会自动GC(垃圾回收),除非手动删除,或清空Map对象,WeekMap的键,当时最后一个引用的时候会自动回收

IOC 控制反转()

控制反转 (inversion of control, 缩写 IOC), 是一种设计原则, 顾名思义,他是用于面向对象设计中的各种控件.

可以降低代码的耦合度, 使程序模块化,易于扩展, IOC意味着将依赖模块和被依赖模块都交给容器去管理,当我们使用模块的时候,由容器动态的将它的依赖关系注入到模块中,

正对上面的问题, 往往需要IOC模式, 一般使用DI(Dependency Injection) 依赖注入的方式实现.

以订单为例子, 先看美誉依赖注入的情况.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 订单model类, models/order.js
class Order {
constructor() {}

insert() {
return true;
}
}

// 订单类 controllers/OrderController.js
const Order = require("./Order.js");

class OrderController {
constructor() {
this.order = new Order();
}

createOrder(...args) {
this.order.insert(...args);
}
}

// router/index.js
const OrderController = require("./OrderController.js");
const orderController = new OrderController();

上面是没有依赖注入的情况, OrderController 类耦合了Order类, 在使用前必须require 引入 Order类才能使用,假如Order类发生了修改变化, 那么所有依赖这个Order类的文件都需要修改,

来在看看依赖注入的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 订单model类, models/order.js
class Order {
constructor() {}

insert() {
return true;
}
}

// 订单类 controllers/OrderController.js
const Order = require("./Order.js");

class OrderController {
constructor(order) {
this.order = order;
}

createOrder(...args) {
this.order.insert(...args);
}
}

// router/index.js
const Order = require("../model/Order.js");
const OrderController = require("./OrderController.js");
const orderController = new OrderController(new Order());

可以看出依赖注入已经对模块进行了解耦,但是还是有不足之处, 下面总结一下依赖注入的优缺点:

优点: 通过依赖注入高层模块与底层模块耦合度降低,因此底层模块发生变化时,我们不需要了解底层模块发生的变化, 只需要管理router中依赖的路径.

不足:

我们所有依赖的模块都在router中引入,明显增加了router模块的复杂性, 我们需要一个专门管理注入及被注入的容器. 即我们常说的IOC 容器.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class IOC {
constructor() {
this.controller = new Map();
}

bind(key, callback) {
this.controller.set(key, { callback, single: false });
}

sigleton(key, callback) {
this.controller.set(key, { callback, single: true });
}

use(key) {
const item = this.controller.get(key);
if (!item) {
throw new Error("error");
}

if (item.single && !item.instance) {
item.instance = item.callback();
}

return item.single ? item.instance : item.callback();
}
}
// router/index.js
const Order = require("../model/Order.js");
const OrderController = require("./OrderController.js");

ioc.bind("order", (...args) => new Order(...args));
ioc.bind("orderController", (...args) => new OrderController(ioc.use("order")));

// router.js
const ioc = require("../ioc.js");
const orderController = ioc.use("orderController");

上面就是一个简单的ICO 容器实现, 通过bind方法将模块间的依赖绑定到容器中,通过use方法判断模块是否存在,
若不存在则报错;
存在则判断是否实例化,已实例化不需要执行的callback().

// 订单model类, models/order.js class Order { constructor() {} insert() { return true; } } // 订单类 controllers/OrderController.js const Order = require("./Order.js"); class OrderController { constructor(order) { this.order = order; } createOrder(...args) { this.order.insert(...args); } } class IOC { constructor() { this.controller = new Map(); } bind(key, callback) { this.controller.set(key, { callback, single: false }); } sigleton(key, callback) { this.controller.set(key, { callback, single: true }); } use(key) { const item = this.controller.get(key); if (!item) { throw new Error("error"); } if (item.single && !item.instance) { item.instance = item.callback(); } return item.single ? item.instance : item.callback(); } } // router/index.js const Order = require("../model/Order.js"); const OrderController = require("./OrderController.js"); ioc.bind("order", (...args) => new Order(...args)); ioc.bind("orderController", (...args) => new OrderController(ioc.use("order"))); // router.js const ioc = require("../ioc.js"); const orderController = ioc.use("orderController");

Array

new Array() 与 Array.of()

1
2
3
new Array(3) // [undefined,undefined,undefined]

Array.of(3) // [3]

Array.from(arrLike, mapFn)

从一个类型Array对象上产生一个数组,如果有迭代器,优先使用迭代器,没有迭代器使用length属性,都没有数组长度为0, mapFn返回值为新数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var a = {
1: "张三",
2: "12"
}
a[Symbol.iterator] = function() {
index = 0;
return {
next: ()=>{
index++;
return {
value: this[index],
done: !this[index]
}
}

}
}

console.dir(Array.from(a))// ["张三", "12"]

核心:

  1. 元素反向
1
2
3
4

display: flex;
flex-flow: row-reverse

  1. 兄弟元素操作
1
input: checked ~ input

原理:
内部求: 使用 blur(5) 模糊边缘,然后当两个图片相重叠后,
相互交叉部分由于图片颜色变重, 提高图片对比度后交叉部分颜色显示出来,形成水滴
外部: 使用 contrast(10) 提高对比度

在给 外部加上白色背景颜色,

Markdown

快捷键

快捷键 操作
Ctrl+B 加粗
Ctrl+I 斜体
Ctrl+Q 引用
Ctrl+L 插入链接
Ctrl+K 插入代码
Ctrl+G 插入图片
Ctrl+H 提升标题
Ctrl+O 有序列表
Ctrl+U 无序列表
Ctrl+R 横线
Ctrl+Z 撤销
Ctrl+Y 重做