[TOC]
>[success] # 几种值传递操作
1. **引入的赋值**:指向同一个对象,相互之间会影响;
2. **对象的浅拷贝**:只是浅层的拷贝,内部引入对象时,依然会相互影响;
3. **对象的深拷贝**:两个对象不再有任何关系,不会相互影响
>[success] # 引用赋值
~~~
const obj = {
name: "123",
age: 18,
height: 1.88,
friend: {
name: "456"
}
}
// 1.引用赋值
const info1 = obj
~~~
>[success] # 深浅拷贝
~~~
1.由于引用类型在赋值时只传递指针,这种拷贝方式称为'浅拷贝'。
2.而创建一个新的与之相同的引用类型数据的过程称之为'深拷贝'。
~~~
>[info] ## 浅copy -- 的方法
![](https://img.kancloud.cn/e7/4d/e74d50f3fabb7bf847702724ce6ca110_874x533.png)
>[danger] ##### object.assign
~~~
1.object.assign 的语法为:Object.assign(target, ...sources)
2.它不会copy 原型链上共享的属性,不会copy 不可枚举属性,可以copy 'symbol' 类型
~~~
~~~
const obj = {a:{b:1},c:1,d:Symbol(10)}
Object.defineProperty(obj,'e',{
value:"不可枚举",
enumerable:false
})
const copyObj = {}
// 返回值是目标对象 即Object.assign 整体的返回值也是copyObj
Object.assign(copyObj,obj)
console.log(copyObj) // { a: { b: 1 }, c: 1, d: Symbol(10) }
obj.c = 100
obj.a.b = 200
console.log(copyObj); // { a: { b: 200 }, c: 1, d: Symbol(10) }
~~~
* 关于原型链上共享的属性
~~~
1.原型链上共享的属性
~~~
~~~
const obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
~~~
* 如图
![](https://img.kancloud.cn/2d/bf/2dbf883711c98e78a5337174de05acbd_258x125.png)
>[danger] ##### 扩展运算符
~~~
1.扩展运算符的语法为:let cloneObj = { ...obj }
2.它不会copy 原型链上共享的属性,不会copy 不可枚举属性,可以copy 'symbol' 类型
~~~
~~~
const obj = {a:{b:1},c:1,d:Symbol(10)}
Object.defineProperty(obj,'e',{
value:"不可枚举",
enumerable:false
})
const copyObj = {...obj}
console.log(copyObj) // { a: { b: 1 }, c: 1, d: Symbol(10) }
obj.c = 100
obj.a.b = 200
console.log(copyObj); // { a: { b: 200 }, c: 1, d: Symbol(10) }
~~~
>[danger] ##### concat 拷贝数组
~~~
let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr); // [ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]
~~~
>[danger] #### slice 拷贝数组
~~~
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr); //[ 1, 2, { val: 1000 } ]
~~~
>[danger] ##### 实现一个自己的浅拷贝
~~~
1.实现的思路只要将数组或者是对象类型的数据最外层进行重新内存指向
~~~
~~~
const shallowClone = (target)=>{
// 浅拷贝将对象或者数组最外层copy一份
if(typeof target === "object" && target !==null){
const cloneTarget = Array.isArray(target)?[]:{}
for(let key in target){
cloneTarget[key] = target[key]
}
return cloneTarget
}else{
// 基本类型直接返回
return target
}
}
~~~
>[info] ## 深copy
>[danger] ##### 30s js 的案例
~~~
const deepClone = obj => {
if (obj === null) return null;
// 先浅copy 一层
let clone = Object.assign({}, obj);
Object.keys(clone).forEach(
key =>
(clone[key] =
typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
);
if (Array.isArray(obj)) {
clone.length = obj.length;
return Array.from(clone);
}
return clone;
};
~~~
>[danger] ##### JSON.stringfy()
~~~
1.使用JSON.stringfy() 和 JSON.parse() 的方法将JSON 字符串生成一个新的对象,日常的简单开发已经
够用了
2.对应弊端
2.1.拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后
的字符串中这个键值对会消失
2.2.拷贝 Date 引用类型会变成字符串;
2.3.无法拷贝不可枚举的属性;
2.4.无法拷贝对象的原型链;
2.5.拷贝 RegExp 引用类型会变成空对象;
2.6.对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
2.7.无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。
~~~
>[danger] ##### 第一版
~~~
1.深copy 时候,需要遍历object 的key,遍历一般采用'Object.keys 获取键名或 for...in 方式遍历',但是
这种处理方式不能针对'Symbol'类型做'key',想解决这个问题可以使用 'getOwnPropertyNames' 和
'getOwnPropertySymbols' 函数将键名组合成数组
2.getOwnPropertyNames 不单单可以获取可枚举属性包括不可枚举属性,'Object.keys' 仅仅是对象自己的属性,
非原型链上的,'for..in'循环遍历整个原型链 二者都不能对不可枚举 和symbols 数据返回,因此需要
'getOwnPropertyNames' 和'getOwnPropertySymbols' 函数将键名组合成数组
3.也可以使用Reflect.ownKeys(target)Reflect.ownKeys 方法返回一个由目标对象自身的属性键组成的数组。
它的返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
~~~
[for...in 和 Object.keys
](https://www.kancloud.cn/cyyspring/more/2079489)
~~~
function clone(data) {
let result = {}
// Reflect.ownKeys(data)
const keys = [...Object.getOwnPropertyNames(data), ...Object.getOwnPropertySymbols(data)]
if(!keys.length) return data
keys.forEach(key => {
let item = data[key]
// null 的type类型也是object
if (typeof item === 'object' && item) {
result[key] = clone(item)
} else {
result[key] = item
}
})
return result
}
~~~
* 吸取30s 数组也支持改进
~~~
function clone (data) {
if (typeof data === null || typeof data !== 'object') return data
const result = {}
// 获取所有key
const keys = Reflect.ownKeys(data)
keys.forEach(key => {
result[key] = typeof data[key] === 'object' ? clone(data[key]) : data[key]
})
// length 因为使用了获取不可枚举的属性相当于一获取了 所以不用length 重新赋值
return Array.isArray(data) ? Array.from(result) : result
}
~~~
>[danger] ##### 解决循环引用问题
~~~
1.添加一个 WeakMap 来记录已经拷贝过的对象,如果当前对象已经被拷贝过,那么直接从 WeakMap 中取出,
否则重新创建一个对象并加入 WeakMap 中
var a = {}
var b = {}
a.b = b
b.a = a
~~~
~~~
function clone(obj) {
let map = new WeakMap()
function deep(data) {
let result = {}
const keys = [...Object.getOwnPropertyNames(data), ...Object.getOwnPropertySymbols(data)]
if(!keys.length) return data
const exist = map.get(data)
if (exist) return exist
map.set(data, result)
keys.forEach(key => {
let item = data[key]
if (typeof item === 'object' && item) {
result[key] = deep(item)
} else {
result[key] = item
}
})
return result
}
return deep(obj)
}
~~~
>[danger] ##### 在升级思考
~~~
1.当参数为 Date、RegExp 类型,则直接生成一个新的实例返回
2.考虑原型链上需要被原型链上的方法和属性
~~~
~~~
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date)
return new Date(obj) // 日期对象直接返回一个新的日期对象 可以使用instanceof 作为判断
if (obj.constructor === RegExp)
return new RegExp(obj) //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性 这一步 如果是数组生成的就变成了数组
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
~~~
>[info] ## jq 深浅copy
~~~
// $.extend(obj1,obj2,obj3) // 浅
// $.extend(true,obj1,obj2,obj3) // 深
jQuery.extend = jQuery.fn.extend = function () {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// 保证target是一个对象「第一个如果是布尔类型,是控制深浅合并的,让其赋值给deep,并且让target等于第二个实参{对象}」
if (typeof target === "boolean") {
deep = target;
target = arguments[i] || {};
i++;
}
if (typeof target !== "object" && !isFunction(target)) target = {};
// 只传递了一个对象,这种情况下,是为了给$/$.fn扩充方法的「也可以理解为,把传递对象中的每一项和$/$.fn进行合并」
if (i === length) {
target = this;
i--;
}
// target是多个对象合并中,被覆盖的那个对象,我们拿其他传递的对象,依次覆盖他即可
for (; i < length; i++) {
// options就是剩余传递的某个对象
if ((options = arguments[i]) != null) {
// 迭代options中的每一项,用其替换target中对应的项(如果target中没有这一向,就是直接新加一个)
for (name in options) {
copy = options[name];
// 循环引用
if (name === "__proto__" || target === copy) {
continue;
}
// 实现深度合并
if (deep && copy && (jQuery.isPlainObject(copy) ||
(copyIsArray = Array.isArray(copy)))) {
// src是要被替换的项 copy拿它替换src
src = target[name];
if (copyIsArray && !Array.isArray(src)) {
clone = [];
} else if (!copyIsArray && !jQuery.isPlainObject(src)) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
// 浅合并
target[name] = copy;
}
}
}
}
return target;
};
~~~
* 作为类工具类仿照
~~~
class Tools {
/**
* 描述jq 的深浅copy
* @date 2022-01-15
* @param {target} 合并的目标对象例如 extends(a,b) 则b和并到a 上,a为目标对象
* @param {options} 合并的配置对象例如 extends(a,b) 则b和并到a 上,b为被copy配置对象
* @param {deep} 深浅copy 默认浅copy 用法extends(true,a,b) b合并到a进行深copy
* @param {name} 对象的key
* @returns {any}
*/
extends() {
var options,
name,
src,
copy,
copyIsArray,
clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false
// 判断是否为深度拷贝参数,如果是的话赋值给deep,并且设置拷贝目标为arguments[1]
// 且修改类数组的长度i为2
// 举例子 extends(false,a,b) 此时明确了是采用浅copy,因此实际合并对象就是从第二个参数开始
// 因此需要依次前移目标参数
if (typeof target === 'boolean') {
deep = target
target = arguments[1] || {}
i = 2
}
// 判断传入合并对象参数必须为对象,如果不是对象重置为对象
if (typeof target !== 'object') target = {}
// 判断参数长度是否等于i 来决定最后和调用本身进行合并
// 递归到最后来和自己本身合并
if (length === i) {
target = this
--i
}
// 开始前一个赋值给后一个对象 举个例子
// extends(false,a,b) 即 b 合并给目标a
for (; i < length; i++) {
if ((options = arguments[i]) !== null) {
// 对被拷贝的对象属性进行循环 for...in 不能copy 不可枚举的属性
for (name in options) {
src = target[name]
copy = options[name]
}
// 防止循环引用
if (target === copy) continue
// 判断是否是深度拷贝,并且进行相应的操作
if (
(deep && copy && typeof copy === 'object') ||
(copyIsArray = Array.isArray(copy))
) {
// 如果目标对象为 {ls:1} 被克隆对象为{ls:[1,2,3]}
// 则此时要把目标对象 合并 转换为数组即 {ls:1} => {ls:[]}
// 对象和数组同理
if (copyIsArray) {
copyIsArray = false
clone = src && Array.isArray(src) ? src : []
} else {
clone = src && typeof src === 'object' ? src : {}
}
// 使用递归进行深度拷贝,且不修改原对象
target[name] = this.extend(deep, clone, copy)
// 如果拷贝的对象为undefined则不进行拷贝
} else if (copy !== undefined) {
target[name] = copy
}
}
}
}
}
~~~
- HTTP -- 初始
- HTTP -- 什么是HTTP
- HTTP -- 相关技术
- HTTP -- 相关的协议
- Emmet -- 语法
- HTML -- 补充
- iframe -- 补充
- checkbox 和 radio 细节
- form -- 补充
- html -- html5
- html -- 视频音频
- html -- html5 data-* 全局属性
- css -- 重学
- css -- 单位
- css 知识补充 -- 导入
- css -- 颜色补充
- css --继承性
- css - 元素隐藏
- 标签元素--块/行内/行内块
- css -- 盒子阴影 – 在线查看
- css -边框图形
- css -- Web字体
- css -- 精灵图
- css -- 属性补充
- text-align -- 内容
- line-height -- 行高
- 文字换行
- overflow-溢出
- css -- 选择器
- css -- 伪元素
- css -- 伪类选择器
- 设置高度宽度 和 盒子模型
- css -- 文字溢出
- css -- white-space / text-overflow
- css -- 定位
- css -- 浮动
- 浮动 -- 案例
- flex -- 布局
- flex -- 解决等距布局
- flex -- 内容居中
- flex -- 导航栏
- Grid -- 布局
- css -- transform 形变
- css -- 垂直水平居中
- css -- transition 动画
- css -- Animation 动画
- css -- vertical-align
- css -- 函数
- css -- 媒体查询
- 重学案例
- 重新 -- 鼠标常见样式
- 重学 -- 透明边框background-clip
- 重学 -- 多重边框box-shadow
- css -- 预处理器
- 移动端适配
- 前端结构目录说明
- 浏览器 加载过程
- 回流和重绘
- 杂七杂八 -- 小工具方法
- npm包比较网站
- IP定位
- 通过useragent获取手机品牌型号
- 自启本地服务
- BOM -- 常用知识点记录
- window -- 认识
- windows -- 大小滚动
- BOM -- Location
- BOM -- URLSearchParams
- BOM -- history
- BOM -- navigator 和 screen
- 前端存储 -- Cookie、LocalStorage、IndexedDB
- DOM -- 知识总结
- DOM -- 获取元素
- DOM -- 节点属性
- DOM -- 元素属性
- 获取元素style的读取 - getComputedStyle
- DOM -- 元素的操作
- DOM -- 元素大小和滚动
- DOM -- 小练习
- Event -- 事件
- event -- 事件对象
- event -- 案例
- event -- 做一个树
- js -- ajax
- 封装一个ajax
- ajax -- 文件上传
- 倒计时抢购案例
- axios -- 封装
- 跨域
- 前端 -- Fetch API
- js -- 基础篇
- 数据类型
- 数据类型检测
- 类型小知识
- 原始类型的包装类
- 类型转换
- delete -- 运算符
- Date -- 对象
- 函数参数传递
- 对象某个属性不存时候判断
- 操作符
- 函数变量传值
- 访问对象通过点和[]
- 和if等同的一些写法
- for -- 执行顺序
- JS -- 执行过程
- JS中的堆(Heap)栈(Stack)内存
- JS -- 执行上下文
- Js -- ES3 和 ES5+ 后
- let const var
- ES3 -- 案例思考
- 闭包概念
- 浅拷贝和深拷贝
- JS -- 严格模式
- js -- 数组篇
- Array -- 数组基础
- Array -- 小常识检测数组
- Array -- 小技巧将数组转成字符串
- Array -- 自带方法
- Array -- 数组插入总结
- Array -- every是否都符合巧用
- js--Function篇
- Function -- length 属性
- Function -- arguments
- Function -- 也是对象
- Function -- new Function 创建方法
- Function -- 函数作为返回值
- Function -- callee
- 匿名函数
- Function -- 闭包
- 闭包内存查看
- 闭包 -- 使用的案例
- 闭包 -- 使用的案例
- 箭头函数不适用场景
- js -- this、call、apply
- this -- 指向
- call、apply -- 讲解
- 总结 -- this
- 思考题
- Object -- 数据属性/访问器属性
- 新增关于对象方法
- js -- 面向对象
- 对象到底是什么
- 到底什么是js的对象
- js --prototype、__proto__与constructor 1
- JS es5继承
- JS 中的原型继承说明
- JS -- Object是所有类的父类
- 总结
- Object -- 函数构造函数
- Object -- 手动实现一个new
- js -- 函数式编程(目前只是了解后面需要更多时间深入)
- 了解 -- 高阶函数
- 了解 -- 纯函数
- 了解 -- 函数柯里化
- 柯里化 -- 知识点
- 了解 -- 函数组合
- 了解 -- 函子
- js--小知识点
- url -- 将get请求连接参数变成对象
- event -- 一个函数处理多个事件
- try -- 处理异常
- Error -- 前段报错信息传给后台
- JSON -- 序列化
- return -- 返回true和false
- for -- 循环里初始化变量
- 命名 -- get和set怎么命名
- 链式调用
- 利用递归代替循环思路
- JS -- 技巧篇
- 技巧 -- 代码规范
- 技巧 -- 惰性载入函数
- 技巧 -- 防抖函数
- 技巧 -- 节流函数
- 插入补充(防抖/节流)
- 技巧 -- 定时器的使用
- 技巧 -- 回调函数
- 技巧 -- 分时函数
- 技巧 -- 除了return 我怎么跳出多层循环
- 技巧 -- switch 还是 if-else?
- 技巧 -- 将字符串转成对象
- 技巧 -- 函数转换
- 技巧 -- 工作记录数组对象中相同项
- JS -- 数组小文章总结
- 数组类型判断
- includes 和 indexOf
- for ... in,for ... of和forEach之间有什么区别
- map、filter、reduce、find 四种方法
- 多种形式处理数组思考方式
- for...in 和 Object.keys
- 各种知识点集合文章
- 创建数组 -- 总结
- 数组去重 -- 总结
- 获取数组中不重复的元素 -- 总结
- 比较两个数组元素相同 -- 总结
- 出现频率最高的元素 -- 总结
- 两数组交集 -- 总结
- 两数组差集 -- 总结
- 工具方法 - 总结
- 扁平化数组
- JS -- 数组技巧篇 30s
- 30s Array -- 创建数组篇(一)
- 30s Array --过滤(查询)篇章(一)
- 30s Array --过滤篇章(二)
- 30s Array -- 新增篇(一)
- 30s Array--比较篇(一)
- 30s Array -- 分组篇(一)
- 30 Array -- 删除篇(一)
- 30s Array-- 其他篇(一)
- 30s Array -- 个人感觉不常用篇章
- JS -- 对象技巧篇30s
- 30s Object -- 查(一)
- 30s Object -- 增(一)
- 30s Object -- 工具类型小方法
- 30s Object -- 跳过没看系列
- ES -- 新特性
- 变量篇章
- 变量 -- let/const/var声明
- 变量 -- 词法声明和变量声明
- 变量 -- var x = y = 100
- 变量 -- a.x = a = {n:2}
- 带标签的模板字符串
- 函数篇章
- 函数 -- 参数篇章
- 函数 -- 只能通过new创建实例
- 函数 -- 箭头函数
- 函数 -- 尾调优化
- 对象篇章
- 对象 -- 字面量写法的优化/新增方法
- 赋值篇章
- 解构赋值 -- 简单概念
- 解构赋值 -- 对象解构
- 解构赋值 -- 数组解构
- 解构赋值 -- 函数参数
- Symbol 属性
- Set 和 Map
- Set -- 去重/交、并、差集
- Map -- 集合
- 类class篇章
- ES6 和 ES5 的写法比较
- es6 -- mixin函数继承
- es6 -- 创建一个接口
- ES5 VS ES6 -- class 编译
- 数组新功能
- 创建/转换 -- 数组
- 迭代器和生成器
- es6 -- 迭代器
- es6 -- 生成器
- for-of 循环的是什么
- 做一个异步执行器
- 代理和反射
- Proxy -- 使用
- Reflect -- 使用
- Proxy 和 Reflect 结合使用
- 运算符 -- 展开运算符
- ES7 -- 新特性
- ES8 -- 新特性
- ES9 -- 新特性
- ES10 -- 新特性
- ES11 -- 新特性
- ES12 -- 新特性
- ES13 -- 新特性
- ES6 和 ES8 的异步
- js -- 异步、同步、阻塞、非阻塞
- js -- 如何做到异步的
- js -- 引擎线程,GUI渲染线程,浏览器事件触发线程
- js -- 如何通过事件循环运行异步
- js -- 误区任务回调就是异步
- js -- 宏任务和微任务
- 参考文章推荐阅读
- js -- callback 还是 promise
- js -- Promise 初识
- js -- 自己实现一个Promise
- js -- Promise 更多用法
- 再来看setTimeout 和for
- js -- ES8 异步 async await
- js -- 红绿灯问题
- js -- 倒计时
- 异步图片预加载
- 手动触发异步
- 异步题
- JS -- 模块化
- CommonJS -- 在node服务器端
- AMD 模块化规范
- ES Modules
- ES Modules -- 使用特点
- import 和 export 使用
- export 和 import -- 执行
- 其他用法
- systemjs
- 对比区别
- 使用babel 或webpack进行模块打包
- Jq -- 细节
- JS -- 性能优化
- 图片预加载
- js -- 正则
- 设计原则和编程技巧
- 设计原则 -- 单一职责
- js -- 设计模式
- 设计模式 -- 简单理解
- 一、鸭子类型
- 1.1 多态概念
- 1.2 小章节记录 -- 封装
- 1.3.多态和鸭子
- 设计模式 -- 单例模式(透明、惰性)
- 单例模式 -- js单例和惰性单例
- ES6 -- 写单例模式
- 设计模式 -- 策略模式
- 策略模式 -- 表单验证
- 策略模式 -- 表单验证多规则
- 策略模式和多态区别
- 设计模式 -- 代理模式(代理被代理相同方法)
- 代理模式 -- 分类
- 代理模式 -- 总结
- 设计模式 -- 迭代器模式
- 设计模式 -- 观察者(发布订阅)模式
- 观察者 和 发布订阅区别
- 发布订阅模式 -- 现在通用模型
- 发布订阅模式 -- 书中实际案例
- 发布订阅模式--全局发布订阅对象
- 发布订阅模式 -- vue
- 设计模式 -- 命令模式(对象方法拆分指令)
- 命令模式 -- js 自带
- 命令模式 -- 撤销和重做
- 命令模式 -- 命令和策略的区别
- 设计模式 -- 组合模式(树)
- 组合模式 -- 拆分了解
- 组合模式 -- java角度
- 组合模式 -- 书中案例扫描文件夹
- 组合模式 -- 注意点
- 组合模式 -- 引用父对象(书中案例)
- 组合模式 -- 小的案例
- 组合模式 -- 总结
- 设计模式 -- 模板方法(抽象类继承)
- 模板方法 -- 前端角度思考
- 模板方法 -- java 思考方式
- 模板方法 -- js没有抽象类
- 模板方法 -- 钩子方法
- 模板方法 -- 用js方式来实现
- 设计模式 -- 享元模式
- 享元、单例、对象池
- 享元模式 -- 前端角度思考
- 享元模式 -- 代码案例
- 享元模式 -- 适用和内外部状态
- 额外扩展 -- 对象池
- 设计模式 -- 职责链模式
- 职责链 -- 前端角度
- 职责链和策略
- 职责链 -- 异步的职责链
- 职责链 -- AOP
- 职责链 -- 改造迭代器模式的代码
- 设计模式 -- 中介者模式
- 中介者模式 -- 前端角度(一)
- 中介者模式 -- 前端角度(二)
- 中介者模式 -- 前端角度(三)
- 中介者模式 -- 总结
- 设计模式 -- 装饰者模式
- 装饰者模式 -- es5 解决方案
- 装饰器模式 -- 和代理模式区别
- 设计模式 -- 状态模式
- 状态模式 -- 前端思想
- 状态模式 -- 文件上传案例
- 状态模式 -- 和策略模式的区别
- 设计模式 -- 适配器模式
- js -- 代码重构
- 重构 -- 方法封装
- 重构 -- 抽象函数
- 高阶函数 -- 范式
- 状态管理方案
- Node -- 学习篇
- Node -- 服务端运行的js
- node -- Global(全局成员)
- node -- Buffer缓冲器
- node -- 路径操作
- node -- 文件的读写
- node -- 目录操作
- node -- HTTP
- HTTP -- 响应头常见类型
- HTTP -- 处理Get
- HTTP -- 处理Post
- HTTP -- 简单的案例
- Express
- Express -- 中间件
- Express -- 处理post/get
- Express -- 模板引擎
- Express -- 简单案例和目录搭建
- Express -- 数据库service.js
- Express -- json/url
- Express -- 配合数据库
- 配合数据库 -- 简单的登录
- npm -- js包管理
- npm -- 淘宝镜像
- nrm -- 更多镜像的选择
- yarn -- 包管理
- yarn -- 清理缓存
- WebPack -- 模块化开发
- webPack -- 安装、使用、打包
- webPack -- 使用配置文件
- 延伸 -- base64图片优劣
- webPack -- 完整配置