Skip to content

Commit 785c2dc

Browse files
committed
项目初始化
0 parents  commit 785c2dc

20 files changed

+1212
-0
lines changed

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## handwritten-js-code ##
2+
3+
手写JS代码实现 - JS代码实现的最易记忆写法。
4+
5+
1. [防抖](./src/debounce.md)
6+
2. [节流](./src/throttle.md)
7+
3. [数组去重](./src/unique.md)
8+
4. [数组扁平化](./src/flatten.md)
9+
5. [深拷贝](./src/deepClone.md)
10+
6. [函数柯里化](./src/curry.md)
11+
7. [偏函数](./src/partial.md)
12+
8. [函数组合](./src/compose.md)
13+
9. [函数记忆化](./src/memoize.md)
14+
10. [函数bind实现](./src/bind.md)
15+
11. [函数call与apply实现](./src/call.md)
16+
12. [Object.create实现](./src/Object.crteate.md)
17+
13. [new实现](./src/new.md)
18+
14. [instanceOf实现](./src/instanceOf.md)
19+
15. [forOf循环实现](./src/forOf.md)
20+
16. [trim实现](./src/trim.md)
21+
17. [Promise实现](./src/promise.md)
22+
18. [Symbol实现](./src/Symbol.md)
23+
19. [Set实现](./src/Set.md)

src/Object.crteate.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Object.crteate实现 #
2+
3+
## 描述 ##
4+
5+
实现一个`create`函数,功能与`Object.crteate`相同。如:
6+
7+
```JavaScript
8+
let obj = {
9+
name: 'Orange',
10+
sayName () {
11+
console.log(`我是${this.name}`);
12+
}
13+
}
14+
let man = create(obj);
15+
man.name = 'AAA';
16+
obj.sayName(); // 打印 我是Orange
17+
man.sayName(); // 打印 我是AAA
18+
```
19+
20+
## 实现 ##
21+
22+
步骤:
23+
24+
1. 判断类型不是object或者function抛出异常;
25+
2. 定义构造器函数,其原型指向第一个参数,并创建对象;
26+
3. 如果有第二个参数则使用`Object.defineProperties`定义属性操作符;
27+
4. 如果第一个参数是null,则修复`obj.__proto__`指向null;
28+
5. 返回创建的对象。
29+
30+
```JavaScript
31+
const create = (proto, properties) => {
32+
// 不是函数或者对象抛出错误
33+
if (![ 'object', 'function' ].includes(typeof proto)) {
34+
throw new TypeError(`Object prototype may only be an Object or null: ${proto}`)
35+
}
36+
// 创建构造函数
37+
const Constructor = function () {};
38+
// 构造函数的原型指向对象
39+
Constructor.prototype = proto;
40+
// 创建实例对象
41+
const obj = new Constructor();
42+
// 支持第二个参数
43+
if (properties) {
44+
Object.defineProperties(obj, properties);
45+
}
46+
// 支持空原型
47+
if (proto === null) {
48+
obj.__proto__ = null;
49+
}
50+
51+
return obj
52+
}
53+
```

src/Set.md

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Set实现 #
2+
3+
## 描述 ##
4+
5+
定义一个`MySet`类,功能类似于Set。如:
6+
7+
```JavaScript
8+
let set = new MySet([1, 1, 2, 3, 3]);
9+
10+
set.add(NaN);
11+
console.log(set.size); // 4
12+
set.delete(NaN); // 3
13+
console.log(set.size); // 3
14+
console.log([...set.keys()]); // [1, 2, 3]
15+
console.log([...set.values()]); // [1, 2, 3]
16+
console.log([...set.entries()]); // [[1, 1], [2, 2], [3, 3]]
17+
set.clear();
18+
console.log(set.size); // 0
19+
```
20+
21+
## 实现 ##
22+
23+
思路:
24+
25+
1. 内部使用数组作为容器来存放数据,在调用add的时候,如果数据已经存在于数组中则不添加,否则添加。
26+
2. 考虑到`NaN`也可以存放在`Set`中需要对`NaN`做特殊处理,可以使用存放的时候Symbol来代替,取值的时候如果是该Symbol返回NaN。
27+
3. values和keys使用相同的方法,返回迭代器对象。
28+
29+
```JavaScript
30+
(function(global) {
31+
// encodeVal 和 decodeVal是为了处理NaN的问题
32+
let NaNSymbol = Symbol('NaN');
33+
let encodeVal = function(value) {
34+
return value !== value ? NaNSymbol : value;
35+
}
36+
37+
let decodeVal = function(value) {
38+
return (value === NaNSymbol) ? NaN : value;
39+
}
40+
41+
let makeIterator = function(array, iterator) {
42+
let nextIndex = 0;
43+
let obj = {
44+
next: function() {
45+
return nextIndex < array.length ? {
46+
value: iterator(array[nextIndex++]),
47+
done: false
48+
} : {
49+
value: undefined,
50+
done: true
51+
};
52+
}
53+
};
54+
55+
obj[Symbol.iterator] = function() {
56+
return obj;
57+
}
58+
59+
return obj;
60+
}
61+
62+
class MySet {
63+
static length = 0;
64+
65+
constructor(data) {
66+
this._values = [];
67+
this.size = 0;
68+
69+
for(let item of data || []) {
70+
this.add(item);
71+
}
72+
73+
this.keys = this.values;
74+
}
75+
76+
add(value) {
77+
value = encodeVal(value);
78+
if (this._values.indexOf(value) == -1) {
79+
this._values.push(value);
80+
++this.size;
81+
}
82+
return this;
83+
}
84+
85+
has(value) {
86+
return (this._values.indexOf(encodeVal(value)) !== -1);
87+
}
88+
89+
delete(value) {
90+
var idx = this._values.indexOf(encodeVal(value));
91+
if (idx == -1) return false;
92+
this._values.splice(idx, 1);
93+
--this.size;
94+
return true;
95+
}
96+
97+
clear() {
98+
this._values = [];
99+
this.size = 0;
100+
}
101+
102+
forEach(callbackFn, thisArg) {
103+
thisArg = thisArg || global;
104+
for(let item of this._values) {
105+
callbackFn.call(thisArg, item, item, this);
106+
}
107+
}
108+
109+
values() {
110+
return makeIterator(this._values, function(value) { return decodeVal(value); });
111+
}
112+
113+
entries() {
114+
return makeIterator(this._values, function(value) { return [decodeVal(value), decodeVal(value)]; });
115+
}
116+
117+
[Symbol.iterator]() {
118+
return this.values();
119+
}
120+
121+
}
122+
123+
global.MySet = MySet;
124+
125+
})(this);
126+
```

src/Symbol.md

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Symbol实现 #
2+
3+
## 描述 ##
4+
5+
定义一个`MySymbol`函数,功能类似于Symbol。如:
6+
7+
```JavaScript
8+
let s1 = MySymbol('foo');
9+
let s2 = MySymbol('bar');
10+
console.log(s1 === s2); // 打印 false
11+
// MySymbol.for与MySymbol.keyFor不可枚举
12+
let s3 = MySymbol.for('xxx');
13+
let s4 = MySymbol.for('xxx');
14+
console.log(s3 === s4); // 打印 true
15+
let key1 = MySymbol.keyFor(s3);
16+
console.log(key1); // 打印 xxx
17+
let obj = {
18+
[s1]: 'symbol',
19+
}
20+
console.log(obj[s1]); // 打印 symbol
21+
```
22+
23+
## 实现 ##
24+
25+
步骤:
26+
27+
1. 定义一个MySymbol函数,如果是new的该函数,直接报错;该函数接受一个参数作为Symbol的描述,如果有值则转化为字符串;
28+
2. MySymbol函数需要创建一个对象并返回,该函数拥有不可修改的属性`__Description__``__Name__`分别用来记录描述和名称,对象的toString返回唯一的`__Name__`
29+
3. MySymbol函数需要加两个不可枚举的静态方法`for``keyFor`用来查找symbol和对应的key。
30+
31+
```JavaScript
32+
(function() {
33+
let root = this;
34+
35+
// 生成唯一的名称
36+
let generateName = (function(){
37+
let postfix = 0;
38+
return function(descString){
39+
postfix++;
40+
return '@@' + descString + '_' + postfix;
41+
}
42+
})()
43+
44+
let MySymbol = function Symbol(description) {
45+
if (this instanceof MySymbol) throw new TypeError('Symbol is not a constructor');
46+
let descString = description === undefined ? undefined : String(description);
47+
let symbol = Object.create({
48+
toString: function() {
49+
return this.__Name__;
50+
},
51+
valueOf: function() {
52+
return this;
53+
}
54+
});
55+
56+
Object.defineProperties(symbol, {
57+
'__Description__': {
58+
value: descString,
59+
writable: false,
60+
enumerable: false,
61+
configurable: false
62+
},
63+
'__Name__': {
64+
value: generateName(descString),
65+
writable: false,
66+
enumerable: false,
67+
configurable: false
68+
}
69+
});
70+
71+
return symbol;
72+
}
73+
74+
let forMap = {};
75+
76+
Object.defineProperties(MySymbol, {
77+
'for': {
78+
value: function(description) {
79+
let descString = description === undefined ? undefined : String(description);
80+
return forMap[descString] ? forMap[descString] : forMap[descString] = MySymbol(descString);
81+
},
82+
writable: true,
83+
enumerable: false,
84+
configurable: true
85+
},
86+
'keyFor': {
87+
value: function(symbol) {
88+
for (var key in forMap) {
89+
if (forMap[key] === symbol) return key;
90+
}
91+
},
92+
writable: true,
93+
enumerable: false,
94+
configurable: true
95+
}
96+
});
97+
98+
root.MySymbol = MySymbol;
99+
})();
100+
```
101+
102+
> 注意:
103+
>
104+
> 1. `typeof Symbol()`返回的是`symbol`,由于我们无法修改`typeof`操作符,所以这个无法实现;
105+
> 2. `Symbol('foo').toString()`返回的是字符串的`Symbol(foo)`,为了保证在对象上生成唯一的key我们使用了`generateName`来生成唯一的名称,而不是`Symbol(foo)`
106+
> 3. 通过`Symbol('xxx')`创建的Symbol对象,无法通过`Symbol.for('xxx')`找到,我们这里与之相符。

src/bind.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# 函数bind实现 #
2+
3+
## 描述 ##
4+
5+
在函数原型上添加bind2方法,功能与bind相同。如:
6+
7+
```JavaScript
8+
const sayName = function () {
9+
console.log(this.name);
10+
}
11+
12+
const sayNameBind = sayName.bind2({
13+
name: 'Orange',
14+
});
15+
sayNameBind();// 打印 Orange
16+
```
17+
18+
## 实现 ##
19+
20+
### 简单实现 ###
21+
22+
思路:返回一个函数,当这个函数执行的时候,使用bind2第一个参数作为上下文来执行,同时合并bind2剩余参数和返回函数中剩余参数。
23+
24+
```JavaScript
25+
Function.prototype.bind2 = function (context, ...args) {
26+
const self = this;
27+
28+
return function () {
29+
return self.apply(context, [...args, ...arguments]);
30+
}
31+
}
32+
```
33+
34+
> 上述代码存在问题,如果使用new创建的对象,仍然会绑定context实际上new的优先级比bind高。更完善的代码如下:
35+
36+
```JavaScript
37+
Function.prototype.bind2 = function (context, ...args) {
38+
// bind对非函数会抛出异常
39+
if (typeof this !== "function") {
40+
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
41+
}
42+
43+
const self = this;
44+
const Parent = function () {};
45+
const Son = function () {
46+
return self.apply(this instanceof Parent ? this : context, [...args, ...arguments]);
47+
}
48+
49+
Parent.prototype = this.prototype;
50+
Son.prototype = new Parent();
51+
return Son;
52+
}
53+
```

0 commit comments

Comments
 (0)