[TOC]
1\. 数组扁平化
2\. 数组去重
3\. 类数组转化为数组 (类数组是具有 length 属性,但不具有数组原型上的方法)
4\. array.prototype.filter()
5\. Array.prototype.map()
6\. Array.prototype.forEach()
7\. Array.prototype.reduce()
8\. Apple() (function.prototype.apple())
9\. call
10\. Bind()
11\. Debounce () 防抖 (频率高频时间后 n 秒内函数只会执行一次,如果 n 秒内高频时间再次触发,则重新计算时间)
12\. throttle 节流 ()
13\. 函数珂里化
14\. 模拟 new 操作
15\. Instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某一个实例对象的原型链上
16\. 原型继承
17\. Object.is
18\. Object.assign () 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象 (它返回目标对象 这个操作是浅拷贝)
19\. 深拷贝
20\. promise
21\. Promise.all
22\. Promise.race
23\. Promise 并行限制
24\. JSONP
25\. AJAX
26\. event 模块 实现 node 中回调函数的机制,node 中回调函数其实是内部使用了观察者模式
27\. 图片懒加载
28\. 滚动加载
29\. 渲染几万条数据不卡住页面 (渲染大数据时,合理利用 createDocumentFragment 和 requestAnimationFrame,将操作切分为一小段一小段执行)
30\. 打印出当前页面使用了多少种 HTML 元素
31\. 将 virtualDom 转化为真实 DOM 结构
32\. 字符串解析问题 ()
# 深拷贝
简单版:
```
function deepCopy(obj){
var cloneObj;
// 当输入数据为简单数据类型时直接复制
if (obj && typeof obj!=='object'){ return obj;}
// 当输入数据为对象或数组时
else if (obj && typeof obj==='object'){
// 检测输入数据是数组还是对象
cloneObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)){
if(obj[key]&&typeof obj[key] === 'object') {
// 若当前元素类型为对象时,递归调用
cloneObj[key] = deepCopy(obj[key]);
}
// 若当前元素类型为基本数据类型
else{ cloneObj[key] = obj[key]; }
}
}
}
return cloneObj;
}
```
第二种:
~~~js
/**
* Get the first item that pass the test
* by second argument function
*
* @param {Array} list
* @param {Function} f
* @return {*}
*/
export function find (list, f) {
return list.filter(f)[0]
}
/**
* Deep copy the given object considering circular structure.
* This function caches all nested objects and its copies.
* If it detects circular structure, use cached copy to avoid infinite loop.
*
* @param {*} obj
* @param {Array<Object>} cache
* @return {*}
*/
export function deepCopy (obj, cache = []) {
// just return if obj is immutable value
if (obj === null || typeof obj !== 'object') {
return obj
}
// if obj is hit, it is in circular structure
const hit = find(cache, c => c.original === obj)
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
copy
})
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], cache)
})
return copy
}
~~~
# 自定义柯里化函数:
实现 1:
```
function curry(func) {
return function curried(...args) {
console.log(args);
if (args.length >= func.length) {
return func.apply(this, args);
} else {
console.log('calling else');
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
// 测试
function multiply(a, b, c) {
return a * b * c;
}
let curried = curry(multiply);
console.log(curried(1, 2, 3));
console.log(curried(1)(2, 3));
console.log(curried(1)(2)(3));
```
实现 2:
```
const curry = fn =>
(judge = (...args) =>
args.length === fn.length
? fn(...args)
: (...arg) => judge(...args, ...arg));
// 测试
const sum = (a, b, c, d) => a + b + c + d;
const currySum = curry(sum);
currySum(1)(2)(3)(4); // 10
currySum(1, 2)(3)(4); // 10
currySum(1)(2, 3)(4); // 10
```
> http://zetcode.com/javascript/currying/
# call 实现
原理:在方法调用模式下,`this`总是指向调用它所在方法的对象,`this`的指向与所在方法的调用位置有关,而与方法的声明位置无关(箭头函数特殊)。
```
const foo = { name: 'foo' };
foo.fn = function() {
// 这里的 this 指向了 foo
// 因为 foo 调用了 fn,
// fn 的 this 就指向了调用它所在方法的对象 foo 上
console.log(this.name); // foo
};
```
实现:
```js
Function.prototype.mycall = function(thisArg) {
// this指向调用call的对象
if (typeof this !== 'function') {
// 调用call的若不是函数则报错
throw new TypeError('Error');
}
const args = [...arguments].slice(1);
thisArg = thisArg || window;
// 将调用call函数的对象添加到 thisArg 的属性中
thisArg.fn = this;
// 执行该属性
const result = thisArg.fn(...arg);
// 删除该属性
delete thisArg.fn;
// 返回函数执行结果
return result;
};
```
# 实现 new 方法
```
function myNew(){
var obj = {}
var Constructor = Array.prototype.shift.call(arguments)
obj.__proto__ = Constructor.prototype // 把实例与构造函数它们的关系建立起来
var res = Constructor.apply(obj, arguments)
return typeof res === 'object' ? res : obj;
}
```
# 数组扁平化
```
// 方案 1
function recursionFlat(ary = []) {
const res = []
ary.forEach(item => {
if (Array.isArray(item)) {
res.push(...recursionFlat(item))
} else {
res.push(item)
}
})
return res
}
// 方案 2
function reduceFlat(ary = []) {
return ary.reduce((res, item) => res.concat(Array.isArray(item) ? reduceFlat(item) : item), [])
}
// 测试
const source = [1, 2, [3, 4, [5, 6]], '7']
console.log(recursionFlat(source))
console.log(reduceFlat(source))
```
# 对象扁平化
```
function objectFlat(obj = {}) {
const res = {}
function flat(item, preKey = '') {
Object.entries(item).forEach(([key, val]) => {
const newKey = preKey ? `${preKey}.${key}` : key
if (val && typeof val === 'object') {
flat(val, newKey)
} else {
res[newKey] = val
}
})
}
flat(obj)
return res
}
// 测试
const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source));
```
# ES 5、ES 6继承
主要使用 ES 5 跟 ES 6 对比看下`class`继承的原理
使用`es6`语法
~~~
class B {
constructor(opt) {
this.BName = opt.name;
}
}
class A extends B {
constructor () {
// 向父类传参
super({ name: 'B' });
// this 必须在 super() 下面使用
console.log(this);
}
}
~~~
使用`es5`语法
使用寄生组合继承的方式
1. 原型链继承,使子类可以调用父类原型上的方法和属性
2. 借用构造函数继承,可以实现向父类传参
3. 寄生继承,创造干净的没有构造方法的函数,用来寄生父类的 prototype
~~~
// 实现继承,通过继承父类 prototype
function __extends(child, parent) {
// 修改对象原型
Object.setPrototypeOf(child, parent);
// 寄生继承,创建一个干净的构造函数,用于继承父类的 prototype
// 这样做的好处是,修改子类的 prototype 不会影响父类的 prototype
function __() {
// 修正 constructor 指向子类
this.constructor = child;
}
// 原型继承,继承父类原型属性,但是无法向父类构造函数传参
child.prototype = parent === null ? Object.create(parent) : ((__.prototype = parent.prototype), new __());
}
var B = (function() {
function B(opt) {
this.name = opt.name;
}
return B;
})();
var A = (function(_super) {
__extends(A, _super);
function A() {
// 借用继承,可以实现向父类传参, 使用 super 可以向父类传参
return (_super !== null && _super.apply( this, { name: 'B' })) || this;
}
return A;
})(B);
~~~
测试 class
~~~
const a = new A();
console.log(a.BName, a.constructor);
// B ,ƒ A() {}
~~~
# Object.create 的实现
~~~
function create(obj) {
function F() {}
F.prototype = obj;
return new F();
}
~~~
# 参考
[JS 基础手写代码](https://hub.fastgit.org/Godiswill/blog/issues/20)
[前端面试中常见的手写功能](https://www.h5w3.com/44219.html)
- 修仙之路
- 基础原理篇
- JS和Node.js事件环机制剖析
- 一图理解原型链
- 手写篇
- 基础手写
- 手写实现 Promise A+ 类库
- 手写 CommonJS
- 手写 Express 框架
- 手写 React Router 4.0
- 手写虚拟 DOM 和 DOM-Diff
- 手写 Webpack 实现
- 手写一个 MVVM 类库
- 手写一个 Vue-cli 脚手架
- 手写 JWT 类库
- 手写 Mobx 类库
- 手写前端性能和错误监控框架
- 手写 Vue 路由
- 手写 Vuex 实现
- 手写 redux 状态容器
- 手写 throttle 和 debounce
- Node 高级
- Mongodb
- 安全测试篇
- CSRF原理实现
- XSS原理实现
- 九种跨域方法全解析
- 编写单元测试
- 爬虫篇
- 使用puppeteer破解滑动验证码
- 工程篇
- 使用AST语法树手工转译ES6代码
- 编写自己的webpack插件
- 实战篇
- webpack4.0 实战
- Canvas+Websocket 实现弹幕
- canvas 动效
- SVG 动效
- CSS3 实现 Apple Watch 中的呼吸灯效果
- CSS3 实现动态气泡屏保效果
- 算法篇
- 基础知识
- 服务器端
- 分布式架构中的幂等性
- TCP/UDP
- Docker
- V8
- 动画篇
- 贝塞尔曲线
- requestAnimationFrame
- 框架篇
- 随记