zerolhao-blog


  • Home

  • Tags

  • Categories

  • Archives

  • Sitemap

H49-毕设:网易云音乐

Posted on 2018-03-29 | In 饥人谷

这次我们做一个仿手机端网易云音乐

在写代码之前先做好准备工作,需求分析等
首先我们来画图

  • 用例图
  • 线框图(草图)
  • 框架图

接下来搭建 LeanCloud 环境

  1. 创建一个新应用 wy-music
  2. 在 帮助-快速入门 中按照教程安装 SDK
  3. 然后验证-测试代码
  4. 添加 Song 和 Playlist 两个 Class

SDK 是什么

软件开发工具包(Software Development Kit, SDK)一般是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、作业系统等建立应用软件的开发工具的集合。
SDK 与 API 的区别

创建七牛帐号并上传一个mp3成功

注意七牛的版本使用的是 1.0.22,
npm安装的是 2.x,安装后在 package.json中修改后 npm i
代码
方案一:

  1. 注册七牛帐号(上传身份证)
  2. 创建一个篮子(bucket)
  3. 往篮子里上传 mp3 文件
  4. 获取 mp3 文件的外链

方案二:

  1. 注册七牛帐号(上传身份证)
  2. 创建一个篮子(bucket)
  3. 创建一个 nodejs server

    1. 进入七牛 SDK 官网,选择 Node.js
    2. npm init -y
    3. npm install qiniu
    4. 添加 /uptoken 路由
    5. 在 /uptoken 中添加代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      if (path === '/uptoken') {
      response.statusCode = 200
      response.setHeader('Content-Type', 'text/json;charset=utf-8')
      // set CORS
      response.setHeader('Access-Control-Allow-Origin', '*')
      // 获取 key,本地文件不上传
      var key = fs.readFileSync('./qiniu-key.json')
      key = JSON.parse(key)
      // 创建各种上传凭证之前,我们需要定义好其中鉴权对象mac
      let { accessKey, secretKey } = key
      var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
      // 简单上传
      var options = {
      scope: "wy-music", // 篮子(bucket)名称
      };
      var putPolicy = new qiniu.rs.PutPolicy(options);
      var uploadToken = putPolicy.uploadToken(mac);
      // 返回 uoloadToken,admin.html设置访问获取
      response.write(`
      {
      "uptoken": "${uploadToken}"
      }
      `)
      response.end()
      }
    6. 将 uploadToken 作为响应输出

    7. node server.js 8888,启动 server
  4. 管理员页面admin.html,参考七牛的示例,使用 Qiniu.uploader 来上传文件

    1. 引入 moxie 1.x
    2. 引入 plupload 2.x
    3. 引入 qiniu-js 1.x
      1
      2
      3
      <script src="../venders/moxie.min.js"></script>
      <script src="../venders/plupload.min.js"></script>
      <script src="../node_modules/qiniu-js/dist/qiniu.min.js"></script>
  5. 初始化上传按钮

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    var uploader = Qiniu.uploader({
    disable_statistics_report: false, // 禁止自动发送上传统计信息到七牛,默认允许发送
    runtimes: 'html5', // 上传模式,依次退化
    browse_button: 'uploadBtn', // 上传选择的点选按钮,**必需**
    uptoken_url: 'http://localhost:8888/uptoken',
    get_new_uptoken: false, // 设置上传文件的时候是否每次都重新获取新的 uptoken
    domain: 'https://p6az32xea.bkt.clouddn.com', // bucket 域名,下载资源时用到,如:'http:// xxx.bkt.clouddn.com/' **必需**
    max_file_size: '40mb', // 最大文件体积限制
    flash_swf_url: 'path/of/plupload/Moxie.swf', //引入 flash,相对路径
    max_retries: 3, // 上传失败最大重试次数
    dragdrop: true, // 开启可拖曳上传
    drop_element: 'uploadContainer', // 拖曳上传区域元素的 ID,拖曳文件或文件夹后可触发上传
    chunk_size: '4mb', // 分块上传时,每块的体积
    auto_start: true, // 选择文件后自动上传,若关闭需要自己绑定事件触发上传,
    init: {
    'FilesAdded': function(up, files) {
    plupload.each(files, function(file) {
    // 文件添加进队列后,处理相关的事情
    });
    },
    'BeforeUpload': function(up, file) {
    // 每个文件上传前,处理相关的事情
    },
    'UploadProgress': function(up, file) {
    // 每个文件上传时,处理相关的事情
    uploadStatus.innerText = '上传中……'
    },
    'FileUploaded': function(up, file, info) {
    // 每个文件上传成功后,处理相关的事情
    uploadStatus.innerText = '上传完毕'
    // 其中 info.response 是文件上传成功后,服务端返回的json,形式如
    // {
    // "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
    // "key": "gogopher.jpg"
    // }
    // 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html
    // var domain = up.getOption('domain');
    // var res = parseJSON(info.response);
    // var sourceLink = domain + res.key; 获取上传成功后的文件的Url
    },
    'Error': function(up, err, errTip) {
    //上传出错时,处理相关的事情
    },
    'UploadComplete': function() {
    //队列文件处理完毕后,处理相关的事情
    }
    }
    });

本文所使用的依赖:
moxie 版本号:1.5.6
plupload 版本号:2.3.6

H46-从 MVC 到 MVVM

Posted on 2018-03-25 | In 饥人谷

首先来看一段代码

1
2
3
4
5
6
7
8
9
10
11
<div id="app">
<div>
书名:《__name__》
数量:<span id=number>__number__</span>
</div>
<div>
<button id="addOne">加1</button>
<button id="minusOne">减1</button>
<button id="reset">归零</button>
</div>
</div>

从后台获取数据,这次我们使用一个新的库 axios

引入axios

一个基于 Promise 的 HTTP 客户端,用于浏览器和node.js

  1. 比 jQuery.ajax 功能更多
  2. 除了 ajax功能之外,没有其它功能
    代码

引入 MVC

代码

引入 Vue

代码

Vue

教程

下面是几个用 vue 写的例子

  • 浮层例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <div id="app">
    </div>
    <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
    <script>
    let view = new Vue({
    el: '#app',
    data: {
    open: false,
    },
    template: `
    <div>
    <button v-on:click="toggle">点击</button>
    <div v-show="open">现在你看见我了</div>
    </div>
    `,
    methods:{
    toggle(){ // es6 语法,等同于 toggle: function(){
    this.open = !this.open // 注意这里必须有 this
    }
    }
    })
    </script>
  • 轮播例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // css
    .window{
    width:100px;
    height: 60px;
    margin: auto;
    border: 1px solid black;
    }
    .slides{
    width: 300px;
    height: 60px;
    background-color: #906;
    transition: 1s;
    }
    // html
    <div id="app">
    </div>
    <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
    <script>
    let view = new Vue({
    el: '#app',
    data: {
    marginLeftNum: 0,
    url:['/a','/b','/c']
    },
    template: `
    <div>
    <div class='window'>
    <div class="slides"
    v-bind:style="{marginLeft: marginLeftNum+'px'}">
    <img v-for="src in url" />
    </div>
    </div>
    <button v-for="(btn, index) in url"
    v-on:click='go(index)'>{{index+1}}
    </button>
    </div>
    `,
    methods: {
    go(n){
    this.marginLeftNum = `${-n*100}`
    }
    }
    })
    </script>
  • tab切换例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // css
    .active{
    color: red;
    }
    // html
    <div id="app">
    </div>
    <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
    <script>
    let view = new Vue({
    el: '#app',
    data: {
    select: 'a',
    tabs: [
    {name:'a',content:'This is A'},
    {name:'b',content:'This is B'},
    {name:'c',content:'This is C'},
    ]
    },
    template: `
    <div>
    <ol>
    <li v-for="tab in tabs"
    v-on:click="select = tab.name"
    v-bind:class="{active:select === tab.name}">
    {{tab.name}}
    </li>
    </ol>
    <ul>
    <li v-for="tab in tabs"
    v-if="select === tab.name">
    {{tab.content}}
    </li>
    </ul>
    </div>
    `
    })
    </script>

Questions

  • v-if 与 v-show的区别
    使用了v-if的时候,如果值为false,那么页面将不会有这个html标签生成。
    v-show则是不管值为true还是false,html元素都会存在,只是CSS中的display显示或隐藏

  • 单向绑定与双向绑定

API

JavaScript

  • Object.assign()
    jQuery
  • .html()

H45-初识webpack

Posted on 2018-03-24 | In 饥人谷

工程化

  • 自动化(使用sass、babel等工具编译代码)
  • 模块化
  • 性能优化
    自动化工具
    将scss/sass转为ie也可以用的css
    使用babel将es6代码转化为es5代码
    单个使用自动化工具很麻烦,而 webpack 就省事多了,webpack 可以安装插件来实现它们的功能

webpack

WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

WebPack和Grunt以及Gulp相比有什么特性
其实Webpack和另外两个并没有太多的可比性,Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。

Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。

Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

使用webpack
安装教程,直接看官网教程
面试题

模块化,所有文件都是一个模块,引入到其中一个文件,
运行 npx webpack or npm run build (注意这是本地安装使用的命令)
然后所有文件会打包放到 bundle.js 里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// module1.js
function fn(){
console.loog(1)
}
export default fn

// module2.js
function fn(){
console.log(2)
}
export default fn

// app.js
import module1 from './module1' // 引入模块1
import module2 from './module2' // 引入模块2
import '../css/style.css' // 引入css
module1() // use 模块
module2()

所有源代码放在 src 文件夹
所有编译后的代码放在 dist 文件夹
src source 未编译
dist distribution 编译后
代码

webpack 插件

  • Babel Loader
  • SASS Loader
  • PostCSS Loader

parcel

H44-Session、LocalStorage、Cache-Control

Posted on 2018-03-19 | In 饥人谷

Cookie 存在的问题

用户可以随意篡改 Cookie

Session 与 Cookie 的关系

一般来说,Session 基于 Cookie 来实现。
区别:
Cookie 保存在客户端,每次都随请求发送给 Server
Session 保存在 Server 的内存里,其 Session ID 是通过 Cookie 发送给客户端的

知乎
Cookie
本质是HTTP协议的一个内容

  1. 服务器通过 Set-Cookie 头给客户端一串字符串
  2. 客户端每次访问相同域名的网页时,必须带上这段字符串
  3. 客户端要在一段时间内保存这个Cookie
  4. Cookie 默认在用户关闭页面后就失效,后台代码可以任意设置 Cookie 的过期时间
  5. 大小大概在 4kb 以内

Session(不翻译)

  1. 将 SessionID(随机数)通过 Cookie 发给客户端
  2. 客户端访问服务器时,服务器读取 SessionID
  3. 服务器有一块内存(哈希表)保存了所有 session
  4. 通过 SessionID 我们可以得到对应用户的隐私信息,如 id、email
  5. 这块内存(哈希表)就是服务器上的所有 session
    缺点:用户太多的话占内存高(cookie不占内存)

不基于 cookie 的 session

使用查询参数和localstorage来实现

建议:前端不建议读写cookie,那是后端的事

web Storage

window.localStorage
localStorage就是HTML5的API
MDN

  1. localStorage 跟 HTTP 无关
  2. HTTP 不会带上 LocalStorage 的值
  3. 只有相同域名的页面才能互相读取 LocalStorage (没有同源那么严格)
  4. 每个域名 localStorge 最大存储量为 5Mb 左右(每个浏览器不一样)
  5. 常用场景:记录有没有提示过用户(没有用的信息,不能记录密码)
  6. LocalStorage 永久有效,除非用户清理

window.sessionStorage(会话存储)
MDN
1、2、3、4同上

  1. sessionStorage 在用户关闭页面(会话结束)后就失效
    注意:sessionStorage和上面的session并没有关系

使用 localStorage

1
2
3
4
5
6
7
8
9
10
<script>
var a = localStorage.getItem('a')
if(!a){
a = 0
}else{
a = parseInt(a,10) + 1
}
console.log('a',a)
localStorage.setItem('a',a)
</script>

原本页面刷新后变量全部清掉了,但是现在a被localStorage保存下来了(在c盘的一个文件里)
这叫做持久化储存

常用的一种方式

1
2
3
4
5
6
7
<script>
let already = localStorage.getItem('已经提示了')
if(!already){
alert('你好,我们的网站已经改版了,有了这些功能:……')
localStorage.setItem('已经提示了',true)
}
</script>

这样使用localstroge来保存一个判断,就使得不会每次进入页面都出现提示了

cookie 与 LocalStorage 的区别

cookie 会被发送给服务器而 localStorage 不会

HTTP 缓存

web 性能优化

  • Cache-Control

    通用消息头被用于在http 请求和响应中通过指定指令来实现缓存机制。缓存指令是单向的, 这意味着在请求设置的指令,在响应中不一定包含相同的指令。

功能:让浏览器在一定时间内不访问服务器,直接用本地的硬盘或内存直接作为响应 从而提高速度
更新: 在入口处(html)把url稍微变动,和以前所有的url都不一样,那么它就不会使用缓存,浏览器就回去下载最新版

1
2
3
4
5
6
// nodejs
if(path === '/js/main.js'{
...
response.setHeader('Cache-Control','max-age=30') // 缓存30秒
...
})

如果服务器给某个文件设置了cache-control,比如上面,
那么第一次请求后30秒内你刷新页面,请求并不会被浏览器发送到服务器
首页不要设置cache-control(最好所有html都不要设置)

更新缓存

将一个文件设置缓存,比如上面那个mian.js,改为一年(这很常见,比如知乎就是这么设置的)
那么如果有变动怎么办?
只有相同的url才会利用缓存,
所以,只要url有一点变化就会重新获取资源
依然是main.js,假设它有所变化
那么我们在引用mian.js的index.html

1
<script src='main.js?ver=1'></script>

给它添加一个查询参数,它就会自动更新为最新版本
另一种方法,使用md5

  • Expires

Expires 头指定了一个日期/时间, 在这个日期/时间之后,HTTP响应被认为是过时的;
无效的日期,比如 0, 代表着一个过去的事件,即该资源已经过期了。
如果还有一个 设置了 “max-age” 或者 “s-max-age” 指令的Cache-Control响应头,那么 Expires 头就会被忽略。

expires 是以前用的,现在都用 cache-control
response.setHeader('Ex[ores', 'Tue, 20 Mar 2018 07:13:41 GMT')
cache-control 设置的是多少时间以后过期
expires 设置的是什么时候过期
但是,他使用的是本地时间,也就是说如果用户不小心将自己的本地时间设置了,或者错乱了,
那么可能就会受到影响,出现bug

MD5

MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值(hash value),用于确保信息传输完整一致。

node使用md5

  • ETag

ETag HTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)。
如果给定URL中的资源更改,则一定要生成新的Etag值。 因此Etags类似于指纹,也可能被某些服务器用于跟踪。 比较etags能快速确定此资源是否变化,但也可能被跟踪服务器永久存留。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var md5 = require('md5');

if(path === '/js/main.js'{
let string = fs.readFileSync('./js/main/js', 'utf8')
...
let fileMd5 = md5(string)
if(request.headers['if-none-match'] === fileMd5){
// 没有响应体
response/statusCode = 304
} else{
response.setHeader('ETag',fileMd5)
// 有响应体
response.write(steing)
}
response.end()
})

cache-control 是直接不请求
etag 是请求不下载

  • Last-Modified

The Last-Modified 是一个响应首部,其中包含源头服务器认定的资源做出修改的日期及时间。 它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致。由于精确度比 ETag 要低,所以这是一个备用机制。包含有 If-Modified-Since 或 If-Unmodified-Since 首部的条件请求会使用这个字段。

浏览器缓存详解:expires,cache-control,last-modified,etag

H43-Cookie与登录注册

Posted on 2018-03-17 | In 饥人谷

登录与注册

代码

Cookie 的特点

  1. 服务器通过 Set-Cookie 响应头设置 Cookie
  2. 浏览器得到 Cookie 之后,每次请求都要带上 Cookie
  3. 服务器读取 Cookie 就知道登录用户的信息(email)
    wiki
    方应杭

问题

  1. 我在 Chrome 登录了得到 Cookie,用 Safari 访问,Safari 会带上 Cookie 吗
    no
  2. Cookie 存在哪
    Windows 存在 C 盘的一个文件里
  3. Cookie会被用户篡改吗?
    可以,下节课会讲 Session 来解决这个问题,防止用户篡改
  4. Cookie 有效期吗?
    默认有效期20分钟左右,不同浏览器策略不同
    后端可以强制设置有效期,具体语法看 MDN
  5. Cookie 遵守同源策略吗?
    也有,不过跟 AJAX 的同源策略稍微有些不同。
    当请求 qq.com 下的资源时,浏览器会默认带上 qq.com 对应的 Cookie,不会带上 baidu.com 对应的 Cookie
    当请求 v.qq.com 下的资源时,浏览器不仅会带上 v.qq.com 的Cookie,还会带上 qq.com 的 Cookie
    另外 Cookie 还可以根据路径做限制,请自行了解,这个功能用得比较少。

API

jQuery

  • .find()
  • .val()
  • $.ajax()
  • $.post()
  • .each()
    JavaScript
  • decodeURIComponent()
  • try…catch
    JSON
    • JSON.stringify()
      Node.js
  • readFileSync()
  • writeFileSync()

H41-会动的简历

Posted on 2018-03-14 | In 饥人谷

页面会动的原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// html
<head>
...
<style id='styleTag'></style>
</head>
<body>
<pre id='code'></pre>
</body>

// js
var str = `
body {
transition: all 1s;
background: #f60;
}
`
var n = 0
var id = setInterval(()=>{
code.innerHTML = str.slice(0,n)
styleTag.innerHTML = str.slice(0,n)
if(n >= str.length){
window.clearInterval(id)
}
n++
console.log(n)
},100)

最终代码
预览

异步

不等结果直接进行下一步
怎么拿到结果?
使用回调
什么是回调?
a: 让黄牛去买票,然后站着等(同步)
b: 让黄牛去买票(让黄牛买到票call我),然后去做别的
b括号里面的部分就是回调
回调是拿到异步结果的一种方式
回调也可以拿同步结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function 异步做事(){
setTimeout(function(){
console.log('异步做事')
},1000)
}
function 同步做事(){
console.log('同步做事')
}
异步做事()
console.log(1) // 不等上面结果
// 由于异步做事,不等结果,所以会先打印1

同步做事()
console.log(1) // 等上面结果
// 由于同步做事等结果,所以要等做完事,再打印1

知乎

老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点傻2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。3 老张把响水壶放到火上,立等水开。(异步阻塞)老张觉得这样傻等意义不大4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)老张觉得自己聪明了。所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。——来源网络,作者不明。

阮一峰-JS异步编程的四种方法

#

  • Element.scrollHeight
  • <pre></pre> 标签
    pre标签全称preview

H39-面向对象编程

Posted on 2018-03-10 | In 饥人谷

Object-Oriented Programming

面向对象编程
缩写 OOP

MDN-JavaScript面向对象简介

面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。它使用先前建立的范例,包括模块化,多态和封装几种技术。今天,许多流行的编程语言(如Java,JavaScript,C#,C+ +,Python,PHP,Ruby和Objective-C)都支持面向对象编程(OOP)。
相对于 “一个程序只是一些函数的集合,或简单的计算机指令列表。”的传统软件设计观念而言,面向对象编程可以看作是使用一系列对象相互协作的软件设计。 在 OOP 中,每个对象能够接收消息,处理数据和发送消息给其他对象。每个对象都可以被看作是一个拥有清晰角色或责任的独立小机器。
面向对象程序设计的目的是在编程中促进更好的灵活性和可维护性,在大型软件工程中广为流行。凭借其对模块化的重视,面向对象的代码开发更简单,更容易理解,相比非模块化编程方法 1, 它能更直接地分析, 编码和理解复杂的情况和过程。

术语

Namespace 命名空间
允许开发人员在一个独特, 应用相关的名字的名称下捆绑所有功能的容器。
Class 类
定义对象的特征。它是对象的属性和方法的模板定义.
Object 对象
类的一个实例。
Property 属性
对象的特征,比如颜色。
Method 方法
对象的能力,比如行走。
Constructor 构造函数
对象初始化的瞬间, 被调用的方法. 通常它的名字与包含它的类一致.
Inheritance 继承
一个类可以继承另一个类的特征。
Encapsulation 封装
一种把数据和相关的方法绑定在一起使用的方法.
Abstraction 抽象
结合复杂的继承,方法,属性的对象能够模拟现实的模型。
Polymorphism 多态
多意为‘许多’,态意为‘形态’。不同类可以定义相同的方法或属性。

原型编程

基于原型的编程不是面向对象编程中体现的风格,且行为重用(在基于类的语言中也称为继承)是通过装饰它作为原型的现有对象的过程实现的。这种模式也被称为弱类化,原型化,或基于实例的编程。

封装 Model View Controller

代码

new

方应杭

复习 this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
button.onclick = function f1(){
console.log(this) // 触发事件的元素。 button
}

button.onclick.call({name: 'frank'})



button.addEventListener('click', function(){
console.log(this) // 该元素的引用 button
}
2 结果


$('ul').on('click', 'li' /*selector*/, function(){
console.log(this) //this 则代表了与 selector 相匹配的元素。
// li 元素
})
3 结果
去看 on 的源码呀 -> 做不到
jQuery 的开发者知道 onclick 的源码,f1.call(???)
jQuery 的开发者写了文档
看文档呀

return 和 传参

一个练习找this的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function X(){
return obj = {
name:'obj',
options: null,
f1(x){
this.options = x
this.f2()
},
f2(){
this.options.name = 'ob'
this.options.f2.call(this)
}
}
}

var options = {
name:"options",
f1(){},
f2(){
console.log(this)
}
}

var x = X()
var x2 = X()
// return 回来的是对象,是新建立的
console.log(x === x2) // false
x.age = 12
// 传参对象传的是地址,叫引用,名字不同但相等
x.f1(options)
options.f2()
console.log(options === x.options) // true

注意这里

1
2
3
4
5
6
7
...
f1(x){
this.options = x
this.f2()
},
...
x.f1(options)

这里将options传入f1,x是对options的引用,
而不是像return一样是一个新的对象,
注意脑子不要乱。

&& 和 || 操作符

注意,在js中 &&、||返回值并不是true或false

1
2
3
4
5
6
7
// && 返回它遇到的第一个 falsy 值,
1 && 0 && console.log(2) // 0
// 如果没有falsy值,返回最后一个非 falsy 值
1 && 2 && 3 // 3

// || 返回它遇到的第一个非 falsy 值
0 || null || undefined || 1 || '' || NaN // 1

MDN

H37-给简历加个数据库

Posted on 2018-03-10 | In 饥人谷

LeanCloud 介绍

一个自带数据库和增删改查(CRUD)功能的后台系统。
LeanCloud
拥有:

  1. 登录注册、手机验证码功能(收费)
  2. 存储任意信息
  3. 读取任意信息
  4. 搜索任意信息
  5. 删除任意信息
  6. 更新任意信息
    等功能。

成果

预览
代码

MVC

MVC 是一种代码组织形式,它不是任何一种框架或技术,它是一种思想。
MVC分为三部分:

  1. Model(模型): 数据
  2. View(视图): 用户界面
  3. Controller(控制器): 业务逻辑
    举个例子用户在网页上查看个人信息
    主要逻辑为:
    1. 用户进行操作(view)
    2. 控制器监听到操作,执行对应的代码, (controller)
    3. 控制器操作model从服务器取出用户数据(model)
    4. 然后将数据显示在页面上 (controller)
      这其中,view只负责视图,model只负责数据,其余如监听事件,添加元素,添加信息等等全部由controller负责
      这样,就使得我们的代码分类,并显得井然有序,更加的方便未来的修改。

H36-把MVC的VC加到简历里

Posted on 2018-03-09 | In 饥人谷

模块化

简单来说就是将功能不同的代码分开来,实现a功能的代码一个文件,实现b功能的代码一个文件
阮一峰

模块就是实现特定功能的一组方法。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。

ES6 Module
Web Components
知乎问答

如何使用立即执行函数

  1. 我们不想要全局变量
  2. 我们要使用局部变量
  3. ES 5 里面,只有函数有局部变量
  4. 于是我们声明一个 function xxx,然后 xxx.call()
  5. 这个时候 xxx 是全局变量(全局函数)
  6. 所以我们不能给这个函数名字
  7. function(){}.call()
  8. 但是 Chrome 报错,语法错误
  9. 试出来一种方法可以不报错:
    1. !function(){}.call() (我们不在乎这个匿名函数的返回值,所以加个 ! 取反没关系)
    2. (function(){}).call() 方方不推荐
      xxx
      (function(){}).call() 报错
    3. frank192837192463981273912873098127912378.call() 不推荐

如何使用闭包

  1. 立即执行函数使得 person 无法被外部访问
  2. 闭包使得匿名函数可以操作 person
  3. window.frankGrowUp 保存了匿名函数的地址
  4. 任何地方都可以使用 window.frankGrowUp
    => 任何地方都可以使用 window.frankGrowUp 操作 person,但是不能直接访问 person

箭头函数没有 this

箭头函数内外 this 不变,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
!function() {
var view = document.querySelector('#siteTopNavBar')
var controller = {
view: null,
init: function(view) {
this.view = view
this.bindEvents()
},
bindEvents: function() {
this.userDoScroll()
window.addEventListener('scroll',()=>{
this.userDoScroll()
})// 箭头函数没有this,自动引用外层的this

// 注意this指向,下面这样写this就会变成指向window
//window.addEventListener('scroll', this.userDoScroll) // 等于下面
//window.onscroll = function(){...}

//或者使用 bind 绑定this
// window.addEventListener('scroll',this.userDoScroll.bind(this))
},
userDoScroll: function(){
if (window.scrollY !== 0) {
this.active()
} else {
this.deactive()
}
//console.log(this)
},
active: function(){
this.view.classList.add('sticky')
},
deactive: function(){
this.view.classList.remove('sticky')
}
}

controller.init(view)
}.call()

MVC

wiki

MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
MVC模式的目的是实现一种动态的程式设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。
除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。

  • 控制器(Controller)- 负责转发请求,对请求进行处理。
  • 视图(View) - 界面设计人员进行图形界面设计。
  • 模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

MDN MVC architecture
阮一峰-谈谈MVC模式
阮一峰-MVC,MVP 和 MVVM 的图示
方应杭-后端的 MVC
方应杭-前端的 MVC

H35-自己写AJAX与理解Promise

Posted on 2018-03-07 | In 饥人谷

AJAX 的所有功能

  • 客户端的JS发起请求(浏览器上的)
  • 服务端的JS发送响应(Node.js上的)

1. JS 可以设置任意请求 header 吗?

第一部分 request.open(‘get’, ‘/xxx’)
第二部分 request.setHeader(‘content-type’,’x-www-form-urlencoded’)
第四部分 request.send(‘a=1&b=2’)

  • open()
  • setRequestHeader()
    setRequestHeader方法用于设置HTTP头信息。
    该方法必须在open()之后、send()之前调用。
    如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。
    1
    2
    3
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
    xhr.send(JSON.stringify(data));

上面代码首先设置头信息Content-Type,表示发送JSON格式的数据;然后设置Content-Length,表示数据长度;最后发送JSON数据。

  • send()

2. JS 可以获取任意响应 header 吗?

第一部分 request.status / request.statusText
第二部分 request.getResponseHeader() / request.getAllResponseHeaders()
第四部分 request.responseText

  • status
    status属性为只读属性,表示本次请求所得到的HTTP状态码,它是一个整数。一般来说,如果通信成功的话,这个状态码是200。
  • statusText
    statusText属性为只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status属性,该属性包含整个状态信息,比如”200 OK“。(测试显示只有 OK)
  • getAllResponseHeaders()
    getAllResponseHeaders方法返回服务器发来的所有HTTP头信息。
    格式为字符串,每个头信息之间使用CRLF(回车换行)分隔,如果没有受到服务器回应,该属性返回null。
  • getResponseHeader()
    getResponseHeader方法返回HTTP头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,则该属性为null。
    consolo.log(request.getResponseHeader('Content-Type'))
  • responseText
    responseText属性返回从服务器接收到的字符串,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null。

还记得之前写的 window.jQuery 吗

1
2
3
4
5
6
7
8
9
10
11
window.jQuery = function(node){
let nodes = {
0: node,
length: 1
}
return {
addClass: function(){

}
}
}

今天写 window.jQuery.ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
window.jQuery = function(nodeOrSelector){
let nodes = {}
nodes.addClass = function(){}
nodes.html = function(){}
return nodes
}
window.$ = window.jQuery

/*
window.jQuery.ajax = function(options){
let url = options.url
let method = options.method
...
// es6-解构赋值
let {url, method, body, successFn, failFn, headers} = options
*/

window.jQuery.ajax = function({url, method, body, successFn, failFn, headers}){
// 这里直接从第一个参数开始解构,同时声明这6个变量,用let
let request = new XMLHttpRequest()
request.open(method, url) // 配置request
for(let key in headers) { // 如果设置不止一个请求头
let value = headers[key]
request.setRequestHeader(key, value)
}
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status < 300){
successFn.call(undefined, request.responseText)
}else if(request.status >= 400){
failFn.call(undefined, request)
}
}
}
request.send(body)
}

function f1(responseText){}
function f2(responseText){}

myButton.addEventListener('click', (e)=>{
window.jQuery.ajax({ // 传入对象
url: '/frank',
method: 'get',
headers: {
'content-type':'application/x-www-form-urlencoded',
'frank': '18'
},
successFn: (x)=>{ // 回调函数
f1.call(undefined,x) //同时调用复数函数
f2.call(undefined,x)
},
failFn: (x)=>{
console.log(x)
console.log(x.status)
console.log(x.responseText)
}
})
})

ES6-解构赋值

MDN
阮一峰

1
2
3
4
5
6
7
8
9
10
11
12
// eg
var a = 'a', b='b',temp
temp = a
a = b // 'b'
b = temp // 'a'

// es6
var a = 'a'
var b = 'b'; // 这里必须有分号
[a,b] = [b,a]
a // 'b'
b // 'a'

回调函数

wiki

在计算机编程中,一个回调是对一段可执行代码的引用,该代码作为参数传递给其他代码。
jQuery文档
回调是一个函数,它作为参数传递给另一个函数,并在父函数完成后执行。
回调的特别之处在于,在“父”节点之后出现的函数可以在回调执行之前执行。
另一个需要知道的重要事情是如何正确地传递回调。这就是我经常忘记正确语法的地方。
百科
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

因此,回调本质上是一种设计模式,并且jQuery(包括其他框架)的设计原则遵循了这个模式。

在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。

因此callback 不一定用于异步,一般同步(阻塞)的场景下也经常用到回调,比如要求执行某些操作后执行回调函数。

参考

回调的问题

问题是每个程序员的回调名不一样

阮一峰-JS异步编程的四种方法

Promise 解决了这个问题

基本逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
function xxx(){
return new Promise((f1, f2) => {
doSomething()
setTimeout(()=>{
// 成功就调用 f1,失败就调用 f2
},3000)
})
}

xxx().then(success, fail)

// 链式操作
xxx().then(success, fail).then(success, fail)

上面的例子改用 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
window.jQuery = function(nodeOrSelector){
let nodes = {}
nodes.addClass = function(){}
nodes.html = function(){}
return nodes
}
window.$ = window.jQuery

/* Promise基本逻辑
//注意Promise是 window 的API
window.Promise = function(fn){
// ...
return {
then: function(){}
}
}
*/
window.jQuery.ajax = function({url, method, body, headers}){
return new Promise(function(resolve, reject){
let request = new XMLHttpRequest()
request.open(method, url) // 配置request
for(let key in headers) {
let value = headers[key]
request.setRequestHeader(key, value)
}
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status < 300){
resolve.call(undefined, request.responseText)
}else if(request.status >= 400){
reject.call(undefined, request)
}
}
}
request.send(body)
})
}

myButton.addEventListener('click', (e)=>{
let promise = window.jQuery.ajax({
url: '/xxx',
method: 'get',
headers: {
'content-type':'application/x-www-form-urlencoded',
'frank': '18'
}
}).then( // then会返回一个Promise对象,以供调用,进行链式操作
(text)=>{console.log(text)}, //成功就调用这个函数
(request)=>{console.log(request)} //失败就调用这个函数
).then(
(text)=>{console.log('success')}, //上面then里面代码成功了会继续调用这个函数,不管它执行的是它的第一个还是第二个参数(函数)
(request)=>{console.log('fail')} //上面失败了会继续调用这个函数
)
// 等于下面,上面其实就是jq的链式操作
promise.then(
(text)=>{console.log(text)},
(request)=>{console.log(request)}
)
Promise.then(
(text)=>{console.log('success')},
(request)=>{console.log('fail')}
)

})

MDN
阮一峰

123

zerolhao

28 posts
2 categories
43 tags
© 2018 zerolhao
Powered by Hexo
|
Theme — NexT.Muse v5.1.4