夜间模式暗黑模式
字体
阴影
滤镜
圆角

Category: Develop

10 篇文章

thumbnail
JavaScript&C++ Interaction Based on V8 Engine
V8也是必须得过却不容易过的一道坎啊。 Data & Template V8中实现了JavaScript和C++之间数据和函数的相互调用。但是应当想到原生C++的数据类型和JavaScript中的数据类型还是很不一样的。这里V8相当于一个桥梁作用,提供了Value Class,在JavaScript和C++进行相互调用数据的时候就通过Value Class进行,比如说: Handle<Value> Add(const Arguments& args) { int a = args[0]->Uint32Value(); int b = args[1]->Uint32Value(); return Integer::New(a + b); } 对于JavaScript对象和函数来说,在V8中是通过两个模板类Template进行定义(并不是C++里的模板,别搞混了)。一个是对象模板ObjectTemplate,另一个是FunctionTemplate函数模板,分别定义JavaScript的对象和函数。通过这两个模板类也可以将C++中的对象和函数暴露给JavaScript脚本环境。 Interaction 对于变量相互调用来说,变量首先可以参照这样的方式来定义getter/setter: static char sname[512] = {0}; static Handle<Value> NameGetter(Local<String> name, const AccessorInfo& info) { return String::New((char*)&sname, strlen((char*)&sname)); } static void NameSetter(Local<String> name, Local<Value> value, const AccessorInfo& info) { Local<String> str = value->ToString(); str->WriteAscii((char*)&sname); } 然后要把它们注册到Global上: Handle<ObjectTemplate> global = ObjectTemplate::New(); // 为全局对象创建一个template global->SetAccessor(String::New("name"), NameGetter, NameSetter); // 把getter和setter注册到global中 对于函数来说也可以照猫画虎: // 在C++中 定义原型函数 Handle<Value> func(const Argument& args){ return xxx; } // 注册到global 公开给脚本环境 global->Set(String::New("func"), FunctionTemplate::New(func)); C++的类是个一言难尽的东西,相较于JavaScript这种动态性比较强的语言来说,实在是显得不太“方便灵活”。可以认为V8实现了一个任务,就是把复杂的C++对象封装好来给JavaScript使用。从这个角度来说,可以不用太过纠结于C++的繁杂语法,但也能享受到“系统语言级的效率”,同时也能继续享受JavaScript的灵活多变: // Example C++ class class Person { private: unsigned int age; char name[512]; public: Person(unsigned int age, char *name) { this->age = age; strncpy(this->name, name, sizeof(this->name)); } // getter unsigned int getAge() { return this->age; } // setter void setAge(unsigned int nage) { this->age = nage; } // getter char *getName() { return this->name; } // setter void setName(char *nname) { strncpy(this->name, nname, sizeof(this->name)); } } 上面就是一个简单的Person类实现了对应的getter和setter方法,对于包装来说,不仅需要对getter和setter进行包装,也要对构造函数进行包装: Handle<Value> PersonConstructor(const Argument& args){ Handle<Object> object = args.This(); HandleScope handle_scope; int age = args[0]->Uint32Value(); String::Utf8Value str(args[1]); char* name…
thumbnail
JavaScript Event Loop
这篇当做Jake Archibald在2018 JSConf Asia和Philip Roberts在2014 JSConf EU讲的Event Loop笔记,视频在Youtube上可以搜到。 引子 A single-threaded non-blocking asynchronous concurrent language —— JavaScriptWhat the heck is the event loop anyway? | Philip Roberts at 2014 JSConf EU JavaScript是单线程非阻塞的。单线程很容易理解,因为用户在浏览网页的时候常常要进行页面交互,以及JavaScript背后也会进行一些操作DOM的操作。要是搞成多线程,就会出现DOM操作的条件竞争。所以还是干脆一点弄成单线程就好。 当然也有“多线程”的解决方案,Web Worker就是其中一个,但是用Worker可以操作的东西是有限制的,而且Worker一多起来资源开销也是很雷普的。(并不是这篇文章的主题,就略过了) 非阻塞是JavaScript中一个有趣的点,像JavaScript Runtime中(比如V8),存在一个堆空间用来存数据,也有一个栈空间来存储函数的调用。但是说到异步非阻塞,粗略看看V8的源码也并没有类似诸如setTimeout、DOM操作还有HTTP请求这样的东西在,也没有和异步相关的东西。这其实很有意思——我的意思是JavaScript非阻塞实现很有意思。 这种“有意思”,主要还是在于“从0开始的非阻塞实现”这方面的有意思。JavaScript是单线程,也意味着单一call stack。 Call Stack function multiply(a, b) { return a * b; } function square(n) { return multiply(n, n); } function printSquare(n) { var squared = square(n); console.log(squared); } // 一切的开始 entry point printSquare(4); 稍稍有点Binary的基础就很能理解call stack的压栈弹栈的操作。对于单线程来说,所谓的阻塞可以理解为在某个阶段,或者说某个函数中,代码需要执行很久。造成了用户在界面交互的时候卡顿的现象。 var foo = $.getSync('//foo.com'); var bar = $.getSync('//bar.com'); var qux = $.getSync('//qux.com'); // 每一次getSync请求 我都得等一会儿 不能干其他事情 console.log(foo); console.log(bar); console.log(qux); 但是对于诸如setTimeout这样的异步回调函数,它在一般的调用栈上有着异于其他函数的表现。对于setTimeout(callback, ms)来说,setTimeout仅会短时间在调用栈上出现一下,随后很快就消失: console.log('hi'); setTimeout(function () { console.log('there'); }, 5000); console.log('JSConfEU'); 等到所有的代码都执行完,5秒过去后就会在调用栈上出现console.log('there'),然后就消失了。这对于单线程来说是匪夷所思的。能够解释这种现象的东西,就得谈谈Event Loop了。 Event Loop JavaScript能够实现“并发”,其实是浏览器所赋予的能力。像setTimeout是浏览器提供的API,而不是存在于V8源码中的东西。 承接Call Stack中讲的东西,Web API会帮JavaScript承包计时器Handler,当时间到了,就会把callback回调函数放到所谓task queue任务队列中。 这个时候Event Loop登场了,事件循环干的事情其实也非常简单。它会查看call stack和task queue中的内容,如果call stack空了,就把task queue里面排在第一个的task塞到call stack里面去。call stack是V8的管辖领域,所以之后就按照原来的“常识”继续工作。如果call stack中有东西,并且task queue中也有东西,事件循环会等到call stack里面的东西全部执行完了在把task queue中的东西塞到调用栈。 现在也可以理解一下Ajax的请求流程,当代码执行到xhr请求时,先扔给浏览器处理handler。这里就是接受返回的响应信息(比如json),接受完就扔到任务队列里面。然后重复上面setTimeout的流程。 像比较常见的onClick事件,通常是在代码执行到定义回调函数的地方,把监听handler一直放在浏览器里,每次点击按钮的时候,就会扔一个事件到任务队列里面。 页面渲染也是浏览器的任务,理想情况下1秒钟渲染60次(满足绝大多数显示器的刷新频率)。渲染也可以看做一种callback,也要等待调用栈清空以后执行。不同地方在于render的优先级更高,也就是所render queue排在callback queue的前面。普通的callback要等render queue清空以后才能得到执行。 macro/micro Task macrotask:包含执行整体的js代码,事件回调,XHR回调,定时器(setTimeout/setInterval/setImmediate),IO操作,UI rendermicrotask:更新应用程序状态的任务,包括promise回调,MutationObserver,process.nextTick,Object.observe宏任务和微任务 macro task宏任务和micro task微任务是规范里面的内容。通俗点来说,微任务是由宏任务产生的,但是微任务的执行优先级却高于宏任务。可以认为微任务紧紧跟在宏任务的屁股后面,在微任务的后面是其他的宏任务。 render视图渲染是在本轮事件循环的微任务队列执行完后执行,执行任务的耗时会影响视图渲染的时间。 1、执行宏任务队列的一个任务2、执行完当前微任务队列的所有任务3、视图渲染事件循环的步骤 视图渲染也不是必然会执行的步骤,浏览器的优化策略有可能将几次视图更新累计到一起进行渲染,执行渲染时会调用requestAnimationFrame回调函数执行重绘。 Loop in Node.js Node.js和浏览器又有点不太一样,它搞了一套自己的模型。node中实现Event Loop主要依赖libuv。在较新的node版本中(指大于11版本号),事件循环模型是这样的: ┌───────────────────────┐ ┌─>│ timers │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ I/O callbacks │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ idle, prepare │ │ └──────────┬────────────┘ ┌───────────────┐ │ ┌──────────┴────────────┐ │ incoming: │ │ │…
thumbnail
浅析双向绑定原理
分析任何原理,看源代码就行了。 0x01 几种实现双向绑定的方法 主流的mvc/mvvm框架都实现了单向数据绑定,双向绑定无非就是在单向绑定的基础上给可输入元素添加了change(input)事件监听,来动态修改model/view。 数据绑定的做法: 发布者-订阅者模式(backbone.js)脏值检查(angular.js)数据劫持(vue.js) 发布者-订阅者模式 发布订阅模式属于广义上的观察者模式 发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式 发布订阅模式多了个事件通道 在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应。 ╭─────────────╮ Fire Event ╭──────────────╮│             │─────────────>│             ││   Subject   │             │   Observer   ││             │<─────────────│             │╰─────────────╯ Subscribe   ╰──────────────╯​ 在发布订阅模式中,发布者和订阅者之间多了一个发布通道;一方面从发布者接收事件,另一方面向订阅者发布事件;订阅者需要从事件通道订阅事件 以此避免发布者和订阅者之间产生依赖关系 简单实现 var shoeObj = {}; // 定义发布者shoeObj.list = []; // 缓存列表 存放订阅者回调函数        // 增加订阅者shoeObj.listen = function(fn) {    shoeObj.list.push(fn);  // 订阅消息添加到缓存列表}​// 发布消息shoeObj.trigger = function(){    for(var i = 0,fn; fn = this.list[i++];) {        fn.apply(this,arguments);   }}// 小红订阅如下消息shoeObj.listen(function(color,size){    console.log("颜色是:"+color);    console.log("尺码是:"+size);  });​// 小花订阅如下消息shoeObj.listen(function(color,size){    console.log("再次打印颜色是:"+color);    console.log("再次打印尺码是:"+size); });shoeObj.trigger("红色",40);shoeObj.trigger("黑色",42); 脏值检查 angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过 setInterval() 定时轮询检测数据变动,当然Google不会这么low,angular只有在指定的事件触发时进入脏值检测,大致如下: DOM事件,譬如用户输入文本,点击按钮等。( ng-click )XHR(XMLHttpRequest)响应事件 ( $http )浏览器Location变更事件 ( $location )Timer事件( timeout, interval )执行digest()或apply() 数据劫持 vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。 0x02 Object.defineProperty() Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 Document 0x03 双向绑定实现 要实现mvvm的双向绑定,就必须要实现以下几点: 1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者 2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图 4、mvvm入口函数,整合以上三者 Observer var data = {name: 'mashiro'};observe(data); // 监听data对象data.name = 'endcat'; // 哈哈哈,监听到值变化了 mashiro --> endcat​function observe(data) {    // 检查data    if (!data || typeof data…
thumbnail
Evan You讲Vue响应性原理的摘记
摘自尤雨溪的讲座 万事先从简单开始讲起,比如现在项目经理需要我实现一个东西。 "嘿,我需要两个变量,一个叫a,一个叫b,并且b永远是a的10倍,永 远!" 我拍了拍脑袋开始写: let a = 3let b = a * 10console.log(b) // 30a = 4console.log(b) // Oops, b is still 30 当a改变的时候,b是不会随着a的改变而改变。为了需要让b改变的话那该咋办呢,再加一行? let a = 3let b = a * 10console.log(b) // 30a = 4b = a * 10console.log(b) // now b is 40 但是这样显然是治标不治本的做法,我的想法是当我每次改变a的时候我不需要手动更新b,我希望这样的关系是声明式的。所以我决定使用Excel解决项目经理的需求。 AB1440 (fx = A1 * 40) 在Excel里面,单元格数值之间的关系使用函数来表示。如果在JavaScript中,我也想要实现这样的效果,就应该类似地存在这样一个魔法函数: onAChanged(() => {    b = a * 10}) 每当a改变的时候,就应该执行onAChanged函数。现在的问题就转变为了如何实现这样一个魔法函数。把这个放到实际的web开发情景来看: <span class="cell b1"></span>​<script>    onStateChanged(() => {        document   .querySelector('.cell.b1')   .textContent = state.a * 10   })</script> 这样的声明式函数描述了b1和状态之间的数值关系,如果我们进一步抽象,使用模板语言来加以描述: <span class="cell b1"> {{ state.a * 10}}</span>​<script> onStateChanged(() => { view = render(state) })</script> view = render(state)是所有视图渲染系统中如何工作的非常高等级的抽象。当然其内部有复杂的DOM实现、虚拟DOM实现机制。由于不是这篇文章的重点就不再详细说明。我们更感兴趣的事情是如何实现onStateChanged这一外部函数: let updateconst onStateChanged = _update => {    update = _update}​const setState = newState => {    state = newState    update()} 上面是可能的一种实现方式:它只是简单的将更新函数保存在某个地方,然后不允许用户任意操作状态并要求总是调用一个函数来处理状态,这个函数就是setState,setState简单地接受新状态,替换旧状态,然后再次调用更新函数。 onStateChanged(() => {    view = render(state)})​setState({ a:5 }) 使用过react的人对此应该会特别熟悉。react会在setState中强制性地触发状态改变,而这实际上就是react在响应性上的简单工作机制。但是在angular或者vue中,我们不必调用setState来操作状态。 onStateChanged(() => {    view = render(state)})state.a = 5 事实上在angular中使用了脏检查的方法(回顾一下双向绑定原理),它会拦截事件后检查所有的东西是否被改变了,在vue里会做得更加细致一点。 autorun(() => {    console.log(state.count)}) 把onStateChanged改写成上面这样的形式,其实这本质上是一种在Knockout.js/Meteor Tracker/Vue.js和MobX中通用的基本形式的依赖跟踪。Vue的响应性采用了ES5的Object.defineProperty()API,简单翻一下文档回忆一下: Document 我们来看一个简单实现的方案: <script>function observe (obj) {    // 遍历键值  Object.keys(obj).forEach(key => {    let internalValue = obj[key]    let dep =…
thumbnail
TypeScript的Module小应用
今天我们来写一个非常简单的网页时钟。 TypeScript部分 module Time{     export class Test{         element:HTMLElement;         span:HTMLElement;         timer:number;         constructor(e:HTMLElement){             this.element = e;             this.element.innerHTML = "现在时间是:";             this.span = document.createElement("span");             this.element.appendChild(this.span);             this.span.innerHTML = new Date().toTimeString();         }         start(){             this.timer = setInterval(()=>this.span.innerHTML = new Date().toTimeString(),500);         }         stop(){             clearInterval(this.timer);         }     } }   其实也可以对比一下编译后的JavaScript文件。 var Time; (function (Time) { var Test = /** @class */ (function () { function Test(e) { this.element = e; this.element.innerHTML = "现在时间是:"; this.span = document.createElement("span"); this.element.appendChild(this.span); this.span.innerHTML = new Date().toTimeString(); } Test.prototype.start = function () { var _this = this; this.timer = setInterval(function () { return _this.span.innerHTML = new Date().toTimeString(); }, 500); }; Test.prototype.stop = function () { clearInterval(this.timer); }; return Test; }()); Time.Test = Test; })(Time || (Time = {})); 很容易发现的是,JavaScript里的封装等同于TypeScript里的Module,其中的start,stop方法也通过原型prototype表示了出来。 JavaScript部分 emmm这一部分是来实现开始和暂停按钮的功能。(并没有什么花里胡哨的地方) var div = document.createElement("div"); document.body.appendChild(div); var obj = new Time.Test(div); var button = document.createElement("button"); button.innerHTML = "start"; button.onclick = function () { obj.start(); } document.body.appendChild(button); var buttons = document.createElement("button"); buttons.innerHTML = "stop"; buttons.onclick = function(){ obj.stop(); } document.body.appendChild(buttons); Html部分 (就是把两个js文件引用一下咯) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Timer</title> </head> <body> <script src="balabala.js"></script> <script src="balabala.js"></script> </body> </html> 实现效果 难道你你你不会Copy到你的ide里实现一下嘛(生气)……
thumbnail
JavaScript的单例模式
什么叫做单例模式? [begin]单[/begin]例模式(Singleton Pattern),是一种简单常用的软件设计模式。在数学和逻辑学中,singleton被定义为只包含一个元素的集合。那在JavaScript中我们将其理解为只包含一个称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。 单例模式的要点有三个: 某个类只能有一个实例;它必须自行创建这个实例;它必须自行向整个系统提供这个实例。 单例模式的优点和缺点 优点 实例控制性灵活性 实例控制性 是意味着单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。 在团队开发中,为了实现一些相似的功能,比如不同页面之间的表单验证,可能需求是不一样的,但是呢命名可能一样,这时就会产生冲突,这时候单例模式就能很好的解决这个问题。 灵活性在于该类控制了实例化过程,所以针对实例化过程能实现便捷修改。 缺点 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。 单例类的职责过重,在一定程度上违背了“单一职责原则”。 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。 代码示例 小王小李邻居例子 /* * @Author: Endcat * @Date: 2019-02-21 19:09:46 * @Last Modified by: Endcat * @Last Modified time: 2019-02-21 20:41:55 */ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <!-- 假想背景: 1、两个独立的对象,xiaoWang和xiaoLi,即小王和小李。 2、让xiaoLi跟xiaoWang通过门铃menLing进行通信 3、首先判断小王家xiaoWangjia有没有门men,如果有通过门铃通信didi,如果没有就造一个门。 4、对象之间开始相互通信。 --> <script> var xiaoWang = (function (argument){ var xiaoWangjia = function(message){ this.menLing = message; } var men; var info = { sendMessage:function(message){ if (!men){ men = new xiaoWangjia(message); }; return men; } }; return info; })(); var xiaoLi = { callXiaowang:function(msg){ var _xiaoWang = xiaoWang.sendMessage(msg); alert(_xiaoWang.menLing); _xiaoWang = null; //垃圾回收内存释放 } }; xiaoLi.callXiaowang('didi'); </script> </body> </html> 应用开发举例 /* * @Author: Endcat * @Date: 2019-02-21 20:53:55 * @Last Modified by: Endcat * @Last Modified time: 2019-02-21 20:53:55 */ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> // 页面上有若干个按钮 // 如果用下面的结构,控制同一个变量,势必会很麻烦 $('#a').click(function(){ //逻辑特别多 var varA = 4; }); $('#b').click(function(){ //逻辑特别多 }); $('#c').click(function(){ //逻辑特别多 }); $('#d').click(function(){ //逻辑特别多 });…
thumbnail
随便整理一下web开发相关
[toc] 连接是一件单纯的事情,让连接能够产生绚丽多彩的东西就是不简单的事情。Web开发,差不多就是在干绚丽多彩的事情,重点不在connected的结果,而是connected后的画面。 1 架构的转变 在以前的应用软件都是运行在大型机上面,要使用软件就要通过“哑终端”登录到大型机上去操作软件。什么叫做哑终端呢?就是字符终端,只具备输入输出字符的功能。这时候软件和数据都集中在大型机上。 后来兴起了Personal Computer,软件开始运行在桌面上,和现在的情况类似。而那些软件需要的数据则由远程的服务器存储,服务器端运行像数据库这样的软件。这种模式称为CS架构,也就是客户端和服务器(Client/Server)架构。 客户端和服务器(Client/Server)架构 随着互联网的不断发展,发现CS架构并不是太适合Web应用。主要原因是Web内容更新太快,而若是采用CS架构则需要客户端不停地同步更新桌面应用。后来的浏览器和服务器模式(Brower/Server)应用广泛,在客户端下只要通过浏览器向服务器请求,获取Web内容,并将其显示在浏览器上即可。 浏览器和服务器模式(Brower/Server) 2 Web开发是组啥的? 还记得我之前说的一个词“绚丽多彩”吗,Web开发就是干绚丽多彩的事情的。然而并不是全部。可以把Web分为前端后端,但我觉得要是从事Web开发,一定要冲着全栈的方向去发展自己。表面来看,Web前端更加能够接近绚丽多彩的意思,而后端,甚至是一些Web框架的开发,都是枯燥无味的。但是没有框架也就没有绚丽多彩了。这之间的关系相信读者能够十分轻易的揣摩出来。 3 Web应用开发阶段 3.1 静态阶段 直接编写html文件,如果需要更新则要重新修改原html文件 3.2 交互阶段 静态阶段的网页无法和用户进行交互,如果用户在某个网站上有注册用户的需求,必定会提交一个注册表单。但是静态页面是无法处理这样的数据。CGI(Common Gateway Interface)通用网关接口的出现,使得网页处理动态数据成为了可能。 3.3 脚本阶段 Web应用的特点在前面也提到过,就是更新频繁。在交互阶段的CGI中编写语言都是采用C/C++这样的低级语言,使得开发非常不方便。而因为脚本语言和html结合紧密,且本身非常适合用来开发,因而迅速取代了CGI。常见的脚本语言有ASP/JSP/PHP。 ASP(Active Server Pages)是微软自家开发的服务器端脚本环境,JSP(Java Server Pages)实现了html语法中的Java扩展,PHP(Hypertext Preprocessor)这是一种通用的开源脚本语言。 3.4 MVC阶段 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。MVC解决的脚本语言直接嵌入html所带来的开发问题,实现了业务逻辑、数据和界面相分离的方法。将业务逻辑全部聚集在一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,因此可以显著简化Web开发。目前的脚本语言都有很多兼容的MVC框架。 MVC 3.5 现阶段 Web开发不断发展中,诸如MVVC、MVVM、异步开发层出不穷。 MVVM
thumbnail
用Python来写个简单GUI
虽然是强烈不建议用Python这样的语言来写GUI,即便Python有兼容各系统的图形库,但是在面向特定系统时还是要用本家的语言来写。Python可以用来学习一下接口调用和对象逻辑还是ok的。 #导入tkinter包所有内容,tkinter封装了访问tk的接口,而tk是支持多操作系统的图形库 from tkinter import * #来写一个GUI的HelloWorld #先从Frame框架中派生一个Application类 #注意的是,要把Application看成一个父Widget,之后所有的标签按钮元素都是子Widget。根据这个逻辑可以构建一个Widget树 #使用pack方法添加Widget至父容器 class Application(Frame): def __init__(self): Frame.__init__(self) self.pack() self.createWidgets() def createWidgets(self): self.helloLabel = Label(self, text='Hello, world!') self.helloLabel.pack() self.quitButton = Button(self, text='Quit', command=self.quit) self.quitButton.pack() #pack方法实现了最简单的布局方法,更加复杂应当使用grid方法 app = Application() #设置窗口标题 app.master.title('Hello World') #主消息循环,即开始监听操作系统的命令 app.mainloop() 以上代码实现了最简单的标签和按钮功能。不妨在其中再加一个文本输入框,实现交互功能。 from tkinter import * import tkinter.messagebox as messagebox class Application(Frame): def __init__(self): Frame.__init__(self) self.pack() self.createWidgets() def createWidgets(self): self.helloLabel = Label(self, text='Hello, world!') self.helloLabel.pack() self.quitButton = Button(self, text='Quit', command=self.quit) self.quitButton.pack() #---------- self.nameInput = Entry(self) self.nameInput.pack() self.alertButton = Button(self, text='Hello', command=self.hello) self.alertButton.pack() def hello(self): name = self.nameInput.get() or 'world' messagebox.showinfo('Message', 'Hello, %s' % name) #---------- app = Application() app.master.title('Hello World') app.mainloop()
thumbnail
简单的网络连接实验
[toc] 怎么来模拟一下我们日常的网络连接呢? 网络连接首先要理解成为信息数据的传输。在传输层中我们需要符合一些“规则”,让数据们“符合规范”,才能在网络中进行传输,这也是理所当然且可以理解的事情。一旦没有了规则,做什么事都会乱套。 一般有两套规则可以使用:TCP和UDP协议。 1 什么叫TCP呢? TCP(Transmission Control Protocol),叫做传输控制协议,是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,也就是俗称的“三次握手”。三次握手简单来说是这样的:A向B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;B向A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;A再发出一个数据包确认B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,A才向B正式发送数据。 归纳一下,就是TCP规则下,双方先试着传一下数据看看能否接受同步,如果可以就认为是建立好连接了,接下来就可以正式进行数据的传输。 2 什么叫UDP呢? UDP(User Data Protocol,用户数据报协议)应该和TCP差不多都是一种数据传输规则。差别在于UDP是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去! UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送ICMP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。例如,在默认状态下,一次“ping”操作发送4个数据包(可以在cmd中执行ping www.endcat.cn)。可以看到,发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效率高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。 3 接下来我们来模拟吧 3.1 TCP连接模拟 3.1.1 首先要知道一下Socket是什么东西 谷歌翻译socket是插座的意思?!但是好像在网络编程里面并不是“插座”的意思鸭… 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。等一下插座这个意思可以解释的通。想象一下哈,我们有两台主机,各自带有一个插孔(插孔就是Socket啦),现在又有一条两端都是插头的绳子,两端插在两台主机上面使得主机之间可以通信。这条绳子就差不多是像网线一样的东西吧,应该是这样的(自言自语)。所以建立网络通信连接至少要一对端口(socket)。 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。句柄就是当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄来工作。 在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。 3.1.2 简单的TCP客户端(Client) 客户端是什么意思?简单的来说,主动发起连接的就是客户端,被动接受连接的就是服务器(server) 。举个例子,当我们在浏览器中访问网站时,我们自己的计算机就是客户端,浏览器会主动向网站的服务器发起连接。如果一切顺利,网站的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了。 既然要发起连接,前面说过了,我们应该要有一个socket,这样才能和远程网站服务器的socket进行连接,这样我们才能看到网站的信息。 所以接下来我们要模拟创建一个socket。 #利用python来创建一个socket #首先导入一个socket库,里面提供了创建socket的必要函数 import socket #创建一个socket,名字叫做s s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #开始建立连接 s.connect(('127.0.0.1', 9999)) #接受数据并把它们打印出来 print(s.recv(1024).decode('utf-8')) #从客户端发送给服务器一些数据 for data in [b'Aumi', b'Endcat']: s.send(data) print(s.recv(1024).decode('utf-8')) #发送一个exit信息,表示我要退出连接辽 s.send(b'exit') #关闭socket,本次连接结束 s.close() 有几点地方要注意一下: connect函数要注意参数是一个元组tuple,包含地址和端口号。 端口号小于1024的是Internet标准服务的端口,可能需要管理员权限。端口号大于1024的,可以任意使用。 创建socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个socket对象就创建成功,但是还没有建立连接。 127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。接收数据时,调用recv(max)方法,一次最多接收指定的字节数 . 3.1.3 简单的TCP服务器(Server) 现在假设我们是一个服务器了,我们需要接受远程客户端发来的连接请求。不管怎么说,创建socket是头等大事,之后我们要按照对方发来的数据进行分析,并返回给客户端数据。 #引用一些库,某些不一样的地方会在后面提示 import socket import threading import time #新线程的函数 def tcplink(sock, addr): print('Accept new connection from %s:%s...' % addr) #发送一个欢迎标识 sock.send(b'Welcome!') #死循环接受客户端数据 while True: data = sock.recv(1024) #哦,在这里停顿一下 time.sleep(1) #如果没有数据或者接收到了exit,那么就跳出循环 if not data or data.decode('utf-8') == 'exit': break #发送打招呼的数据 sock.send(('Hello, %s' % data.decode('utf-8')).encode('utf-8')) #关闭连接,本次连接结束 sock.close() print('Connection from %s:%s closed.' % addr) #创建一个socket对象,还是熟悉的味道 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #绑定接口,注意参数tuple s.bind(('127.0.0.1', 9999)) #开始监听有无数据到达,参数表示最大等待链接数量 s.listen(5) print('Waiting for connection...') #死循环接受客户端连接 while True: #接收到了客户端请求 sock, addr = s.accept() #创建一个线程来处理改客户端数据,多线程可以同时处理不同客户端的请求 t = threading.Thread(target=tcplink, args=(sock, addr)) t.start() 3.1.4 开始连接尝试 可以打开两个终端,先运行server再运行client,看看两个终端各自返回了什么信息。接下来的UDP也是同理哦。 3.2 UDP连接模拟 相比TCP,感觉UDP更加简单一点。其实原因也是显而易见的,前面说过UDP是不讲究连接的可靠性而是直接把数据发送过去。在代码量上也可以看出UDP更加简单。 3.2.1 简单的UDP客户端(Client) import socket #DGRAM指定了UDP s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for data in [b'Endcat', b'Aumi']: #不需要connect,直接使用sendto发送数据 s.sendto(data, ('127.0.0.1', 9999)) print(s.recv(1024).decode('utf-8')) s.close() 3.2.2…
thumbnail
PIP安装
[toc] [begin]p[/begin]ip 是 Python 著名的包管理工具,在 Python 开发中必不可少。本文只介绍各平台最新可用并且最简单的 pip 安装方式,以方便初学者和不会敲代码只需通过 pip 安装特定工具的小伙伴们。 方式一 wget http://python-distribute.org/distribute_setup.py sudo python distribute_setup.py wget https://github.com/pypa/pip/raw/master/contrib/get-pip.py sudo python get-pip.py 方式二 wget https://pypi.python.org/packages/source/p/pip/pip-1.3.1.tar.gz --no-check-certificate tar xvf pip-1.3.1.tar.gz python pip-1.3.1/setup.py install 方式三 wget https://bootstrap.pypa.io/get-pip.py python get-pip.py