《JavaScript 设计模式与开发实战》第一部分(1、2、3章)笔记
第1章:面向对象的JavaScript
动态类型和鸭子类型
编程语言按照数据类型大体可以分为两类:
① 静态类型语言:在编译时便已确定变量的类型。
② 动态类型语言:变量类型要到程序运行的时候,待变量被赋予某个值之后,才会具有某种类型。
【鸭子类型】:如果它走起路来像鸭子,叫起来像鸭子,那么它就是鸭子。
鸭子类型指导我们只关注对象的行为,而不关注对象本身,也就是关注 HAS-A,而不是 IS-A。
☛ 在动态类型语言的面向对象设计中,鸭子类型的概念至关重要。利用鸭子类型的思想,我们不必借助超类型的帮助,就能轻松地在动态类型语言中实现一个原则:“面向接口编程,而不是面向实现编程。”
多态
【多态的实际含义】:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。即,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。
【多态背后的思想】:是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来。
静态类型的面向对象语言通常被设计为可以“向上转型”,使用继承得到多态效果,是让对象表现出多态性的最常用手段。
JavaScript 对象的多态性是与生俱来的,并不需要诸如向上转型之类的技术来取得多态的效果。
将行为分布在各个对象中,并让这些对象各自负责自己的行为,这正是面型对象设计的优点。
对象的多态性提示我们,“做什么”和“怎么去做”是可以分开的。
封装
封装的目的是将信息隐藏,封装应该被视为“任何形式的封装”,即,封装不仅仅是隐藏数据,还包括隐藏实现细节、设计细节以及隐藏对象的类型等。
(1)封装数据
JavaScript 只能依赖变量的作用域来实现封装特性,而且只能模拟出 public 和 private 这两种封装性。
var myObject = (function() {
var __name = 'sven'; // 私有(private)变量
return {
getName: function() { // 公开(public)函数
return __name;
}
}
})(); console.log(myObject.getName()); // 'sven'
console.log(myObject.__name); // 'undefined'
(2)封装实现
封装使得对象之间的耦合变松散,对象之间只通过暴露的 API 接口来通信。
(3)封装类型
封装类型是静态语言中一种重要的封装方式。一般而言,封装类型是通过抽象类和接口来进行的。
而 JavaScript 本身是一门类型模糊的语言,在封装类型方面没有能力,也没有必要做的更多。
(4)封装变化
从设计模式的角度出发,封装在更重要的层面体现为封装变化。
通过封装变化的方式,把系统中稳定不变的部分和容易变化的部分隔离开来,在系统的演变过程中,我们只需要替换那些容易变化的部分,如果这些部分是已经封装好的,替换起来也相对容易。
这可以很大程度地保证程序的稳定性和可扩展性。
原型模式和基于原型的 JavaScript 对象系统
(1)使用克隆的原型模式
原型模式是通过克隆来创建对象的。使用原型模式,我们只需要调用负责克隆的方法,便能完成同样的功能。
【原型模式的实现关键】:语言本身是否提供了 clone 方法,ECMAScript 5 提供了
Object.create
方法,可以用来克隆对象:var Plane = function() {
this.blood = 100;
this.attackLevel = 1;
this.defenseLevel = 1;
}; var plane = new Plane();
plane.blood = 500;
plane.attackLevel = 10;
plane.defenseLevel = 7; var clonePlane = Object.create(plane);
console.log(clonePlane); // 在不支持 Object.create 方法的浏览器中,可以使用以下代码:
Object.create = Object.create || function(obj) {
var F = function() {};
F.prototype = obj; return new F();
}
(2)克隆是创建对象的手段
原型模式的真正目的并非在于需要得到一个一模一样的对象,而是提供了一种便捷的方式去创建某个类型的对象,克隆只是创建这个对象的过程和手段。
(3)体验 Io 语言
原型模式不仅仅是一种设计模式,也是一种编程范型。JavaScript 就是使用原型模式来搭建整个面向对象系统的。
在 JavaScript 语言中不存在类的概念,对象也并非从类中创建出来的,所有的 JavaScript 对象都是从某个对象上克隆而来的。
(4)原型编程范型的一些规则
【原型编程中的一个重要特性】:当对象无法响应某个请求时,会把该请求委托给它自己的原型。
【原型编程范型至少包括以下基本规则】:
- 所有的数据都是对象。
- 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它。
- 对象会记住它的原型。
- 如果对象无法响应某个请求,它会把这个请求委托给它自己的原型。
(5)JavaScript 中的原型继承
① 所有的数据都是对象
JavaScript 在设计时,模仿 Java 引入了两套类型机制:基本类型和对象类型。基本类型包括
undefined
、number
、boolean
、string
、function
、object
。按照 JavaScript 设计者的本意,除了
undefined
之外,一切都应是对象。为了实现这一目标,number
、boolean
、string
这几种基本类型数据也可以通过“包装类”的方式变成对象类型数据来处理。我们不能说在 JavaScript 中所有的数据都是对象,但可以说绝大部分数据都是对象。 JavaScript 中的跟对象是
Object.prototype
空对象,每个对象都是从它克隆而来的,它就是它们的原型。var obj1 = new Object();
var obj2 = {}; console.log(Object.getPrototypeOf(obj1) === Object.prototype); // true
console.log(Object.getPrototypeOf(obj2) === Object.prototype); // true
② 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它
JavaScript 的函数既可以作为普通函数被调用,也可以作为构造器被调用。当使用
new
运算符来调用函数时,此时的函数就是一个构造器。③ 对象会记住它的原型
就 JavaScript 的真正实现来说,其实并不能说对象有原型,而只能说对象的构造器有原型。对象把请求委托给它的构造器的原型。。
对象的
__proto__
的隐藏属性,默认会指向它的构造器的原型对象,即{Constructor}.prototype
。实际上,__proto
就是对象跟“对象构造器的原型”联系起来的纽带。④ 如果对象无法响应某个请求,它会把这个请求委托给它自己的原型
(6)原型继承的未来
很多时候,设计模式其实都体现了语言的不足之处。Peter Norvig 曾说,设计模式是对语言不足的补充,如果要使用设计模式,不如去找一门更好的语言。
虽然大多数主流浏览器都提供了
Object.create
方法,但通过其来创建对象的效率并不高,通常比通过构造函数创建对象要慢。另外,通过设置构造器的
prototype
来实现原型继承的时候,除了根对象Object.prototype
本身之外,任何对象都会有一个原型。而通过Object.object(null)
可以创建出没有原型的对象。ECMAScript 6 带来了新的 Class 语法。但其背后仍是通过原型机制来创建对象的。示例代码:
class Animal {
constructor(name) {
this.name = name;
} getName() {
return this.name;
}
} class Dog extends Animal {
constructor(name) {
super(name);
} speak() {
return "woof";
}
} var dog = new Dog('Scamp');
console.log(dog.getName() + ' says ' + dog.speak());
第2章:this、call 和 apply
this
在 JavaScript 中,
this
总是指向一个对象,而具体指向哪一个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。this
的指向大致可以分为以下4种:- 作为对象的方法调用。
- 作为普通函数调用。
- 构造器调用。
Function.prototype.call
或Function.prototype.apply
调用
☛ 丢失的
this
var obj = {
myName: 'sven',
getName: function() {
return this.myName;
}
}; // 作为对象的方法调用
console.log(obj.getName()); // 'sven' // 作为普通函数调用
var getName2 = obj.getName;
console.log(getName2()); // 'undefined'
call 和 apply
【区别】:传参方式不同
【用途】:
(1)改变
this
的指向(2)
Function.prototype.bind
// 简化版实现
Function.prototype.bind = function(context) {
var self = this;
return function() {
return self.apply(context, arguments);
}
}; var obj = {
name: 'sven'
}; var func = function() {
alert(this.name);
}.bind(obj); func();
// 完整版实现
Function.prototype.bind = function() {
var self = this, // 保存原函数
context = [].shift.call(arguments), // 需要绑定的 this 上下文
args = [].slice.call(arguments); // 剩余的参数转成数组 return function() {
return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
// 执行新的函数的时候,会把之前传入的 context 当做函数体内的 this
// 并且组合两次分别传入的参数,作为新函数的参数
}
}; var obj = {
name: 'sven'
}; var func = function(a, b, c, d) {
alert(this.name); // 输出:sven
alert([a, b, c, d]); // 输出: [1, 2, 3, 4]
}.bind(obj, 1, 2); func(3, 4);
(3)借用其他对象的方法
// 方法1
var A = function(name) {
this.name = name;
}; var B = function() {
A.apply(this, arguments);
}; B.prototype.getName = function() {
return this.name;
}; var b = new B('sven');
console.log(b.getName()); // 输出:sven
// 方法2:借用 Array.prototype 对象操作 arguments
Array.prototype.slice // 转成真正的数组
Array.prototype.shift // 截去 arguments 列表中的头一个元素
Array.prototype.push // 往 arguments 中添加一个新元素
第3章:闭包和高阶函数
在 JavaScript 版本的设计模式中,许多模式都可以用闭包和高阶函数来实现。
闭包
闭包的形成与变量的作用域以及变量的生存周期密切相关。
(1)变量的作用域:
就是指变量的有效范围,最常指函数中声明的变量作用域。
(2)变量的生存周期:
① 全局变量:生存周期是永久的,除非主动销毁这个全局变量。
② 局部变量:当退出函数时,局部变量即失去它们的价值,会随着函数调用的结束而被销毁。
★ 闭包的经典应用:
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div> <script>
var nodes = document.getElementsByTagName('div'); // 无论点击哪个div,结果都是5
// 因为div节点的onclick事件是被异步触发的,
// 当事件被触发时,for循环早已结束,此时变量i的值已经是5
// 所以在div的onclick事件函数中顺着作用域链从内到外查找变量i时,查找到的值总是5
for (var i = 0, len = nodes.length; i < len; i++) {
nodes[i].onclick = function() {
console.log(i);
}
}
</script>
☞ 解决方法:
for (var i = 0, len = nodes.length; i < len; i++) {
(function(i) {
nodes[i].onclick = function() {
console.log(i);
}
})(i);
}
在闭包的帮助下,把每次循环的
i
值都封闭起来。当在事件函数中顺着作用域链从内到外查找变量i
时,会先找到被封闭在闭包环境中的i
。(3)闭包的更多作用
① 封装变量
/*var mult = function() {
var a = 1;
for (var i = 0, len = arguments.length; i < len; i++) {
a = a * arguments[i];
}
return a;
};*/ // 改进1:
// 对于那些相同的参数来说,每次都进行计算是一种浪费
// 加入缓存机制来提高函数的性能 /*var cache = {};
var mult = function() {
var args = Array.prototype.join.call(arguments, ',');
if (cache[args]) {
return cache[args];
} var a = 1;
for (var i = 0, len = arguments.length; i < len; i++) {
a = a * arguments[i];
} return cache[args] = a;
}*/ // 继续改进2:减少页面中的全局变量
/*var mult = (function() {
var cache = {};
return function() {
var args = Array.prototype.join.call(arguments, ',');
if (cache[args]) {
return cache[args];
} var a = 1;
for (var i = 0, len = arguments.length; i < len; i++) {
a = a * arguments[i];
} return cache[args] = a;
}
})();*/ // 继续改进4:提炼函数是代码重构中的一种常见技巧
var mult = (function() {
var cache = {};
var calculate = function() {
var a = 1;
for (var i = 0, len = arguments.length; i < len; i++) {
a = a * arguments[i];
}
return a;
}; return function() {
var args = Array.prototype.join.call(arguments, ','); if (args in cache) {
return cache[args];
} return cache[args] = calculate.apply(null, arguments);
}
})(); console.log(mult(1, 2, 3, 4));
② 延续局部变量的寿命
// 把 img 变量用闭包封闭起来,解决请求丢失的问题
var report = (function() {
var imgs = [];
return function(src) {
var img = new Image();
imgs.push(img);
img.src = src;
}
})();
(4)闭包和面向对象设计
对象以方法的形式包含了过程,而闭包则是在过程中以环境的形式包含了数据。通常用面向对象能实现的功能,用闭包也能实现。反之亦然。
// 闭包实现
var extent = function() {
var value = 0;
return {
call: function() {
value++;
console.log(value);
}
}
}; var extent = extent();
extent.call(); // 1
extent.call(); // 2 // 面向对象写法1
var extent = {
value: 0,
call: function() {
this.value++;
console.log(this.value);
}
}; extent.call(); // 1
extent.call(); // 2 // 面向对象写法2
var Extent = function() {
this.value = 0;
}; Extent.prototype.call = function() {
this.value++;
console.log(this.value);
} var extent = new Extent();
extent.call(); // 1
extent.call(); // 2
(5)用闭包实现命令模式
在面向对象版本的命令模式中,预先植入的命令接收者被当成对象的属性保存起来;而在闭包版本的命令模式中,命令接收者会被封闭在闭包形成的环境中。
(6)闭包与内存管理
局部变量本来应该在函数退出的时候被解除引用,但如果局部变量被封闭在闭包形成的环境中,那么这个局部变量就能一直生存下去。从这个意义上,闭包的确会使一些数据无法被及时销毁。
使用闭包的一部分原因是我们选择主动把一些变量封闭在闭包中,因为可能在以后还需要使用这些变量,把这些变量方闭包中和放在全局环境中,对内存方面的影响是一致的,这里并不能说是内存泄漏。
如果将来要回收这些变量,可以手动把这些变量设为 null。
跟闭包和内存泄漏有关系的地方是,使用闭包的同时比较容易形成循环引用,如果闭包的作用域链中保存着一些DOM节点,这时候可能造成内存泄漏。但这本质上并非由闭包造成的。
同样,如果要解决循环引用带来的内存泄漏问题,我们只需要把循环引用中的变量设为 null 即可。
高阶函数
高阶函数是指至少满足下列条件之一的函数:
- 函数可以作为参数被传递;
- 函数可以作为返回值输出。
(1)函数作为参数传递
① 回调函数
回调函数的应用不仅只在异步请求中,当一个函数不适合执行一些请求时,我们也可以把这些请求封装成一个函数,并把它作为参数传递给另外一个函数,“委托”给另外一个函数来执行。
var appendDiv = function(callback) {
for (var i = 0; i < 100; i++) {
var div = document.createElement('div');
div.innerHTML = 1;
document.body.appendChild(div);
if (typeof callback === 'function') {
callback(div);
}
}
}; appendDiv(function(node) {
node.style.display = 'none';
});
② Array.prototype.sort
把用什么规则去排序(可变的)的部分封装在函数参数里,动态传入。
// 从小到大排序
[1, 4, 3].sort(function(a, b) {
return a - b;
});
(2)函数作为返回值输出
① 判断数据的类型
// 判断数据类型1
var isType = function(type) {
return function(obj) {
return Object.prototype.toString.call(obj) === '[object ' + type + ']';
}
}; var isString = isType('String');
var isArray = isType('Array');
var isNumber = isType('Number'); console.log(isArray([1, 2, 3])); // 判断数据类型2:循环语句,批量注册这些 isType 函数
var Type = {}; for (var i = 0, type; type = ['String', 'Array', 'Number'][i++];) {
(function(type) {
Type['is' + type] = function(obj) {
return Object.prototype.toString.call(obj) === '[object ' + type + ']';
}
})(type);
} Type.isArray([]);
Type.isString('str');
② getSingle
// 既把函数当作参数传递,又让函数执行后返回了另外一个函数
var getSingle = function(fn) {
var ret;
return function() {
return ret || (ret = fn.apply(this, arguments));
};
}; var getScript = getSingle(function() {
return document.createElement('script');
}); var script1 = getScript();
var script2 = getScript(); alert(script1 === script2); // true
(3) 高阶函数实现 AOP
AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。
好处:(1)可以保持业务逻辑模块的纯净和高内聚性;(2)可以很方便地服用日志统计等功能模块。
通常在 JavaScript 中实现 AOP,都是指把一个函数“动态织入”到另外一个函数之中。
Function.prototype.before = function(beforefn) {
var __self = this;
return function() {
beforefn.apply(this, arguments);
return __self.apply(this, arguments);
}
}; Function.prototype.after = function(afterfn) {
var __self = this;
return function() {
var ret = __self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
} var func = function() {
console.log(2);
}; func = func.before(function() {
console.log(1);
}).after(function() {
console.log(3);
}); func();
(4) 高阶函数的其他应用
① currying
currying 又称部分求值。一个 currying 的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。
var currying = function(fn) {
var args = []; return function() {
if (arguments.length === 0) {
return fn.apply(this, args);
} else {
[].push.apply(args, arguments);
return arguments.callee;
}
}
}; var cost = (function() {
var money = 0; return function() {
for (var i = 0, len = arguments.length; i < len; i++) {
money += arguments[i];
}
return money;
}
})(); var cost = currying(cost); // 转化成curring函数
cost(100);
cost(200);
cost(300); cost(); // 600
② uncurring
在 JavaScript 中,当我们调用对象的某个方法时,其实不用去关心该对象原本是否被设计为拥有这个方法,这是动态类型语言的特点(鸭子类型思想)。
同理,一个对象未必只能使用它自身的方法,可以让它去借用一个原本不属于它的方法。
// 实现1
Function.prototype.uncurrying = function() {
var self = this;
return function() {
var obj = Array.prototype.shift.call(arguments);
return self.apply(obj, arguments);
};
}; var push = Array.prototype.push.uncurrying();
var obj = {
'length': 1,
'0': 1
}; push(obj, 2);
console.log(obj); // 实现2
Function.prototype.uncurrying = function() {
var self = this;
return function() {
return Function.prototype.call.apply(arguments);
};
};
③ 函数节流
【函数被频繁调用的场景】:
- window.onresize 事件
- mousemove 事件
- 上传进度
【实现代码】:
// 原理:将即将被执行的函数用 setTimeout 延迟一段时间执行。
// 如果该次延迟执行还没有完成,则忽略接下来调用该函数的请求。
var throttle = function(fn, interval) {
var __self = fn, // 保存需要被延迟执行的函数引用
timer, // 定时器
firstTime = true; // 是否是第一次调用 return function() {
var args = arguments,
__me = this; if (firstTime) { // 如果是第一次调用,不需要延迟执行
__self.apply(__me, args);
return firstTime = false;
} if (timer) { // 如果定时器还在,说明前一次延迟执行还没有完成
return false;
} timer = setTimeout(function() {
clearTimeout(timer);
timer = null;
__self.apply(__me, args);
}, interval || 500);
};
};
④ 分时函数
// 原理:让创建节点的工作分批进行
// 比如把1秒钟创建1000个节点,改为每隔200毫秒创建8个节点
var timeChunk = function(arr, fn, count) {
var obj,
t; var len = arr.length; var start = function() {
for (var i = 0; i < Math.min(count || 1, arr.length); i++) {
var obj = arr.shift();
fn(obj);
}
}; return function() {
t = setInterval(function() {
if (arr.length === 0) { // 如果全部节点都已经被创建好
return clearInterval(t);
}
start();
}, 200); // 分批执行的时间间隔,也可以用参数的形式传入
};
};
⑤ 惰性加载函数
// 原理:在第一次进入条件分支之后,在函数内部会重写这个函数
// 重写之后就是我们期望的addEvent函数
// 在下一次进入addEvent函数的时候,addEvent函数里不再存在条件分支语句
var addEvent = function(elem, type, handler) {
if (window.addEventListener) {
addEvent = function(elem, type, handler) {
elem.addEventListener(type, handler, false);
}
} else if (window.attachEvent) {
addEvent = function(elem, type, handler) {
elem.attachEvent('on' + type, handler);
}
} addEvent(elem, type, handler);
};
小结
在 JavaScript 中,很多设计模式都是通过闭包和高阶函数实现的。相对于模式的实现过程,我们更关注的是模式可以帮助我们完成什么。
《JavaScript 设计模式与开发实战》第一部分(1、2、3章)笔记的更多相关文章
- 《JavaScript设计模式与开发实践》整理
最近在研读一本书<JavaScript设计模式与开发实践>,进阶用的. 一.高阶函数 高阶函数是指至少满足下列条件之一的函数. 1. 函数可以作为参数被传递. 2. 函数可以作为返回值输出 ...
- JavaScript设计模式与开发实践 - 观察者模式
概述 观察者模式又叫发布 - 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫做订阅者,将目标对象叫做 ...
- JavaScript设计模式与开发实践 - 策略模式
引言 本文摘自<JavaScript设计模式与开发实践> 在现实中,很多时候也有多种途径到达同一个目的地.比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路. 如果没有时间但 ...
- 《Javascript设计模式与开发实践》--读书笔记
第2章 this call apply bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用. bind( ...
- JavaScript设计模式与开发实践 - 单例模式
引言 本文摘自<JavaScript设计模式与开发实践> 在传统开发工程师眼里,单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返 ...
- 《javascript设计模式与开发实践》--- (单一职责原则)
看的这本书叫<JavaScript设计模式与开发实践> 先规划一下看书的顺序,基础知识我已经大概的浏览了一遍了,没有留下笔记,以后有时间还会补上.本来打算顺着看的.但是我感觉我很难短时间内 ...
- Javascript设计模式理论与实战:工厂方法模式
本文从简单工厂模式的缺点说起,引入工厂方法模式,介绍的工厂方法模式的基本知识,实现要点和应用场景,最后举例进行说明工厂方法模式的应用.在之前的<Javascript设计模式理论与实战:简单工厂模 ...
- JavaScript设计模式与开发实践——读书笔记1.高阶函数(上)
说来惭愧,4个多月未更新了.4月份以后就开始忙起来了,论文.毕设.毕业旅行等七七八八的事情占据了很多时间,毕业之后开始忙碌的工作,这期间一直想写博客,但是一直没能静下心写.这段时间在看<Java ...
- 《JavaScript设计模式与开发实践》读书笔记-基础知识
笔记内容多摘录自<JavaScript设计模式与开发实践>(曾探著),侵删. 面向对象的JavaScript 1. 动态需要类型和鸭子类型 鸭子类型 如果它走起路来像鸭子,叫起来也是鸭子, ...
随机推荐
- AJAX跨域问题以及解决思路(更新中)
跨域的三大原因(同时满足) 浏览器限制 跨域 XHR请求 解决思路: 让浏览器不做限制,指定参数,让浏览器不做校验,但该方法不太合理,它需要每个人都去做改动. 不要发出XHR请求,这样就算是跨域,浏览 ...
- oracle 老用户数据导入新建用户
$sqlplus/nolog #使用PLSQL进入oracleSQL>conn/as sysdba #进入sysdba权限CREATE DIRECTORY datadir1 AS ' ...
- 搭建Elasticsearch平台
https://cloud.tencent.com/developer/article/1189282 https://blog.csdn.net/qq_34021712/article/detail ...
- Vue的介绍及基础指令
一.什么是Vue Vue.js是一个渐进式 JavaScript 框架 通过对框架的了解与运用程度,来决定其在整个项目中的应用范围,最终可以独立以框架方式完成整个web前端项目 为什么要学习Vue 三 ...
- Ngnix配置
server { listen 80; server_name www.local.test; root /data/workspace; index index.php index.html in ...
- 导出html table 数据到Excel
其实只需要复制 粘贴.... <script type="text/javascript" src="http://code.jquery.com/jquery- ...
- 拼接字符串,生成tree格式的JSON数组
之前做的执法文书的工作,现在需要从C#版本移植到网页版,从Thrift接口获取数据,加载到对应的控件中 之前用的easyui的Tree插件,通过<ul><li><span ...
- 二: drf视图
Django REST framwork 提供的视图的主要作用: 控制序列化器的执行(检验.保存.转换数据) 控制数据库查询的执行 一:请求与响应 1:request Request 1 REST f ...
- Windows Server 2008环境下Apache2.4+Tomcat8配置
安装步骤 1. 安装配置JDK2. 安装配置Apache3. 安装配置Tomcat4. 启动服务并测试 一.Apache安装与配置 1.Apache解压在D盘根目录下建立一个文件夹Apache Gro ...
- vue的技巧代码
转载:https://segmentfault.com/a/1190000014085613 第一招:化繁为简的Watchers 场景还原: created(){ this.fetchPostList ...