深入浅出KnockoutJS
深入浅出KnockoutJS
写在前面,本文资料大多来源网上,属于自己的学习笔记整理。
其中主要内容来自learn.knockoutjs.com,源码解析部分资料来自司徒正美博文《knockout.js学习笔记》系列。
1. Knockout初体验
1.1 Before Knockout
<div id=”itemName”></div>
<input type=”text” id=”itemNameEdit”/>
使用JQuery,上述交互逻辑可以如下实现
var item = {
id: 88,
name: "Apple Pie"
};
$("#itemName").text(item.name);
$("#itemNameEdit").val(item.name).change(function() {
item.name = $(this).val();
$("#itemName").text(item.name);
});
采用这种方式的缺点
- 当UI和data的交互越来越多时,代码量迅速增长到难以维护
- •Dom Query Based
- 上述代码耦合度高,不可重用
- Id、classname命名难以管理
1.2 Use Knockout
HTML View如下
<div data-bind=”text:name”></div>
<input type=”text” data-bind=”value:name”/>
Javascript如下
function ViewModel={
this.id=88;
this.name=ko.observable(“Apple”);
};
ko.applyBindings(new ViewModel());
现在,当输入框中值发生变化时,div中显示的值也会自动发送变化
2. Knockout基础
2.1 MVVM模式
Knockoutjs遵循Model(M)—View(V)—ViewModel(VM)模式
2.2 单次绑定
从ViewModel绑定至UI这一层只进行一次绑定,不追踪数据在任何一方的变化,适用于数据展现
Javascript与Html示例如下
function AppViewModel() {
this.firstName = "Bert";
this.lastName = "Bertington";
}
ko.applyBindings(new AppViewModel());
<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>
效果如下图所示
2.3 双向绑定
无论数据在ViewModel或者是UI中变化,将会更新另一方,最为灵活的绑定方式,同时代价最大
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
}
ko.applyBindings(new AppViewModel());
<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
上述绑定,当输入框中值发生改变时,<p>标签中显示内容相应发生改变
2.4 依赖绑定
以其它observable的值为基础来组成新的值,新值也是双向绑定的
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington"); this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
}
ko.applyBindings(new AppViewModel());
<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<p>Full name: <strong data-bind="text: fullName"></strong></p>
上述代码示例中,fullName依赖于firstName和lastName,改变firstName和lastName任意值,fullName的显示也相应改变
2.5 绑定数组
可以为属性绑定集合
// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
var self = this;
self.name = name;
self.meal = ko.observable(initialMeal);
} // Overall viewmodel for this screen, along with initial state
function ReservationsViewModel() {
var self = this; // Non-editable catalog data - would come from the server
self.availableMeals = [
{ mealName: "Standard (sandwich)", price: 0 },
{ mealName: "Premium (lobster)", price: 34.95 },
{ mealName: "Ultimate (whole zebra)", price: 290 }
]; // Editable data
self.seats = ko.observableArray([
new SeatReservation("Steve", self.availableMeals[0]),
new SeatReservation("Bert", self.availableMeals[0])
]);
} ko.applyBindings(new ReservationsViewModel());
<h2>Your seat reservations</h2> <table>
<thead><tr>
<th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
</tr></thead>
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: meal().mealName"></td>
<td data-bind="text: meal().price"></td>
</tr>
</tbody>
</table>
上述代码将seats对象绑定了一个集合对象,在html view中,通过foreach指令渲染视图,效果如下下图
2.6 增加添加和删除元素功能
// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
var self = this;
self.name = name;
self.meal = ko.observable(initialMeal); self.formattedPrice = ko.computed(function() {
var price = self.meal().price;
return price;
});
} // Overall viewmodel for this screen, along with initial state
function ReservationsViewModel() {
var self = this; // Non-editable catalog data - would come from the server
self.availableMeals = [
{ mealName: "Standard (sandwich)", price: 0 },
{ mealName: "Premium (lobster)", price: 34.95 },
{ mealName: "Ultimate (whole zebra)", price: 290 }
]; // Editable data
self.seats = ko.observableArray([
new SeatReservation("Steve", self.availableMeals[0]),
new SeatReservation("Bert", self.availableMeals[0])
]); // Operations
self.addSeat = function() {
self.seats.push(new SeatReservation("", self.availableMeals[0]));
}
self.removeSeat = function(seat) { self.seats.remove(seat) }
} ko.applyBindings(new ReservationsViewModel());
<h2>Your seat reservations</h2> <table>
<thead><tr>
<th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
</tr></thead>
<tbody data-bind="foreach: seats">
<tr>
<td><input data-bind="value: name" /></td>
<td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>
<td data-bind="text: formattedPrice"></td>
<td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
</tr>
</tbody>
</table> <button data-bind="click: addSeat">Reserve another seat</button>
- 上述代码中,为viewmodel添加了addSeat和removeSeat方法。
- 调用addSeat方法时,为seats集合添加一个初始化SeatReservation对象
- 调用removeSeat方法时,knockout将当前dom元素绑定的seat对象作为参赛传入到方法中
效果如图
3. Knockout进阶
3.1 Custom bindings
Binding连接view和viewmodel,除了内置bindings,你可以创建自己的binding
- 将待注册的绑定,添加为ko.bindingHandlers的属性,然后可以在任意dom元素中使用它
ko.bindingHandlers.yourBindingName = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called once when the binding is first applied to an element,
// and again whenever any observables/computeds that are accessed change
// Update the DOM element based on the supplied values here.
}
};
<div data-bind="yourBindingName: someValue"> </div>
custom binding示例
// ----------------------------------------------------------------------------
// Reusable bindings - ideally kept in a separate file ko.bindingHandlers.fadeVisible = {
init: function(element, valueAccessor) {
// Start visible/invisible according to initial value
var shouldDisplay = valueAccessor();
$(element).toggle(shouldDisplay);
},
update: function(element, valueAccessor) {
// On update, fade in/out
var shouldDisplay = valueAccessor();
shouldDisplay ? $(element).fadeIn() : $(element).fadeOut();
}
}; // ----------------------------------------------------------------------------
// Page viewmodel function Answer(text) { this.answerText = text; this.points = ko.observable(1); } function SurveyViewModel(question, pointsBudget, answers) {
this.question = question;
this.pointsBudget = pointsBudget;
this.answers = $.map(answers, function(text) { return new Answer(text) });
this.save = function() { alert('To do') }; this.pointsUsed = ko.computed(function() {
var total = 0;
for (var i = 0; i < this.answers.length; i++)
total += this.answers[i].points();
return total;
}, this);
} ko.applyBindings(new SurveyViewModel("Which factors affect your technology choices?", 10, [
"Functionality, compatibility, pricing - all that boring stuff",
"How often it is mentioned on Hacker News",
"Number of gradients/dropshadows on project homepage",
"Totally believable testimonials on project homepage"
]));
<h3 data-bind="text: question"></h3>
<p>Please distribute <b data-bind="text: pointsBudget"></b> points between the following options.</p> <table>
<thead><tr><th>Option</th><th>Importance</th></tr></thead>
<tbody data-bind="foreach: answers">
<tr>
<td data-bind="text: answerText"></td>
<td><select data-bind="options: [1,2,3,4,5], value: points"></select></td>
</tr>
</tbody>
</table> <h3 data-bind="fadeVisible: pointsUsed() > pointsBudget">You've used too many points! Please remove some.</h3>
<p>You've got <b data-bind="text: pointsBudget - pointsUsed()"></b> points left to use.</p>
<button data-bind="enable: pointsUsed() <= pointsBudget, click: save">Finished</button>
上述代码定义了一个fadeVisible绑定,用来控制元素显示动画效果。init方法根据dom元素传入参数当前状态设置初始显示效果;update方法在pointsUsed 每次发生更新时触发,更新元素显示效果
3.2 Template binding
- Native templating:内置,用于加强控制流程的绑定
- String-based templating:集成第三方模板引擎的方式,原理是将model value传递给第三方模板引擎,将结果字符串注入到当前document
Native templating示例
<h2>Participants</h2>
Here are the participants:
<div data-bind="template: { name: 'person-template', data: buyer }"></div>
<div data-bind="template: { name: 'person-template', data: seller }"></div> <script type="text/html" id="person-template">
<h3 data-bind="text: name"></h3>
<p>Credits: <span data-bind="text: credits"></span></p>
</script> <script type="text/javascript">
function MyViewModel() {
this.buyer = { name: 'Franklin', credits: 250 };
this.seller = { name: 'Mario', credits: 5800 };
}
ko.applyBindings(new MyViewModel());
</script>
3.3 Components and Custom Elements
组件是将UI代码组织成可复用模块的方法
使用ko.components.register方法注册组件,组件定义包含viewModel和template
ko.components.register('some-component-name', {
viewModel: <see below>,
template: <see below>
});
一个like/dislike组件示例
ko.components.register('like-widget', {
viewModel: function(params) {
// Data: value is either null, 'like', or 'dislike'
this.chosenValue = params.value; // Behaviors
this.like = function() { this.chosenValue('like'); }.bind(this);
this.dislike = function() { this.chosenValue('dislike'); }.bind(this);
},
template:
'<div class="like-or-dislike" data-bind="visible: !chosenValue()">\
<button data-bind="click: like">Like it</button>\
<button data-bind="click: dislike">Dislike it</button>\
</div>\
<div class="result" data-bind="visible: chosenValue">\
You <strong data-bind="text: chosenValue"></strong> it\
</div>'
}); function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
} function MyViewModel() {
this.products = [
new Product('Garlic bread'),
new Product('Pain au chocolat'),
new Product('Seagull spaghetti', 'like') // This one was already 'liked'
];
} ko.applyBindings(new MyViewModel());
<ul data-bind="foreach: products">
<li class="product">
<strong data-bind="text: name"></strong>
<like-widget params="value: userRating"></like-widget>
</li>
</ul>
viewModel中,为products单项绑定了一个Product集合,并为第三个Product对象userRating属性设置为like
html view中,使用like-widget指令使用上述定义的组件
效果如下图
4. Knockout实战
4.1 knockout版todo app
- 需要一个todo对象作为 Model
- 需要一个todos 的集合用来存储各个todo对象
- 需要filterTodos对象,根据All,Active,Completed过滤todos集合
- 需要添加、删除、编辑、清除等各种事件方法
4.2 todo app主要代码分析
- Todo Model,包含3 个属性分别是title,completed,editing
// represent a single todo item
var Todo = function (title, completed) {
this.title = ko.observable(title);
this.completed = ko.observable(completed);
this.editing = ko.observable(false);
};
- todos Array、filteredTodos
// map array of passed in todos to an observableArray of Todo objects
this.todos = ko.observableArray(todos.map(function (todo) {
return new Todo(todo.title, todo.completed);
})); // store the new todo value being entered
this.current = ko.observable(); this.showMode = ko.observable('all'); this.filteredTodos = ko.computed(function () {
switch (this.showMode()) {
case 'active':
return this.todos().filter(function (todo) {
return !todo.completed();
});
case 'completed':
return this.todos().filter(function (todo) {
return todo.completed();
});
default:
return this.todos();
}
}.bind(this));
- Events binding
- Custom binding
提供了对键盘回车键ENTER_KEY、取消键ESCAPE_KEY的事件绑定
当为dom元素绑定enter_key、escape_key事件时,会以当前dom元素作用域执行赋予的valueAccessor函数
在selectAndFocus自定义绑定中,同时定义了init方法和update方法
在init中为dom元素注册了foucs方法,在update方法中来触发元素的focus,其目的是为了在选中todo元素,可以立即进入可编辑的状态
function keyhandlerBindingFactory(keyCode) {
return {
init: function (element, valueAccessor, allBindingsAccessor, data, bindingContext) {
var wrappedHandler, newValueAccessor; // wrap the handler with a check for the enter key
wrappedHandler = function (data, event) {
if (event.keyCode === keyCode) {
valueAccessor().call(this, data, event);
}
}; // create a valueAccessor with the options that we would want to pass to the event binding
newValueAccessor = function () {
return {
keyup: wrappedHandler
};
}; // call the real event binding's init function
ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, data, bindingContext);
}
};
} // a custom binding to handle the enter key
ko.bindingHandlers.enterKey = keyhandlerBindingFactory(ENTER_KEY); // another custom binding, this time to handle the escape key
ko.bindingHandlers.escapeKey = keyhandlerBindingFactory(ESCAPE_KEY); // wrapper to hasFocus that also selects text and applies focus async
ko.bindingHandlers.selectAndFocus = {
init: function (element, valueAccessor, allBindingsAccessor, bindingContext) {
ko.bindingHandlers.hasFocus.init(element, valueAccessor, allBindingsAccessor, bindingContext);
ko.utils.registerEventHandler(element, 'focus', function () {
element.focus();
});
},
update: function (element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor()); // for dependency
// ensure that element is visible before trying to focus
setTimeout(function () {
ko.bindingHandlers.hasFocus.update(element, valueAccessor);
}, 0);
}
};
- HTML View
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" data-bind="value: current, enterKey: add" placeholder="What needs to be done?" autofocus>
</header>
<section id="main" data-bind="visible: todos().length">
<input id="toggle-all" data-bind="checked: allCompleted" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-bind="foreach: filteredTodos">
<li data-bind="css: { completed: completed, editing: editing }">
<div class="view">
<input class="toggle" data-bind="checked: completed" type="checkbox">
<label data-bind="text: title, event: { dblclick: $root.editItem }"></label>
<button class="destroy" data-bind="click: $root.remove"></button>
</div>
<input class="edit" data-bind="value: title, enterKey: $root.saveEditing,
escapeKey: $root.cancelEditing, selectAndFocus:editing, event: { blur: $root.cancelEditing }">
</li>
</ul>
</section>
<footer id="footer" data-bind="visible: completedCount() || remainingCount()">
<span id="todo-count">
<strong data-bind="text: remainingCount">0</strong>
<span data-bind="text: getLabel(remainingCount)"></span> left
</span>
<ul id="filters">
<li>
<a data-bind="css: { selected: showMode() == 'all' }" href="#/all">All</a>
</li>
<li>
<a data-bind="css: { selected: showMode() == 'active' }" href="#/active">Active</a>
</li>
<li>
<a data-bind="css: { selected: showMode() == 'completed' }" href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" data-bind="visible: completedCount, click: removeCompleted">
Clear completed (<span data-bind="text: completedCount"></span>)
</button>
</footer>
</section>
5. Knockout源码解析
5.1 ko.observable是什么
this.firstName=ko.observable(“Bert”);
this.firstName();
this.firstName(“test”);
调用上面代码发生了什么
$.observable = function(value){
var v = value;//将上一次的传参保存到v中,ret与它构成闭包
function ret(neo){
if(arguments.length){ //setter
if(v !== neo ){
v = neo;
}
return ret;
}else{ //getter
return v;
}
}
return ret
}
5.2 ko.computed是什么
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
$.computed = function(obj, scope){
//computed是由多个$.observable组成
var getter, setter
if(typeof obj == "function"){
getter = obj
}else if(obj && typeof obj == "object"){
getter = obj.getter;
setter = obj.setter;
scope = obj.scope;
}
var v
var ret = function(neo){
if(arguments.length ){
if(typeof setter == "function"){//setter不一定存在的
if(v !== neo ){
setter.call(scope, neo);
v = neo;
}
}
return ret;
}else{
v = getter.call(scope);
return v;
}
}
return ret;
}
5.3 属性依赖如何实现
调用observable中getter方法时,ret函数对象收集所有对自身的依赖对象
调用observable中setter方法时,ret函数对象想依赖对象发生通知
调用computed中getter方法时,ret函数对象将自身传递给依赖探测的begin方法
然后通过call()方法获取函数值,这时,会触发observable中相对应的getter的调用,从而收集到computed中的ret函数对象
在调用完成后,再将自身移除
$.dependencyDetection = (function () {
var _frames = [];
return {
begin: function (ret) {
_frames.push(ret);
},
end: function () {
_frames.pop();
},
collect: function (self) {
if (_frames.length > 0) {
self.list = self.list || [];
var fn = _frames[_frames.length - 1];
if ( self.list.indexOf( fn ) >= 0)
return;
self.list.push(fn);
}
}
};
})();
$.valueWillMutate = function(observable){
var list = observable.list
if($.type(list,"Array")){
for(var i = 0, el; el = list[i++];){
el();
}
}
}
5.4 双向绑定如何实现
The name is <span data-bind="text: fullName" id="node"></span>
$.buildEvalWithinScopeFunction = function (expression, scopeLevels) {
var functionBody = "return (" + expression + ")";
for (var i = 0; i < scopeLevels; i++) {
functionBody = "with(sc[" + i + "]) { " + functionBody + " } ";
}
return new Function("sc", functionBody);
}
$.applyBindings = function(model, node){ var nodeBind = $.computed(function (){
var str = "{" + node.getAttribute("data-bind")+"}"
var fn = $.buildEvalWithinScopeFunction(str,2);
var bindings = fn([node,model]);
for(var key in bindings){
if(bindings.hasOwnProperty(key)){
var fn = $.bindingHandlers["text"]["update"];
var observable = bindings[key]
$.dependencyDetection.collect(observable);//绑定viewModel与UI
fn(node, observable)
}
}
},node);
return nodeBind }
$.bindingHandlers = {}
$.bindingHandlers["text"] = {
'update': function (node, observable) {
var val = observable()
if("textContent" in node){
node.textContent = val;
}
}
}
window.onload = function(){
var model = new MyViewModel();
var node = document.getElementById("node");
$.applyBindings(model, node);
}
上述代码中,$.buildEvalWithinScopeFunction(str,2)返回一个匿名函数
function anonymous(sc/**/) {with(sc[1]) { with(sc[0]) { return ({text: fullName}) } } }
通过var bindings = fn([node,model]),bindings得到一个{text:fullName函数对象}的对象,其中,fullName是一个组合依赖属性,即fullName是一个computed中ret函数对象
6. 总结
6.1 优点
- 专注于data-binding,UI自动刷新,model依赖跟踪
- 简单易上手,学习成本低
- 轻量,方便与其他第三方JS框架集成
- 可扩展,支持自定义定制
- 浏览器兼容度高,几乎支持所有现代浏览器
6.2 不足
- 是一个MVVM library,不是一个前端解决方案
- 缺少Router等重要模块支持
- 缺少可测试性支持
参考资料
如果您觉得本文对您有帮助,请【推荐】,谢谢。
深入浅出KnockoutJS的更多相关文章
- 【Knockout.js 学习体验之旅】(2)花式捆绑
本文是[Knockout.js 学习体验之旅]系列文章的第2篇,所有demo均基于目前knockout.js的最新版本(3.4.0).小茄才识有限,文中若有不当之处,还望大家指出. 目录: [Knoc ...
- 【Knockout.js 学习体验之旅】(1)ko初体验
前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析2--奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 深入浅出Struts2+Spring+Hibernate框架
一.深入浅出Struts2 什么是Struts2? struts2是一种基于MVC的轻量级的WEB应用框架.有了这个框架我们就可以在这个框架的基础上做起,这样就大大的提高了我们的开发效率和质量,为公司 ...
- DOM 事件深入浅出(二)
在DOM事件深入浅出(一)中,我主要给大家讲解了不同DOM级别下的事件处理程序,同时介绍了事件冒泡和捕获的触发原理和方法.本文将继续介绍DOM事件中的知识点,主要侧重于DOM事件中Event对象的属性 ...
- DOM 事件深入浅出(一)
在项目开发时,我们时常需要考虑用户在使用产品时产生的各种各样的交互事件,比如鼠标点击事件.敲击键盘事件等.这样的事件行为都是前端DOM事件的组成部分,不同的DOM事件会有不同的触发条件和触发效果.本文 ...
- 翻译:使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 6 - 业务逻辑
Part 3: 设计逻辑层:核心开发 如前所述,我们的解决方案如下所示: 下面我们讨论整个应用的结构,根据应用中不同组件的逻辑相关性,分离到不同的层中,层与层之间的通讯通过或者不通过限制.分层属于架构 ...
- 翻译:使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 4 - 验证
验证: 快要完成我们程序的界面部分了.剩下的事情就是在用户点击 "保存" 的时候管理验证问题了.验证是主要需求,今天就是最无知的应用也不会忽视它.通过正确的验证,用户可以知道应该输 ...
随机推荐
- Java使用HttpURLConnection上传文件
从普通Web页面上传文件非常easy.仅仅须要在form标签叫上enctype="multipart/form-data"就可以,剩余工作便都交给浏览器去完毕数据收集并发送Http ...
- 我工作这几年(五)-- Android学习4.5月总结(一)
今年是对我个人成长和程序员生涯冲击很大的一年. 有了小孩之后,家里发生了太多太多的事情,现在已经慢慢步入正轨,还好撑过来了,当然还有老婆.岳父岳母.我爸妈.还有好多关心支持我的人的帮助.在各种挫折交替 ...
- D2010 RTTI + Attribute 简单实现ORM
还记得David I 今年四月来盛大时,被问及“反射机制能再做得好一点吗?我们想放弃RTTI”,David I 回答“这的确是需要考虑的地方,当然RTTI我们不会放弃的”.(这个白胡子的老哥哥还真很可 ...
- span标签可以使用hide()方法隐藏吗?
/获取li下的span var $span = $('ul.selector li span'); //span对象隐藏 $span.hide(); //或者 $span.css('display', ...
- 《Head First 设计模式》学习笔记——模板方法模式
模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以详细方法以及详细构造函数的形式实现.然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类能够以不同的方式实现这些抽象方法,从而对剩余的逻辑有 ...
- 理解Lambda表达式
1.什么是Lambda表达式 Lambda表达式是一个匿名方法,通常在LINQ中被用来创建委托 简单来说.它是一个没有声明,没有访问修饰符,没有返回值.甚至没有名字的方法. 2.为什么我们需要使用La ...
- dokcer 运行和进入容器
<pre name="code" class="html">docker:/root# docker run -itd --name zjtest8 ...
- docker 私有仓库查询
docker:/etc/init.d# docker ps -a | grep reg c7d5592a3658 registry "docker-registry" 11 min ...
- java之jvm学习笔记三(Class文件检验器)
java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...
- Linux 利用hosts.deny 防止暴力破解ssh(转)
一.ssh暴力破解 利用专业的破解程序,配合密码字典.登陆用户名,尝试登陆服务器,来进行破解密码,此方法,虽慢,但却很有效果. 二.暴力破解演示 2.1.基础环境:2台linux主机(centos 7 ...