GithubHelp home page GithubHelp logo

blog's Introduction

Hi! 👋

I'm PLDaily, a web developer from Hangzhou, China.

For enquiries, reach out Gmail or over on Twitter.

Stay awesome!

blog's People

Contributors

pldaily avatar

Stargazers

 avatar

Watchers

 avatar  avatar

blog's Issues

Generate与yield的使用详解

Generator很像是一个函数,但是你可以暂停它的执行。你可以向它请求一个值,于是它为你提供了一个值,但是余下的函数不会自动向下执行直到你再次向它请求一个值.

环境搭建

ES6环境

使用示例

function* generateNaturalNumber() {
    var i = 0;
    while(i <= 5) {
        yield i;
        i++;
    }
}

for(let i of generateNaturalNumber()) {
	console.log(i);//0, 1, 2, 3, 4, 5
}

var test = generateNaturalNumber();
console.log(test.next());//{value: 0, done: false}
console.log(test.next());//{value: 1, done: false}
console.log(test.next());//{value: 2, done: false}

实现原理

function test() {
	var i = 0;
	return {
		next: function() {
			return i <= 3 ? {value: i++, done: false} : {value: undefined, done: true};
		}
	}
}

var test = test();
console.log(test.next());//{value: 0, done: false}
console.log(test.next());//{value: 1, done: false}
console.log(test.next());//{value: 2, done: false}
console.log(test.next());//{value: 3, done: false}
console.log(test.next());//{value: undefined, done: true}

yield中的特例

function *func() {
	yield [2, 3, 4];
    yield* [2, 3, 4];
}

for(let i of func()) {
	console.log(i);
}
//[2, 3, 4]
//2
//3
//4

传入参数

function* ticketGenerator(){
    for(var i=0; true; i++){
        var reset = yield i;
        if(reset) {i = -1;}
    }
}  

var takeANumber = ticketGenerator();
console.log(takeANumber.next().value);//0
console.log(takeANumber.next().value);//1
console.log(takeANumber.next(true).value);//0
console.log(takeANumber.next(true).value);//0
console.log(takeANumber.next().value);//1

实现Promise

一般的方法

function delay(time, callback){
    setTimeout(function(){
        callback("Slept for "+time);
    },time);
}   

delay(1000,function(msg){
    console.log(msg);
    delay(1200,function(msg){
        console.log(msg);
    });
})

等待1s后出现"Slept for 1000",等待1.2s后出现"Slept for 1200"。

使用generate与yield实现

function delay(time, callback){
    setTimeout(function(){
        callback("Slept for "+time);
    },time);
} 

function* run(resume) {
	yield delay(1000, resume);
	yield delay(1200, resume);
}

function resume(msg) {
	console.log(msg);
	test.next()
}

var test = run(resume);
test.next();

等待1s后出现"Slept for 1000",等待1.2s后出现"Slept for 1200"。

通过 hexo + NexT 搭建博客

一般的步骤网上有很多教程都进行了详细的讲解,在此不做复述,主要讲解一下自己在构建博客时遇到的问题及解决方案。

搭建Hexo个人博客无法部署到github

主要原因是由于__config.yml中deploy的配置
解决方案:

deploy:
  type: git
  repository: https://github.com/pcd12321/pcd12321.github.io.git
  branch: master

1.安装

 npm install hexo-deployer-git --save

2.将deploy 的 type由github改为git

上传代码成功后your_user_name.github.io显示404

主要有一下四种原因:
1.github仓库名必须与你的用户名相同
2.在git中输入指令时没有输入你的用户名与邮箱
3.需要等待十几分钟后网站才能正常运行
4.github给你发的邮箱没有打开验证
我是由于邮箱没有验证造成网页显示404

主题配置

主题配置可以通过查看官网配置:NexT官网

如何删除一篇文章

在source目录下先删除该文章,再讲主目录下的db.json删除,使用命令hexo clean清除所有,在用hexo g构建,亲测有效。
注:public文件中的图片等一些在之前文章用到的需要先备份。

如何在文章中添加图片

1._config.yml 中有 post_asset_folder:true
2.执行

npm install https://github.com/CodeFalling/hexo-asset-image --save

3.在文章的目录下新建一个文件夹images放入需要的文件
4.在文章中使用

  ![logo](images/bg1.jpg)

pushState 让 Ajax 改变 URL 且支持后退

很久之前就一直想知道通过ajax加载且支持URL后退的博客的写法是如何实现,但由于当时的水平有限变放弃了。这几天在学习ng的时候想起了以前的这个问题,而且现在也学了许多的新知识,于是决定花些时间将其掌握。

实现基本的html页面

<nav>
  <a href="#/">Home</a>
  <a href="#/blog">blog</a>
  <a href="#/productos">productos</a>
</nav>

<section id="contenido" role="main">

</section>

我们新建三个导航栏,点击不同的导航通过ajax实现在#contenido中显示不同的内容。

点击导航发起ajax请求

我们通过点击导航获得不同的hash值,使ajax请求时发送不同的内容,从而让#contenido中显示不同的内容,我们将其放入函数getContent中

var getContent = function() {
    var hash = window.location.hash;
    if (hash == "#/") { load_page("Home"); } else 
    if (hash == "#/blog") { load_page("Blog"); } else 
    if (hash == "#/productos") {load_page("Productos"); } 
}

load_page中的参数为发起ajax请求时发送的内容

实现load_page

load_page为ajax请求:

var load_page = function(inf){
    $.ajax({
        type: "GET",
        url: "http://localhost:8080/test.php",
        data: {page: inf},
        cache: false,
        success: function(result) {
            $("#contenido").html(result);
        }
    });
}

实现test.php

<?php 
	echo $_GET['page'];
?>

实现URL后退

当每次点击导航时将其href放置到history中,且要发起ajax请求

$("nav a").click(function() {
    var href = $(this).attr("href");
    history.pushState("", "", href);//ajax可前进后退
    getContent();//执行ajax
});

URL后退前进时实现ajax加载

window.onpopstate = function(event) {//点击前进后退时执行
    getContent();
};

初始化

当该页面执行ajax操作后被保存,则在初始化时判断并执行

$(document).ready(function() {
    getContent();//初始化页面
});

源码下载

点击下载提取密码:sva7

vue状态管理

使用vuex实现状态管理

引入vuex

npm install vuex --save
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

使用vuex

  • state:存放状态值
const state = {
	count: 0
}
  • getters:获取状态值
const getters = {
	count: state => state.count
}

组件中通过以下方式获取该状态值

computed: mapGetters([
    'count'
])
  • mutations:定义一个事件类型供 actions 调用,默认会以 state 为参数,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
const mutations = {
  increment (state) {
    state.count++
  },
  decrement (state) {
    state.count--
  }
}
  • actions:定义事件类型供 vue 组件中调用,Action 提交的是 mutation,而不是直接变更状态。
const actions = {
  increment: ({ commit }) => commit('increment'),
  decrement: ({ commit }) => commit('decrement')
}

vue组件中通过以下方式调用

methods: mapActions([
    'increment',
    'decrement'
])

以上代表一个状态值,可存放在一个脚本文件中,例:test.js

test.js

const state = {
	count: 0
}
const getters = {
	count: state => state.count
}
const mutations = {
	increment(state) {
		state.count++
	},
	decrement(state) {
		state.count--
	}
}
const actions = {
	increment: ({ commit }) => commit('increment'),
	decrement: ({ commit }) => commit('decrement')
}
export default {
	state,
	getters,
	mutations,
	actions
}

最终将许许多多的状态模块统一管理存放在store.js文件中

import test from './modules/test'
import test1 from './modules/test1'
import test2 from './modules/test2'
export default new Vuex.Store({
	modules: {
		test,
		test1,
		test2
	}
})

在vue中注册vuex

import Vue from 'vue'
import Vuex from 'vuex'
import store from './vuex/store'
import App from './App.vue'
Vue.use(Vuex)

new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

俩个组件之间的相互绑定

vuex适用于在多个组件中均需用到某一状态的时候,例登陆状态,则需每一个组件均需使用。当某一状态仅仅需要特定的俩个组件之间使用时则不需要使用vuex。

A组件

<template>
    <div>
        <count :num="num" @changeNum="changeNum">
        </count>
        <button @click="addNum">按钮</button>
    </div>
</template>
<script>
	import Count from './count.vue'
	export default {
		data() {
			return {
				num: 0
			}
		},
		components: {
			Count
		},
		methods: {
			addNum() {
				this.num++;
			},
			changeNum(val) {
				this.num = val;
			}
		}
	}

</script>

count组件

<template>
	<div>
		{{num}}
		<button @click="reset">重置</button>
	</div>
</template>
<script>
	export default {
		props: ['num'],
		methods: {
			reset() {
				this.$emit('changeNum', 0);
			}
		}
	}

</script>

jquery源码的静态属性与方法

一直有想去看jquery源码的想法,但一直被网上的关于jquery源码很难的言论吓着了,迟迟没有行动。最近在公司写插件的时候发现自己在代码的架构上知识季度匮乏,一直想找一些代码学习与实践,遂决定看下jquery的源码。本章先记录一些jquery源码的静态属性与方法及一些好的代码书写规范。

释放全局变量的控制权

(function(window) {
	var $ = window.$;
	var $ = 3;
	window.$ = $;
})(window);
console.log($);//undefined

将全局的$指向局部的_$,在全局中$显示undefined

判断javascript数据类型

var class2Type = {
	'[object Object]': 'object',
	'[object Array]': 'array',
	'[object Boolean]': 'boolean',
	'[object Number]': 'number',
	'[object Function]': 'function',
	'[object RegExp]': 'regexp',
	'[object Date]': 'date',
	'[object Error]': 'error',
	'[object String]': 'string'
}
var toString = Object.prototype.toString;
function JStype(obj) {
	return obj == null ? String(obj) : class2Type[ toString.call(obj) ] || 'object';
}

console.log(JStype({}));//object
console.log(JStype([1,2]));//array
console.log(JStype(true));//boolean
console.log(JStype(123));//number
console.log(JStype("123"));//string
console.log(JStype(function() {}));//function
console.log(JStype(new Date()));//date
console.log(JStype());//undefined
console.log(JStype(null));//null
console.log(JStype(undefined));//undefined

1.根据[object, class]对javascript的数据类型作出判断,前者的object表示对象的通用类,默认值;后者表示内部的对象类。
2.object为null与undefined可以直接作出判断,通过String转化为字符型。
3.toString不支持将null、undefined转化为字符型。
4.还可以通过obj + ""转化为字符型。

判断参数是否是数字

isNumber: function(obj) {
	return !isNaN(parsentFloat(obj)) && isFinite(obj);
	//通过parsentFloat将obj转化为数字,isNaN判断是不是非数字
}

判断是不是空对象

isEmptyObject: function(obj) {
	for(var name in obj) {
		return false;
	}
	return true;
}

字符串转化为JSON对象

var aaa = '{"a": 123}';
console.log(JSON.parse(aaa));//{"a": 123}
console.log(eval("("+aaa+")"));//{"a": 123}
console.log((new Function("return " + aaa))());//{"a": 123}

去除俩边空格

var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
function trim( text ) {
	return text == null ?
		"" :
		( text + "" ).replace( rtrim, "" );
}
var aaa = "   aaaaaa";
console.log(aaa);//"   aaaaaa"
console.log(trim(aaa));//"aaaaaa"

支持去除中文空格

类数组对象转化成真正的数组

function abc() {
	var abc = [5,6];
	console.log(arguments);
	console.log(typeof arguments);
	var args = Array.prototype.slice.apply(arguments);//转化为数组
	console.log(arguments.cancat(abc));//error
	console.log(args.concat(abc));//[1,2,3,4,5,6];
}

abc(1,2,3,4);

查找指定元素并返回下标

function isArray(elem, array) {
	//局部变量的定义全部放开头
	var len, i = 0, 
	indexOf = Array.prototype.indexOf;
	if(array) {
		if(indexOf) {
			return indexOf.call(array, elem, i);
		}

		len = array.length;
		for( ; i < len; i++) {
			if(i in array && array[i] == elem) {
				return i;
			}
		}
	}
	return -1;
}
console.log(isArray(4, [1,2,3,4,5]));//3

合并俩个数组(包括伪数组)

function merge(first, second) {
	var len = second.length,
		j = 0,
		i = first.length;

	for ( ; j < len; j++ ) {
		first[ i++ ] = second[ j ];
	}

	first.length = i;

	return first;
}
console.log(merge([1,2,3],[4,5,6]));//[1,2,3,4,5,6]

DOM的封装

(function(win) {

	var global = win;
	var doc = global.document;

	var dom = function(params, context) {
		return new GetOrMakeDom(params, context);
	}

	var GetOrMakeDom = function(params, context) {
		var currentContext = doc;
		if(context) {
			if(context.nodeType) {
				currentContext = context;
			}else {
				currentContext = doc.querySelector(context);
			}
		}

		if(!params || params === '' || typeof params === 'string' && params.trim() === '') {
			this.length = 0;
			return this;
		}

		if(typeof params === 'string' && /^\s*<(\w+|!)[^>]*>/.test(params)) {
			var divElm = currentContext.createElement('div');
			divElm.className = 'doc-frag-wrapper';
			var docFrag = currentContext.createDocumentFragment();
			docFrag.appendChild(divElm);
			var queryDiv = docFrag.querySelector('div');
			queryDiv.innerHTML = params;
			var numberOfChildren = queryDiv.children.length;
			for(var i = 0; i < numberOfChildren; i++) {
				this[i] = queryDiv.children[i];
			}
			this.length = numberOfChildren;
			return this;
		}

		if(typeof params === 'object' && params.nodeName) {
			this.length = 1;
			this[0] = params;
			return this;
		}

		var nodes;
		if(typeof params !== 'string') {
			nodes = params;
		}else {
			nodes = currentContext.querySelectorAll(params.trim());
		}

		var nodeLength = nodes.length;
		for(var i = 0; i < nodeLength; i++) {
			this[i] = nodes[i];
		}

		this.length = nodeLength;
		return this;

	}

	global.dom = dom;

	dom.fn = GetOrMakeDom.prototype;

})(window)

dom.fn.each = function(callback) {
	var len = this.length;

	for(var i = 0; i < len; i++) {
		callback.call(this[i], i, this[i]);
	}

	return this;
}

dom.fn.html = function(htmlStringOrTextString) {
	if(htmlStringOrTextString) {
		return this.each(function() {
			this.innerHTML = htmlStringOrTextString;
		})
	}else {
		return this[0].innerHTML;
	}
}

dom.fn.text = function(textString){
	if(textString){
		return this.each(function(){
			this.textContent = textString;
		});
	}else{
		return this[0].textContent.trim();
	}
};
dom.fn.append = function(stringOrObject) {
	return this.each(function() {
		if(typeof stringOrObject === 'string') {
			this.insertAdjacentHTML('beforeend', stringOrObject);
		}else {
			var that = this;
			dom(stringOrObject).each(function(name,value){
				that.insertAdjacentHTML('beforeend',value.outerHTML);
			});
		}
	})
}

vue1.0与vue2.0的区别

自定义按键

vue.js1.0

<input v-on:keyup.13="submit"> 

vuejs2.0

Vue.config.keyCodes.f1 = 112;
<input v-on:keyup.f1="submit">

示例
https://jsfiddle.net/pcd12321/w83deoo0/

生命周期

vue.js1.0
create -> beforeCompile -> compiled -> ready -> beforeDestroy -> destroyed

vuejs2.0
beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeDestroy -> destroyed

v-html

vuejs1.0

{{{html}}}

vuejs2.0

<div v-html="html"></div>

示例
https://jsfiddle.net/pcd12321/8ayeu77d/

去除$dispatch与$broadcast

使用emit与on的方法
https://jsfiddle.net/pcd12321/650rsgna/2/
该方法为通用方法,既可以用于子级监听父级,也可以用于父级监听子级

父级监听子级事件变化还可通过以下方式实现
https://jsfiddle.net/pcd12321/rbq1n4ps/4/
子级监听父级事件变化可以使用prop但当使用路由时,多个路由中只有一个需要用到父组件中的message,则此时使用prop便不是很好的方法,一般使用事件广播。

propData

创建实例时传递 props。主要作用是方便测试。
示例
https://jsfiddle.net/pcd12321/1qc3d21L/

v-once

html只渲染一次
https://jsfiddle.net/pcd12321/chzkk4c8/6/

v-else-if

示例
https://jsfiddle.net/pcd12321/emrzdrvy/

vm.$set改为Vue.set

node中事件轮询

本文记录了通过实际代码演示node中的事件轮询及轮询中的回调函数的执行方式

服务端代码

User.get = function(name, callback) {
	console.log(44);
	//以下为省略数据库读取操作,读取成功后执行回调函数callback
}


User.test = function(callback) {
	console.log(55);
	callback();
}

User.test2 = function(callback) {
	console.log(66);
	callback();
}

客户端代码

User.get("pcd", function(err, user) {
	console.log(11);
});
User.test(function() {
	console.log(22);
});
User.test2(function() {
	console.log(33);
})

以上代码执行的结果为44,55,22,66,33,11。
由以上代码可知node采用的是非阻塞的I/O机制,不会在程序结束之前阻碍其他处理的进行,在程序处理结束的时候调用回调函数。

node.js中的Buffer模块

本文记录了一些node.js中的Buffer模块的学习笔记,基于最新的文档v6.9.1.

创建缓存区

var buffer1 = new Buffer('Hello World');//老方法
var buffer2 = new Buffer('48656c6c6f204e6f6465', 'hex');//老方法
var buffer3 = new Buffer(1024);//老方法
var buffer4 = Buffer.from('Hello World');//新方法
var buffer5 = Buffer.from('48656c6c6f204e6f6465', 'hex');//新方法
var buffer6 = Buffer.alloc(1024);//新方法

alloc()、allocUnsafe()、allocUnsafeSlow()三者之间的区别

var buffer1 = Buffer.alloc(5);//对原数据进行覆盖
var buffer2 = Buffer.allocUnsafe(5;//还存在原数据,速度更快,通过pool分配内存
var buffer3 = Buffer.allocUnsafeSlow(5);//通过c++分配内存

buffer1相当于buffer2.fill(0)

往缓存区中写入数据

var buffer = Buffer.allocUnsafe(5);
console.log(buffer);//buffer中存在原数据
console.log(buffer.toString());
buffer.write('Hello');
console.log(buffer.toString());//Hello

buf.write(string[, offset[, length]][, encoding])的参数

缓存区数据的获取和设置

var buffer = Buffer.from('Hello World');
console.log(Buffer[3]);//l
console.log(buffer.toString());//Hello
buffer[3] = 109;
console.log(buffer.toString());//Helmo

缓存区数据的解码与转码

var buffer1 = Buffer.from('Hello World');
console.log(buffer1.toString());//Hello World
console.log(buffer1.toString('hex'));//48656c6c6f204e6f6465
console.log(buffer1.toString('base64'));//SGVsbG8gV29ybGQ=
var buffer2 = Buffer.from('48656c6c6f204e6f6465', 'hex');
console.log(buffer2.toString());//Hello World
var buffer3 = Buffer.from('SGVsbG8gV29ybGQ=', 'base64');
console.log(buffer3.toString());//Hello World

缓存区的切分复制

var buffer = Buffer.from('Hello World');
var buffer1 = buffer.slice(2, 5);
console.log(buffer1.toString());//llo 

var longBuffer = Buffer.from('this is a long Buffer');
var smallBuffer = Buffer.alloc(5);
longBuffer.slice(5, 10).copy(smallBuffer, 0, 0, 5);//从smallBuffer的第1位开始写入longBuffer.slice(5, 10)的第1到第5位
console.log(smallBuffer.toString());//is a

node.js中async中间件之流程处理

本文记录了一些node.js中的async中间件的学习笔记。

series按照顺序依次执行

async.series({
	one: function(callback) {
		callback(null, 1);
	},
	two: function(callback) {
		callback(null, 1);
	}
}, function(err, results) {
	console.log(results);//{one: 1, two: 1}
})

series实现原理

function async(arg, callback) {
  console.log('do something with \''+arg+'\', return 1 sec later');
  setTimeout(function() { callback(arg * 2); }, 1000);
}

function final() { console.log('Done', results); }

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function series(item) {
  if(item) {
    async( item, function(result) {
      results.push(result);
      return series(items.shift());
    });
  } else {
    return final();
  }
}
series(items.shift());

waterfall执行方式与series相同,但函数产生的值都会传递给下一个函数

async.waterfall([
	function(callback){
		callback(null, 'one', 'two');
	},
	function(arg1, arg2, callback){
	  // arg1 now equals 'one' and arg2 now equals 'two'
		callback(null, 'three');
	},
	function(arg1, callback){
		// arg1 now equals 'three'
		callback(null, 'done');
	}
], function (err, result) {
   // result now equals 'done'
   console.log(result);
});

parallel表示并发,所有函数一起执行,返回的结果与声明的顺序相同

async.parallel([
	function(callback){
		callback(null, 'one');
	},
	function(callback){
		callback(null, 'two');
	}
],
function(err, results){
	console.log(results);//['one', 'two']
});

parallelLimit限制并发数

async.parallelLimit([
	function(callback){
		callback(null, 'one');
	},
	function(callback){
		callback(null, 'two');
	},
	function(callback){
		callback(null, 'one');
	},
	function(callback){
		callback(null, 'two');
	},
	function(callback){
		callback(null, 'one');
	}
	
],
2,
function(err, results){
	console.log(results);
})

parallelLimit实现原理

function async(arg, callback) {
  console.log('do something with \''+arg+'\', return 1 sec later');
  setTimeout(function() { callback(arg * 2); }, 1000);
}
function final() { console.log('Done', results); }

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;

function launcher() {
  while(running < limit && items.length > 0) {
    var item = items.shift();
    async(item, function(result) {
      results.push(result);
      running--;
      if(items.length > 0) {
        launcher();
      } else if(running == 0) {
        final();
      }
    });
    running++;
  }
}

node.js中的fs模块

本文记录了一些node.js中的fs模块的学习笔记,基于最新的文档v6.9.1。

fs.stat用于查询文件信息,可用于判断文件是否存在

var fs = require('fs');
//返回的信息包括文件的大小、创建的日期等信息
fs.stat('./test/test.js', function(err, stats) {
	//返回的信息包括文件的大小、创建的日期等信息
	console.log(stats);
	//stats.isFile()判断是否是标准文件
	console.log(stats.isFile());//true
	//stats.isDirectory()判断是否是目录
	console.log(stats.isDirectory());//false
	if(stats&&stats.isFile()) {
		console.log('文件存在');
	}else {
		console.log('文件不存在或不是标准文件');
	}
})

fs.access()用于检查到指定path路径的目录或文件的访问权限

fs.F_OK - 文件是对于进程是否可见,可以用来检查文件是否存在
fs.R_OK - 文件对于进程是否可读
fs.W_OK - 文件对于进程是否可写

fs.access('./test/test.js', fs.F_OK, function(err) {
	if(err) {
		console.log('文件不存在');
	}else {
		console.log('文件存在');
	}
})
fs.access('./test/test.js', fs.R_OK | fs.W_OK, function(err) {
	if(err) {
		console.log('不可读写');
	}else {
		console.log('可读或写');
	}
})

fs.open用于打开文件

var fs = require('fs');
fs.stat('./test/test.js', function(err, stats) {
	if(stats.isFile()) {
		//r打开文本进行读取操作,数据流位置在文件起始处
		//r+打开文本进行读写操作,数据流位置在文件起始处
		//w打开文件进行写入,如果文件存在,将其清零,不存在创建写入文件,数据流位置在文件起始处
		//w+打开文件进行读写,w如果文件存在,将其清零,不存在创建写入文件,数据流位置在文件起始处
		//a打开文件进行写入,如果文件存在,将其清零,不存在创建写入文件,数据流位置在文件结尾处
		//a+打开文件进行读写,w如果文件存在,将其清零,不存在创建写入文件,数据流位置在文件结尾处
		fs.open('./test/test.js', 'r', function(err, fd) {
		})
	}
})

fs.read用于文件读取

var fs = require('fs');
fs.stat('./test/test.js', function(err, stats) {
	if(stats.isFile()) {
		fs.open('./test/test.js', 'r', function(err, fd) {
			var buffer = Buffer.alloc(1024);
			var offset = 0;//buffer存储开始的位置
			var len = buffer.length;
			var filePosition = 0;//文件开始读取的位置

			fs.read(fd, buffer, offset, len, filePosition, function(err, readByte) {
				console.log('读取数据总数:'+readByte+' bytes' );		
				console.log(buffer);
    		    console.log(buffer.slice(0, readByte));     //数据已被填充到readBuffer中
			})

		})
	}
})

fs.readFile用于读取文件全部内容

fs.readFile('./test/test.js', function(err, data) {
	console.log(data.toString());
})

fs.read与fs.readFile的区别

fs.read可用于读取部分内容,而fs.readFile只能读取全部内容

fs.write用于写入文件

var fs = require('fs');
fs.stat('./test/test.js', function(err, stats) {
	if(stats.isFile()) {
		fs.open('./test/test.js', 'a', function(err, fd) {
			var buffer = Buffer.from("Hello World");
			var offset = 0;//buffer开始写入的位置
			var len = buffer.length;
			var filePosition = null;//文件写入的位置

			fs.write(fd, buffer, offset, len, filePosition, function(err, writeByte) {
				console.log('写入数据总数:'+writeByte+' bytes' );		
			})
		})
	}
})

fs.close用于关闭文件

var fs = require('fs');
fs.open('./test/test.js', 'r', function(err, fd) {
	//对文件的一些操作
	fs.close(fd, function(err) {
		console.log(err);
	})
})

fs.unlink用于文件删除

var fs = require('fs');
s.stat('./test/test.js', function(err, stats) {
	if(stats.isFile()) {
		fs.unlink('./test/test.js');
	}
})

fs.readdir用于读取当前目录

var fs = require('fs');
fs.readdir('./', function(err, files) {
	console.log(files);//['main.js', 'test']
})

fs.mkdir用于目录创建

var fs = require('fs');
var path = require('path');
fs.mkdir(path.join(__dirname, './test1'), function(err) {
	fs.readdir('./', function(err, files) {
		console.log(files);//['main.js', 'test', 'test1']
	})
})

fs.rmdir用于目录删除

var fs = require('fs');
var path = require('path');
fs.rmdir(path.join(__dirname, './test1'), function(err){
	fs.readdir('./', function(err, files) {
		console.log(files);//['main.js', 'test']
	})
})

fs.rename用于文件目录的的重命名与移动

var fs = require('fs');
fs.rename('./test/test.js', './test1.js', function(err) {
	console.log('移动与重命名成功');
})

fs.createReadStream()创建可读流

var fs = require('fs');
readStream.on('open', function(fd) {
	console.log('文件已打开');
})

readStream.on('data', function(data) {
	console.log('收到文件数据');
	console.log(data.toString());
})

fs.createWriteStream()创建可写流

var writeStream = fs.createWriteStream('./test/test.js');
var buffer = Buffer.from("PCD");
writeStream.write(buffer);

JS去抖与节流

函数节流

实现

window.throttle = function(func, wait, options) {
	var context, args, result;
	var timer = null;
	var previous = 0;
	if(!options) options = {};
	var later = function() {
		previous = new Date().getTime();
		timer = null; 
		result = func.apply(context, args);
		context = args = null;
	}
	return function() {
		var now = new Date().getTime();
		if(!previous && options.leading === false) previous = now;
		var remaining = wait - (now - previous);
		context = this;
		args = arguments;
		if(remaining <= 0) {
			if(timer) {
				clearTimeout(timer);
				timer = null;
			}
      		previous = now;
			result = func.apply(context, args);
			context = args = null;
		}else if(!timer && options.trailing !== false) {
			timer = setTimeout(later, remaining);
		}
		return result;
	}
}

调用示例

window.addEventListener('scroll', _.throttle(abc, 500, {leading: false, trailing: true}));

参数说明

func wait options
需要执行的函数 多少时间内不重复执行该函数 可选参数

options参数说明

leading trailing
触发事件前是否先执行一次函数 最后一次在wait时间内的函数是否执行

在wait内只执行一次函数
throttle 策略的电梯。保证如果电梯第一个人进来后,15秒后准时运送一次,不等待。如果没有人,则待机。

函数去抖

实现

window.debounce = function(func, wait, immediate) {
	var timer, args, context, timestamp, result;

	var later = function() {
		var last = new Date().getTime() - timestamp;
		if(last < wait && last > 0) {
			timer = setTimeout(later, wait - last);
		}else {
			timer = null;
			if(!immediate) {
				result = func.apply(context, args);
				context = args = null;
			}
		}
	}

	return function() {
		context = this;
		args = arguments;
		timestamp = new Date().getTime();
		var callNow = immediate && !timer;
		if(!timer) timer = setTimeout(later, wait);
		if(callNow) {
			result = func.apply(context, args);
			context = args = null;
		}
		return result;
	}
}

调用示例

window.addEventListener('scroll', _.debounce(abc, 500, true))

参数说明

func wait immediate
需要执行的函数 多少时间内不执行该函数 可选参数

immediate参数说明

true false
在wait事件内多次触发事件以第一次为准 在wait事件内多次触发事件以最后一次为准

在wait内连续点击以第一次或最后一次为准,wait时间内再次点击则wait时间重新开始计时。
debounce 策略的电梯。如果电梯里有人进来,等待15秒。如果又人进来,15秒等待重新计时,直到15秒超时,开始运送。

参考

underscore源码

node.js中的url

本文记录了一些node.js中的url模块的学习笔记,基于最新的文档v6.9.1.

url.parse用于解析url字符串

var url = require('url');
var urlString = "http://user:[email protected]:8080/p/a/t/h?query=string#hash";
console.log(url.parse(urlString));
/*{
	"protocol":"http:",
	"slashes":true,
	"auth":"user:pass",
	"host":"host.com:8080",
	"port":"8080",
	"hostname":"host.com",
	"hash":"#hash",
	"search":"?query=string",
	"query":
	"query=string",
	"pathname":"/p/a/t/h",
	"path":"/p/a/t/h?query=string",
	"href":"http://user:[email protected]:8080/p/a/t/h?query=string#hash"
}
*/

url.format将url对象转化为字符串

var url = require('url');
var urlJson = {
	"protocol":"http:",
	"slashes":true,
	"auth":"user:pass",
	"host":"host.com:8080",
	"port":"8080",
	"hostname":"host.com",
	"hash":"#hash",
	"search":"?query=string",
	"query":
	"query=string",
	"pathname":"/p/a/t/h",
	"path":"/p/a/t/h?query=string",
	"href":"http://user:[email protected]:8080/p/a/t/h?query=string#hash"
}

console.log(url.format(urlJson));//"http://user:[email protected]:8080/p/a/t/h?query=string#hash"

css选择器

child选择器

.parent p:first-child {
	color:green;
}
.parent p:last-child {
	color: red;
}
.parent p:nth-child(1) {
	color:yellow;
}
.parent p:nth-last-child(1) {
	color:blue;
}
  • :first-child: parent类中所有p的父元素中p作为第一个子元素时字体颜色为绿色
  • :last-child: parent类中所有p的父元素中p作为最后一个子元素时字体颜色为红色
  • :nth-child(n): parent类中所有p的父元素中p作为第n个子元素时字体颜色为黄色
  • :nth-last-child(1): parent类中所有p的父元素中p作为倒数n个子元素时字体颜色为蓝色

type选择器

.parent p:first-of-type {
	color:green;
}
.parent p:last-of-type {
	color:red;
}
.parent p:nth-of-type(2) {
	color:yellow;
}
.parent p:nth-last-of-type(2) {
	color:blue;
}
  • :first-of-type: parent类中所有p的父元素中第一个标签为p的子元素的字体颜色为绿色且p要为第一个标签
  • :last-of-type: parent类中所有p的父元素中最后一个标签为p的子元素的字体颜色为红色且p要为最后一个标签
  • :nth-of-type(n): parent类中所有p的父元素中第n个标签为p的子元素的字体颜色为黄色且p要为第n个标签
  • :nth-last-of-type(n): parent类中所有p的父元素中倒数第n个标签为p的子元素的字体颜色为蓝色且p要为倒数第n个标签

not选择器

.parent p:not(.first) {
	color: red;
}
  • :not(): parent类中class类名不为first的p元素的字体颜色为红色

注意事项

  • 以上选择均为parent类中的所有子元素(包括子元素的子元素),如果只想获取到一级子元素(即不包括子元素的子元素),则使用">"。
    示例:
.parent > p:first-child {
	color: red;
}

字符编码详解

ASCLL码

ASCLL: 上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。
ASCII码一共规定了128个字符的编码,比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0

Unicode

Unicode: 将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储,不同标准占的字节数不同(大于1字节)

UTF-8

UTF-8: UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:

对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

英文在unicode中占2-4个字节(不同标准占的字节数不同,总之大于1字节),但utf-8中占一个字节

Unicode UTF-8
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-16

UTF-16:就是任何字符对应的数字都用两个字节来保存.但是很显然如果都是英文字母这做有点浪费,但汉字在utf-16中占2个字节,但在utf-8中可能占3个字节。

JS中的字符编码

JS中字符以UTF-16的格式存储,每个字符为为2个字节,可存字符为2^8*2^8个字符,但超出这个范围即Unicode编码大于65536,则通过四个字节存储。 以古文"𣦵"为例,Unicode值为145845,大于65536,以4个字节存储。 现有的charAt、charCodeAt只能处理Unicode编码范围内的字符,ES6引入codePointAt方法

var s = "𣦵";
console.log(s.codePointAt(0));//145845
charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数

JS中的编码解码函数

函数 描述
decodeURI() 解码某个编码的 URI。
decodeURIComponent() 解码一个编码的 URI 组件。
encodeURI() 把字符串编码为 URI。
encodeURIComponent() 把字符串编码为 URI 组件。
escape() 对字符串进行编码。(以弃用)
unescape() 对由 escape() 编码的字符串进行解码。(以弃用)

示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>编码/解码</title>
</head>
<body>
    <script type="text/javascript">
    var test = "http://www.test.com/My test/";

    var test1 = encodeURI(test);
    var test2 = decodeURI(test1);
    var test3 = encodeURIComponent(test);
    var test4 = decodeURIComponent(test3);

    console.log(test1);/*
    http://www.test.com/My%20test/
    */
    console.log(test2);/*
    http://www.test.com/My test/
    */
    console.log(test3);/*
    http%3A%2F%2Fwww.test.com%2FMy%20test%2F
    */
    console.log(test4);/*
    http://www.test.com/My test/
    */
    </script>
</body>
</html>

redis数据类型

集合类型

特点:唯一,无序性

增加元素

SADD key number [member ...]
SADD letters a b c

删除元素

SREM key number [member ...]
SREM letters a c

获取集合中的元素

SMEMBERS key
SMEMBERS letters

获取集合的长度

SCARD key
SCARD letters

判断元素是否在集合中

SISMEMBER key member
SISMEMBER letters a

随机从集合中获取元素

SRANDMEMBER key [count]
SRANDMEMBER letter 2 // 从集合中获取2个元素(不重复)
SRANDMEMBER letter -2 // 从集合中获取2个元素(可能重复)

随机删除一个集合元素

SPOP key
SMEMBERS letters // a b c d
SPOP letters //随机选中b
SMEMBERS letters //a c d

多集合执行差集运算

SDIFF key [key ...]
SADD setA 1 2 3
SADD setB 2 3 4
SDIFF setA setB //1
SDIFF setB setA //4

多集合执行交集运算

SADD setA 1 2 3
SADD setB 2 3 4
SINTER setA setB //2 3

多集合执行并集运算

SADD setA 1 2 3
SADD setB 2 3 4
SUNION setA setB //1 2 3 4

有序集合类型

特点: 有序

增加元素

ZADD key score member [score member ...]
ZADD key scoreboard 89 Tom 67 Peter 100 David

获取集合中的元素的属性

ZSCORE key member
ZSCORE scoreboard Tom

获取某范围内的集合

ZRANGE key start stop [WITHSCORES]

ZRANGE scoreboard 0 2 //从0开始,只获取可以值
ZRANGE scoreboard 0 -1 //-1表示最后一个元素,只获取key值
ZRANGE scoreboard 0 2 WITHSCORES //获取key与value值

通过value值查找集合中的key

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

ZRANGEBYSCORE scoreboard 80 100 //大于等于80,小于等于100
ZRANGEBYSCORE scoreboard 80 (100 //大于等于80,小于100
ZRANGEBYSCORE scoreboard (80 +inf //大于80,+inf为正无穷
ZRANGEBYSCORE scoreboard 60 +inf LIMIT 1 3 //分数高于60分的从第二个人开始的3个人

增加某个元素的分数

ZINCRBY key increment member

ZINCRBY scoreboard -4 Tom

onmouseenter与onmouseover的区别

<div id="home" style="width: 100px; height: 100px; background-color: red;">
    <div id="div1"  style="width: 50px; height: 50px; background-color: green;">
        
    </div>
</div>
var oHome = document.getElementById("home");
oHome.onmouseenter = function() {
    console.log('enter');
}
var oHome = document.getElementById("home");

oHome.onmouseover = function() {
    console.log('over');
}

当鼠标移入子元素#div1时,onmouseover事件会冒泡到父元素从而触发事件,而onmouseenter不会发生事件冒泡。

跨域处理之JSONP

关于前端的跨域问题一直都想去了解应该如何去解决,但由于没有实际例子,看了网上的很多例子一直都处于懵懂的状态,正好公司最近出了一个跨域的问题就随便学习总结了一下。

运用情景

情景说明:俩个子域名一个biz,一个user_center,后台使用thinkphp的框架,biz下的一个用户反馈的邮箱的方法写在user_center项目中,当点击邮箱要发送电子邮件的时候就涉及到了跨域。由于jquery的跨域只支持get的方式,不支持post,本文主要讲解jquery的get方式的跨域处理

前端JS脚本发起异步请求

$.ajax({
    type: 'get', //请求的方式
    url: 'http://user.cli.me/service/index/send',//请求的地址
    data: data,//传输的数据
    dataType: 'jsonp',
    jsonp: 'callback',
    jsonpCallback: 'abc',
    success:function(ret){
        //成功后的操作
    }
});

脚本在biz.cli.me的页面下,对user.cli.me的页面发起一个异步请求

后台thinkphp中的输出处理

public function send(){
	//对传过来data数据的处理操作省略
	$this->_ajax(1,'发送成功',4);
}
private function _ajax($status=null,$msg=null,$data=null){
	$arr['status'] = $status;
	$arr['data'] = $data;
	$arr['msg'] = $msg;
	if(isset($_GET['callback'])) {
		$callback = GET("callback");//获取callback,相当于客户端与服务端的一个通信
		$json = $this->_json($arr);
		$jsonp = $callback.'(' . $json . ')';
		echo $jsonp;
	}else {
		echo $this->_json($arr);
	}
	exit;
}
//转化成JSON数据
private function _json($arr){
	if(is_array($arr)) return json_encode($arr);
}

后台先是通过get获取前台的callback,形成前台与后台的通信。再是对需要返回的数据进行处理,使用jsonp形式输出。

成功返回后的结果

  1. 成功后返回的数据为
abc({"status":1,"data":4,"msg":"\u53d1\u9001\u6210\u529f"});

该数据只是示例,可以按自己的实际需求输出

  1. jsonpCallback设置为什么值则jsonp中的abc则显示为什么
  2. 前端脚本异步请求返回的数据
success: function(ret) {
	console.log(ret);//{status: 1, data: 4, msg: "发送成功"}	
}

去除laravel5框架url中的public与index.php

去除public

  • 将根目录下的server.php文件重命名为index.php。
  • 将public目录下的.htaccess文件复制一份到根目录

去除index.php

  • 开启Apache中的rewrite服务(去除前面的“#”号)
LoadModule rewrite_module modules/mod_rewrite.so
  • 修改.htaccess中的内容中的对应代码为

    RewriteEngine On

    RewriteCond %{THE_REQUEST} \s/+index.php?([^\s&]+) [NC]
    RewriteRule ^ %1? [R=301,L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^([^/]+)/?$ index.php?$1 [L,QSA]

内联元素的使用定位的初始位置

使用text-align使内联元素居中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
    * {
        padding: 0;
        margin: 0;
    }
    #div1 {
        width: 500px;
        height: 200px;
        background: yellow;
        text-align: center;
    } 
    #div2 {
        width: 100px;
        height: 100px;
        background-color: red;
        display: inline-block;
    }
    </style>
</head>
<body>
    <div id="div1">
        <div id="div2">
            
        </div>
    </div>
</body>
</html>

将红块div设置为内联块元素,黄块div设置text-align,此时的红块居于黄块中间。

内联元素使用局对定位absolute时

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
    * {
        padding: 0;
        margin: 0;
    }
    #div1 {
        width: 500px;
        height: 200px;
        background: yellow;
        text-align: center;
        position: relative;
    } 
    #div2 {
        position: absolute;
        width: 100px;
        height: 100px;
        background-color: red;
        display: inline-block;
    }
    </style>
</head>
<body>
    <div id="div1">
        <div id="div2">
            
        </div>
    </div>
</body>
</html>

此时的红块左边框处于居中位置,并不是红块整体居中。

内联元素使用相对定位relative时

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
    * {
        padding: 0;
        margin: 0;
    }
    #div1 {
        width: 500px;
        height: 200px;
        background: yellow;
        text-align: center;
        position: relative;
    } 
    #div2 {
        position: relative;
        width: 100px;
        height: 100px;
        background-color: red;
        display: inline-block;
    }
    </style>
</head>
<body>
    <div id="div1">
        <div id="div2">
            
        </div>
    </div>
</body>
</html>

红块整体处于居中位置。

造成以上的原因是由于绝对定位使元素脱离文档流。

vuejs与jquery的区别

VUE以组件的形式实现模块化,提高复用性

适用于前后端分离

后端提供数据接口,前端实现数据展示及交互

MVC与MVVM

vue属于MVVM,jquery属于MVC

MVC: view-Controller-Model
View: 用于展示数据
Model: 用于管理数据
Controller: 响应用户操作,并将Model更新到View上
Controller层上要进行事件绑定,响应用户操作,更新View等事务,jquery就是为了方便这些操作应运而生
缺点: 
1.开发者处理大量的DOM API,处理繁琐,代码难以维护
2.大量DOM操作使页面渲染性能降低,影响用户体验
3.MOdel频繁更改,开发者需要主动更新到View层,用户操作影响Model层数据,用户需要将数据同步,十分繁琐
MVVM: Model-View-ViewModel
Model: 代表数据
View: 数据模型转化成UI展现出来
ViewModel: 同步View 和 Model的对象
ViewModel通过双向数据绑定把View层和Model层,
开发者不需要过多关注数据同步问题,不需要手动操作DOM。只需关注业务逻辑

react写Hello World

方式一

ReactDOM.render(
  <h1>Hello World</h1>,
  document.getElementById('root')
);

方式二

var App = <h1>Hello World</h1>
ReactDOM.render(
  App,
  document.getElementById('root')
);

方式三

class App extends React.Component {
	constructor(props) {
		super(props);
		this.state = {};
	}
	render() {
		return (
			<h1>Hello World</h1>
		)
	}
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

方式四

var App = React.createClass({
	getInitialState: function() {
		return {}
	},
	render: function() {
		return (
			<h1>Hello World</h1>
		)
	}
})
ReactDOM.render(
  <App />,
  document.getElementById('root')
);

node.js中的path模块

本文记录了一些node.js中的path模块的学习笔记,基于最新的文档v6.9.1.

path.basename()

console.log(path.basename('/foo/bar/baz/asdf/quux.html'));
  // returns 'quux.html'

console.log(path.basename('/foo/bar/baz/asdf/quux.html', '.html'));
  // returns 'quux'

console.log(path.basename('/foo/bar/baz/asdf/quux.html', '.js'));
  // returns 'quux.html'

path.basename()方法可以提取出一个文件路径中的文件的部分。第二个参数为可选扩展名,指定扩展名则只返回文件名,指定扩展名不合法时将返回文件全名

path.dirname()

console.log(path.dirname('/foo/bar/baz/asdf/quux'));
// returns '/foo/bar/baz/asdf'

path.dirname()方法可以提取出一个文件路径中的目录的部分

path.extname()

console.log(path.extname('index.html'));
// returns '.html'

console.log(path.extname('index.coffee.md'));
// returns '.md'

console.log(path.extname('index.'));
// returns '.'

console.log(path.extname('index'));
// returns ''

console.log(path.extname('.index'));
// returns ''

path.extname()方法可以提取文件的扩展名

path.join()

console.log(path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'));
// returns '/foo/bar/baz/asdf'

path.join()方法可以连接任意多个路径字符串

path.normalize()

console.log(path.normalize('/foo/bar//baz/asdf/quux/..'));
// returns '/foo/bar/baz/asdf'

path.normalize()方法对路径进行规范化处理

path.parse()

console.log(path.parse('/home/user/dir/file.txt'));
// returns
// {
//    root : "/",
//    dir : "/home/user/dir",
//    base : "file.txt",
//    ext : ".txt",
//    name : "file"
// }

path.parse()方法对路径进行解析

path.format()

console.log(path.format({
    root : "C:\\",
    dir : "C:\\path\\dir",
    base : "file.txt",
    ext : ".txt",
    name : "file"
}));
//returns c:\path\dir\file.txt

path.format()从对象返回一个字符串,与path.parse()相反

path.resolve()

console.log(path.resolve('/foo/bar', './baz'));
// returns '/foo/bar/baz'

console.log(path.resolve('/foo/bar', '/tmp/file/'));
// returns '/tmp/file'

path.resolve()方法可以将多个路径解析为一个规范化的绝对路径

path.relative()

console.log(path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb'));
// returns '..\\..\\impl\\bbb'

path.relative()方法可以找出一个绝对路径到另一个绝对路径的相对关系,从前者的路径去找后者

path.isAbsolute()

console.log(path.isAbsolute('/foo/bar')); // true
console.log(path.isAbsolute('/baz/..'));  // true
console.log(path.isAbsolute('qux/'));     // false

path.isAbsolute()方法判断路径是否是绝对路径

node.js中的request模块

本文记录了一些node.js中的request模块的学习笔记。

简单的request的使用

request('https://www.baidu.com').pipe(fs.createWriteStream('aaa.html'));

request监听的事件

request
.get('http://itbilu.com/images/logo.png')
.on('error', function(err) {
	console.log(err);
})
.on('response', function(response) {
	console.log(response.statusCode);
	console.log(response.headers['content-type']);
})
.pipe(fs.createWriteStream('doodle.png'));

request结合http

http.createServer(function(req, res) {
	request.get('https://baidu.com').pipe(res);
}).listen(3000);
http.createServer(function(req, res) {
	console.log(req.method);
	if(req.url == '/images/logo.png') {
		var x = request('http://itbilu.com/images/logo.png');
		req.pipe(x);
		x.pipe(res);
	}
}).listen(3000);

request实现表单提交

var formData = {
  file1: fs.createReadStream(__dirname + '/doodle.png'),
};
//localhost:3000是本地搭建的一个express博客的地址,用于表单提交测试
request.post({url:'http://localhost:3000/upload', formData: formData}, function optionalCallback(err, httpResponse, body) {
  if (err) {
    return console.error('upload failed:', err);
  }
  console.log('Upload successful!  Server responded with:', body);
});

基本数据类型与引用类型

基本数据类型

undefined、null、String、Number、Boolean按值传递

引用类型

当一个变量像另一个变量复制引用类型的值时,同样也会将存储在变量中的值复制一份放到为新变量分配的空间中,不同的是,这个值的副本实际是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,俩个变量实际上引的是同一个对象。

var a = 1;
b = a;
a = 2;
console.log(a)//2
console.log(b);//1

var c = {
  a: 1
}
var d = c;
c = {
  a: 2
}

console.log(c.a);//2
console.log(d.a);//1


function abc() {
  this.a = 1;
}

var e = new abc();
var f = e;
e.a = 2;
console.log(f.a);//2

WEB客户端储存

web客户端存储主要包括cookie、localStorage与sessionStorage。本文详细介绍三者之前的区别。

cookie

cookie的保存

cookie的名/值中的值不允许包含分号、逗号和空白号,采用encodeURIComponent对值进行编码,decodeURIComponent进行解码

cookie设置的参数

key:名
value:值
path:路径
expires:过期时间(该属性有HTTP1.0定义,支持IE8。max-age为HTTP1.1定义,不支持IE8)

存储cookie

function setCookie(name, value, expires, path) {
    document.cookie =name + '=' + encodeURIComponent(value) + ';expires = ' + expires + ';path = ' + path;
}

setCookie("name", "pcd", 1000*60*60, '/');

获取cookie

function getCookie(key) {
    var all = document.cookie;
    var list = all.split(';');
    if(all === '') {
        return cookie;
    }
    for(var i = 0; i < list.length; i++) {
        var cookie = list[i].trim();//去除空白字符
        var p = cookie.indexOf('=');
        var name = cookie.substring(0, p);
        if(name == key) {
            var value = cookie.substring(p + 1);
            value = decodeURIComponent(value);
            return value;
        }
    }

    return false;
}

console.log(getCookie('name'));//pcd

cookie的局限性:

因为每一次HTTP请求会把这些数据传输到服务器,所以浏览器对每个cookie的保存数据不能超过4k。

localStorage与sessionStorage

兼容性:支持IE8及IE8+

容量:5-10M

区别:

  1. localStorage存储的数据是永久的,除非刻意删除
  2. sessionStorage存储的数据是暂时的,到浏览器关闭前
  3. sessionStorage作用域是限制在顶层窗口,同源共享限于同一标签页面,例:两个标签页运行同一同源页面,其中一个页面对sessionStorage进行修改操作时,另一页面无法获取该操作后的属性。
    4.localStorage与sessionStorage不同,例:两个标签页运行同一同源页面,其中一个页面对localStorage进行修改操作时,另一页面就会接收到一个存储事件,即可以获取该操作后的属性。

为存储事件注册处理程序

window.addEventListener('storage', function(e) {
    console.log(e.key + "--" + e.newValue + "--" + e.oldValue + "--" + e.storageArea + "--" + e.url);
}, true)

localStorage使用广播机制。

使用示例:一个基于WEB的图片编辑应用,通常允许其他窗口展示工具条。当用户选择一个工具时,应用使用localStorage存储当前状态,便可通知其他窗口用户选择了新工具(其他窗口通过存储事件监听)。

注意事项:浏览器只支持存储字符串类型数据

对象精讲

在看jquery源码时遇到一些关于对象的问题,决定再回去学习巩固一下对象的基础。

构造函数

创建构造函数,从而自定义对象类型的属性和方法。构造函数始终都应以一个大写字母开头,该做法借鉴自其它OO语言,为了区别与其他函数。我们使用new操作符,创建一个实例。每创建一个实例,构造函数内的方法就都要在每个实例上重新创建一遍,但俩个方法不是同一个Function实例。

    function Person(name) {
        this.name = name;
        this.sayName = function() {
            console.log(this.name);
        }
    }
    var person1 = new Person('aaa');
    var person2 = new Person('bbb');
    console.log(person1.sayName == person2.sayName);//false

所以我们需要使用原型共享一些属性与方法

原型

原型的共享

javascript中每一个函数都有一个原型属性,prototype就是通过调用构造函数创建的那个对象实例的原型。使用原型对象可以让所有的对象实例共享它所有的属性和方法。

    function Person(name) {}

    Person.prototype.name = "aaa";
    Person.prototype.sayName = function() {
        console.log(this.name);
    }

    var person1 = new Person();
    var person2 = new Person();
    console.log(person1.sayName == person2.sayName);//true

实例,原型,构造函数三者之间的关系

每个函数都有一个原型属性,这个属性指向函数的原型对象
__proto__存在于实例与构造函数的原型对象之间
原型对象中的constructor属性指向构造函数
构造函数中的this指向实例

new的实现

通过以上的属性,我们可以直接实现new操作符

    function Person(name) {
        this.name = name;
    }
    Person.prototype.sayName = function() {
        console.log(this.name);
    }
    var person1 = {};
    person1.__proto__ = Person.prototype;
    Person.call(person1, "aaa");
    person1.sayName();//aaa

实例读取属性与方法的顺序

当代码读取某个对象的某个属性时,从对象本身开始,到构造函数内部,再到原型去获取。相同的属性会屏蔽其他相同的属性,其他相同的属性并不会消失。

    function Person() {
        this.name = "aaa";
    }
    Person.prototype.name = "bbb";

    var person1 = new Person();

    person1.name = "ccc";

    console.log(person1.name);//"ccc"

重写原型对象(通过对象字面量来重写整个原型对象)

一、实例未创建前

通过对象字面来重写原型对象会将原来的原型对象覆盖,导致原来的原型对象的constructor指向构造函数被新的原型对象所替换,指向了Object。所以需要在对象字面量中添加constructor属性指向构造函数。

constructor的指向

function abc() {
    this.aaa = 1;
}
abc.prototype.bbb = 2;
abc.prototype = {
    constructor: abc,
    "ccc": 3,
    "ddd": 4
}
var a = new abc();
console.log(a.constructor);//abc函数
console.log(abc.prototype.constructor);//abc函数

二、实例创建后

通过对象字面来重写原型对象,该原型对象将不起作用。

重写原型对象代码示例

    function abc() {
        this.aaa = 1;
    }
    abc.prototype.bbb = 2;
    abc.prototype = {
        constructor: abc,
        "ccc": 3,
        "ddd": 4
    }
    var a = new abc();

    abc.prototype.eee = 5;//创建实例后不是重写相当于为重写后的原型对象添加新的属性与方法
    console.log(a.aaa);//1
    console.log(a.bbb);//undefined
    console.log(a.ccc);//3
    console.log(a.ddd);//4
    console.log(a.eee);//5
    function abc() {
        this.aaa = 1;
    }
    abc.prototype.bbb = 2;
    var a = new abc();

    abc.prototype.ccc = 3;//创建实例后不是重写相当于为原来的原型对象添加新的属性与方法
    console.log(a.aaa);//1
    console.log(a.bbb);//2
    console.log(a.ccc);//3
    function abc() {
        this.aaa = 1;
    }
    abc.prototype.bbb = 2;
    
    var a = new abc();

    abc.prototype = {//创建实例后重写原型不起作用
        constructor: abc,
        "ccc": 3,
        "ddd": 4
    }
    abc.prototype.eee = 5;
    console.log(a.aaa);//1
    console.log(a.bbb);//2
    console.log(a.ccc);//undefined
    console.log(a.ddd);//undefined
    console.log(a.eee);//undefined

原型对象的问题

    function Person() {}
    Person.prototype = {
        constructor: Person,
        arrs: [1, 2]
    }
    var person1 = new Person();
    person1.arrs.push(3);
    var person2 = new Person();
    console.log(person2.arrs);//[1, 2, 3]

原型对象共享的本质使新创建的person2获取的属性发放所person1影响。

模式

一、组合使用构造函数模式和原型模式
使用最广的模式,结合了构造函数与原型的优缺点。不过多复述。
二、动态原型模式
独立的构造函数和原型,动态原型模式将他们分装在一个构造函数中。
三、寄生构造函数模式
为某一个对象添加额外的方法
四、稳妥构造函数模式
定义私有的变量和属性,只能通过函数内部的方法去获取。

继承

组合继承

    function SuperType(name) {
        this.name = name;
    }

    SuperType.prototype.sayName = function() {
        console.log(this.name);
    }

    function SubType(name, age) {
        SuperType.call(this, name);//继承构造函数内部的属性与方法
        this.age = age;
    }

    SubType.prototype = new SuperType();//创建一个SuperType的实例将其指向SubType的原型对象
    SubType.prototype.constructor = SuperType;
    SubType.prototype.sayAge = function() {
        console.log(this.age);
    }

    var pcd = new SubType('pcd', 21);
    pcd.sayName();
    pcd.sayAge();

注1:新创建的SuperType实例指向SubType的原型对象,其中也包括了SuperType内部的属性与方法,但还是将SuperType的属性与方法继承到SubType中是因为原型对象中方法和属性具有共享性。
注2:SubType的原型不能写成字面量的形式,否则会将继承的原型对象覆盖。

寄生组合继承

    function SuperType(name) {
        this.name = name;
    }

    SuperType.prototype.sayName = function() {
        console.log(this.name);
    }

    function SubType(name, age) {
        SuperType.call(this, name);//继承构造函数内部的属性与方法
        this.age = age;
    }

    SubType.prototype = SuperType.prototype;
    SubType.prototype.constructor = SuperType;
    SubType.prototype.sayAge = function() {
        console.log(this.age);
    }

    var pcd = new SubType('pcd', 21);
    pcd.sayName();
    pcd.sayAge();

SuperType的原型对象直接赋值给SubType从而实现继承。

注意事项

    function SuperType(name) {
        this.name = name;
    }

    function SubType() {
    }

    SubType.prototype = SuperType.prototype;
    SubType.prototype.constructor = SuperType;
    SubType.prototype.sayName = function() {
        console.log(this.name);
    }

    var pcd = new SuperType('pcd');
    pcd.sayName();//"pcd"

继承中的父类可以使用子类的原型方法

ES6环境搭建

对于前端开发人员来说,ES6一直早有耳闻,但由于运用不到实际而一直没有安排时间去学习,只是对其有一些粗略的了解。近期在实际开发项目中使用了gulp+webpack对vuejs进行编译,对于vuejs的操作是可以支持ES6的,因为可以使用webpack中的babel-loader进行解析。这节主要讲解对ES6的环境进行搭建,在学习的时候可以进行调试。

重要实现思路

通过webpack搭建了一个ES6的环境,在webpack.config.js中定义入口文件问main.js,通过babel对main.js中的ES6的语法进行解析,将其转化为ES5的语法,生成文件为bundle.js。通过index.html引入bundle.js文件进行调试。

webpack.config.js的配置

module.exports = {
  entry: './main',//入口文件
  output: {
    filename: 'bundle.js'//输出文件
  },
  module: {
    loaders: [{
      test: /\.js$/,//正则匹配文件,对其进行解析
      exclude: /node_modules/,//不对node_modules里的js文件进行解析
      loader: 'babel',//使用babel加载器
      query: {
        presets: ['es2015']//解析成ES5的形式
      }
    }]
  }
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>ES6</title>
  </head>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>

package.json

{
  "name": "ES6_environment",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {//可以使用npm run的快捷方式
    "start": "live-server --port=3004",
    "watch": "webpack -w"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.3.17",
    "babel-loader": "^6.2.0",
    "babel-preset-es2015": "^6.3.13",
    "live-server": "^0.9.0",
    "webpack": "^1.12.9"
  }
}

执行命令

$ npm install //安装依赖
$ npm run watch //用于监听文件是否发生变化
$ npm start //打开浏览器监听port:3004

注意事项

以上方法是将ES6的语法编译成ES5后执行,在调试的时候有时不是能很好的体现ES6的属性。也可以通过google浏览器F12在console中直接输入代码编译调试。

flex布局

flex:none | <' flex-grow '> <' flex-shrink '>? || <' flex-basis '>?

参数一为放大值,参数二为缩小值,参数三为基准值

<ul class="flex">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

.flex{display:flex;width:800px;margin:0;padding:0;list-style:none;}
.flex :nth-child(1){flex:1 1 300px;}
.flex :nth-child(2){flex:2 2 200px;}
.flex :nth-child(3){flex:3 3 400px;} 

三个基准值相加为900px大于800px,故需要使用缩小值进行缩小
a: 300 - (1300/(1300 + 2200 + 3400))100) = 284px;
b: 200 - (2
200/(1300 + 2200 + 3400))100) = 179px;
c: 400 - (3
400/(1
300 + 2200 + 3400))*100) = 337px;

<ul class="flex">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

.flex{display:flex;width:1500px;margin:0;padding:0;list-style:none;}
.flex :nth-child(1){flex:1 1 300px;}
.flex :nth-child(2){flex:2 2 200px;}
.flex :nth-child(3){flex:3 3 400px;} 

三个基准值相加为900px小于1500px,故需要使用放大值进行放大
a: 300 + 600 * 1/6 = 400px;
b: 200 + 600 * 2/6 = 400px;
c: 400 + 600 * 3/6 = 700px;

flex-grow: 默认值为0

<ul class="flex">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

.flex{display:flex;width:600px;margin:0;padding:0;list-style:none;}
.flex li:nth-child(1){width:200px;}
.flex li:nth-child(2){flex-grow:1;width:50px;}
.flex li:nth-child(3){flex-grow:3;width:50px;}

300px小于600px,故使用flex-grow放大
a:200px;
b:50 + (600 - 300) * 1 / 4 = 125px;
c:50 + (600 - 300) * 3 / 4 = 275px;

flex-shrink: 默认值为1

<ul class="flex">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

.flex{display:flex;width:400px;margin:0;padding:0;list-style:none;}
.flex li{width:200px;}
.flex li:nth-child(3){flex-shrink:3;}

400px小于600px,故使用flex-shrink缩小
a:200 - (1200 / (1200 + 1200 + 3200)) * 200 = 160px
b:200 - (1200 / (1200 + 1200 + 3200)) * 200 = 160px
c:200 - (3200 / (1200 + 1200 + 3200)) * 200 = 80px

flex-flow:<' flex-direction '> || <' flex-wrap '>

参数一:定义弹性盒子元素的排列方向。
参数二:控制flex容器是单行或者多行。

flex-direction

弹性盒子元素的排列方向(该方向为主轴)

属性 描述
row 主轴为水平方向,起点在左端(默认)
row-reverse 主轴为水平方向,起点在右端
column 主轴为垂直方向,起点在上沿
column-reverse 主轴为垂直方向,起点在下沿

flex-wrap

控制flex容器主轴是单行或者多行。

属性 描述
nowrap 不换行(默认)如果子元素宽度超出或小于父元素宽度,会通过flex-grow、flex-shrink进行扩大缩小
wrap 换行,第一行在上方
wrap-reverse 换行,第一行在下方

align-content

定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。可通过flex-wrap: wrap换行设置多跟轴线

属性 描述
flex-start 与交叉轴的起点对齐。
flex-end 与交叉轴的终点对齐。
center 与交叉轴的中点对齐。
space-between 与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around 每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值) 轴线占满整个交叉轴。

align-items

设置弹性盒子在侧轴方向上的对齐方式

  • 与align-content的区别:该属性对每一个轴均起作用,align-content的多个轴为一个整体起作用
属性 描述
flex-start 与侧轴的起点对齐。
flex-end 与侧轴的终点对齐。
center 与侧轴的中点对齐。
space-between 与侧轴两端对齐,轴线之间的间隔平均分布。
baseline 第一行文字的基线对齐
stretch(默认值) 轴线占满整个侧轴。

justify-content

设置弹性盒子在主轴上的对齐方式

属性 描述
flex-start 左对齐(默认)
flex-end 右对齐
center 居中
space-between 两端对齐,项目之间的间隔都相等。
space-around 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

align-self

设置弹性盒子子元素在侧轴方向上的对齐方式
该属性作用在子元素上,可覆盖align-items

属性 描述
flex-start 与侧轴的起点对齐。
flex-end 与侧轴的终点对齐。
center 与侧轴的中点对齐。
space-between 与侧轴两端对齐,轴线之间的间隔平均分布。
baseline 第一行文字的基线对齐
stretch(默认值) 轴线占满整个侧轴。

order:

设置弹性盒子子元素的排列顺序。数值越小,排列越靠前,默认为0

综上,flex布局通过flex-direction设置主轴,通过flex-wrap设置是否换行(多轴显示)。其中,justify-content作用于主轴,align-content作用于多轴的侧轴,align-item作用于侧轴,align-self作用于子元素的侧轴

session与cookie

http协议是无状态的,但用户浏览不同的页面时,服务器是如何判断用户是否是登录的呢?后端通过setcookie将用户的登录session信息编码后传递给浏览器,浏览器每次访问时携带该信息解码后得到session信息从而判断是否处于登录状态。后端设置max-age限制cookie过期时间,设置http-only禁止cookie被JS访问,避免XSS攻击。

cookie

使用cookie记录用户的状态时,具体分为以下几个步骤

  • 用户登录成功后访问页面时,服务器向用户发送cookie
  • 浏览器保存cookie
  • 每次用户访问页面时会将cookie返回给服务器
  • 服务器根据返回的cookie信息判断用户是否处于登录状态

使用node的express框架做简单的示例

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res, next) {
  	if (req.cookies.isVisit) {
      	console.log(req.cookies);
      	res.send("再次欢迎访问");
    } else {
      	res.cookie('isVisit', 1, {maxAge: 60 * 1000});
      	res.send("欢迎第一次访问");
    }
});

module.exports = app;
  • cookie 中的所有数据在客户端就可以被修改,数据非常容易被伪造。
  • cookie 中数据字段太多会影响传输效率。

session

当你浏览一个网页时,服务端随机产生一个 1024 比特长的字符串,然后存在你 cookie 中的 connect.sid 字段中。当你下次访问时,cookie 会带有这个字符串,然后浏览器就知道你是上次访问过的某某某,然后从服务器的存储中取出上次记录在你身上的数据

使用node的express框架做简单的示例

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');

var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use(session({
  secret: 'test',
  cookie: { maxAge: 60 * 1000 }
}));

app.get('/', function (req, res) {
  if(req.session.isVisit) {
    req.session.isVisit++;
    res.send('<p>第 ' + req.session.isVisit + '次来此页面</p>');
  } else {
    req.session.isVisit = 1;
    res.send("欢迎第一次来这里");
    console.log(req.session);
  }
});

module.exports = app;

node.js中的events模块

本文记录了一些node.js中的events模块的学习笔记,基于最新的文档v6.9.1.

实现一个事件发射器myEmitter

var EventEmitter =  require('events');
//继承方式一
class MyEmitter extends EventEmitter {};
//继承方式二
var util = require('util');
var MyEmitter = function() {
}
util.inherits(MyEmitter, EventEmitter);
var myEmitter = new MyEmitter();

监听事件

myEmitter.on('event1', function(arg1, arg2) {
	console.log(arg1 + "------" + arg2);
})

发射事件

myEmitter.emit('event1', 'arg1', 'arg2');

绑定监听仅一次的事件

var EventEmitter =  require('events');
var util = require('util');
var MyEmitter = function() {

}
util.inherits(MyEmitter, EventEmitter);
var myEmitter = new MyEmitter();

myEmitter.once('eventOnce', function() {
	console.log("one");
})

myEmitter.emit('eventOnce');//one
myEmitter.emit('eventOnce');//无输出

删除绑定事件

var EventEmitter =  require('events');
var util = require('util');
var MyEmitter = function() {

}
util.inherits(MyEmitter, EventEmitter);
var myEmitter = new MyEmitter();

var listenFunc = function() {
	console.log('remove');
}
myEmitter.on('eventRemove', listenFunc)

myEmitter.emit('eventRemove');//remove

myEmitter.removeListener('eventRemove', listenFunc);

myEmitter.emit('eventRemove');//无输出

Object自身属性

通过

Object.getOwnPropertyNames(Object).sort().forEach(function (val) {console.log(val, '\n')});

获取对象的所有自身属性。

arguments

function foo() {
    console.log(arguments);
}

foo(1, 2, 3, 4);//[1, 2, 3, 4]

assign

console.log(Object.assign({"a": 1, "b": 2}, {"b": 3, "c": 4}));//{a: 1, b: 3, c: 4}

caller

严格模式下不可用,不深入了解

create

Object.create() 方法创建一个拥有指定原型和若干个指定属性的对象。

// 创建一个原型为null的空对象
var obj1 = Object.create(null);
// 创建一个原型属性a为1的对象
var obj2 = Object.create({a: "1"});
console.log(obj2.a);//1
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象,该属性不可更改
var obj3 = Object.create({}, { p: { value: 42 } });
console.log(obj3.p);//42
// 多用于继承
function Parent(name) {
    this.name = name;
}
Parent.prototype.show = function() {
    console.log(this.name)
}
function Child(name) {
    Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);

var test = new Child('pcd');
test.show();//pcd

defineProperties

多用于数据劫持,监听多个属性
具体实例:MVVM

var obj = {key1: 11, key2: 22}
function observer(obj, key1, key2) {
    var old1 = obj.key1;
    var old2 = obj.key2;
    Object.defineProperties(obj, {
        key1: {
            enumerable: true,
            configurable: true,
            get: function() {
                return old1;
            },
            set: function(now) {
                if(now !== old1) {
                    console.log(now);
                }
                old1 = now
            }
        },
        key2: {
            enumerable: true,
            configurable: true,
            get: function() {
                return old2;
            },
            set: function(now) {
                if(now !== old2) {
                    console.log(now);
                }
                old2 = now
            }
        }
    })
}

observer(obj, "key1", "key2");
obj.key1 = 2;//2
obj.key2 = 2;//2

defineProperty

多用于数据劫持,监听单个属性
具体实例:MVVM

var obj = {key: 11}
function observer(obj, key) {
    var old = obj.key;
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            return old
        },
        set: function(now) {
            if(now !== old) {
                console.log(now);
            }
            old = now
        }
    })
}

observer(obj, "key");
obj.key = 2;//2

entries

返回一个包含由给定对象所有可枚举属性的属性名和属性值组成的 [属性名,属性值] 键值对的数组
只对最外层进行处理

var obj = { foo: "bar", baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
var obj = { foo: "bar", baz: 42, test: { bb: 1, cc: 2 } };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42], ['test', {bb: 1, cc: 2}] ]

freeze 与 isFrozen

freeze:冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的
isFrozen:判断一个对象是否被冻结

var obj = {
    name: "pcd1",
    age: 22
}

console.log(obj.name);//pcd1
obj.name = "pcd2";
console.log(obj.name);//pcd2
console.log(Object.isFrozen(obj));//false
Object.freeze(obj);
console.log(Object.isFrozen(obj));//true
obj.name = "pcd3";//报错

getOwnPropertyDescriptor

用来获取一个对象的一个自身属性的描述符

var obj = {key: 11}
function observer(obj, key) {
    var old = obj.key;
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            return old
        },
        set: function(now) {
            if(now !== old) {
                console.log(now);
            }
            old = now
        }
    })
}

observer(obj, "key");
console.log(Object.getOwnPropertyDescriptor(obj, "key"));

getOwnPropertyDescriptors

用来获取一个对象的所有自身属性的描述符

var obj = {key1: 11, key2: 22}
function observer(obj, key1, key2) {
    var old1 = obj.key1;
    var old2 = obj.key2;
    Object.defineProperties(obj, {
        key1: {
            enumerable: true,
            configurable: true,
            get: function() {
                return old1;
            },
            set: function(now) {
                if(now !== old1) {
                    console.log(now);
                }
                old1 = now
            }
        },
        key2: {
            enumerable: true,
            configurable: true,
            get: function() {
                return old2;
            },
            set: function(now) {
                if(now !== old2) {
                    console.log(now);
                }
                old2 = now
            }
        }
    })
}

observer(obj, "key1", "key2");
console.log(Object.getOwnPropertyDescriptors(obj));

getOwnPropertyNames

返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组

Object.getOwnPropertyNames(Object).sort().forEach(function (val) {console.log(val, '\n')});
//获取Object的所有自身属性

getPrototypeOf 与 setPrototypeOf

function abc() {
    this.name = "pcd";
}
abc.prototype.sex = "man";
var person = {};
person.__proto__ = abc.prototype;
//Object.setPrototypeOf(person, abc.prototype);
abc.call(person);
console.log(person.name);//pcd
console.log(person.sex);//man
console.log(Object.getPrototypeOf(person));//abc.prototype

isExtensible 与 preventExtensions

isExtensible() 决定一个对象是否可扩展 (即是否能够添加新的属性)
preventExtensions 用來避免物件被新增新的属性
preventExtensions

var empty = {};
console.log(Reflect.isExtensible(empty));//true

Reflect.preventExtensions(empty); 
console.log(Reflect.isExtensible(empty)); //false

seal 与 isSealed

Object.seal() 方法可以让一个对象密封,与冻结freeze的区别是属性可以修改
Object.isSealed: 判断对象是否被密封

var obj = {
    name: "pcd1",
    age: 22
}

console.log(obj.name);//pcd1
obj.name = "pcd2";
console.log(obj.name);//pcd2
console.log(Object.isSealed(obj));//false
Object.seal(obj);
console.log(Object.isSealed(obj));//true
obj.name = "pcd3";
console.log(obj.name);//pcd3

keys 与 values

返回属性及属性值

var arr = ["a", "b", "c"];
console.log(Object.keys(arr));//["0", "1", "2"]
console.log(Object.values(arr));//["a", "b", "c"]

length

多用于数组,数组也为对象

var arr = ["a", "b", "c"];
console.log(arr.length);//3

is

方法用来判断两个值是否是同一个值

  • 与"=="的区别:"=="会做类型转化;
  • 与"==="的区别:
console.log(-0 === +0);//true
console.log(Object.is(-0, +0));//false
console.log(NaN === NaN);//false
console.log(Object.is(NaN, NaN));//true

详见#35

以下方法暂时不做讨论

  • getOwnPropertySymbols

事件详解

冒泡与捕获的优先级

正常冒泡

document.body.addEventListener('click', function() {
  console.log('body');
}, false);

document.getElementById('div1').addEventListener('click', function() {
  console.log('div1');
}, false);
//div1  body

正常捕获

document.body.addEventListener('click', function() {
  console.log('body');
}, true);

document.getElementById('div1').addEventListener('click', function() {
  console.log('div1');
}, true);
//body div1

父元素设为冒泡,子元素设为捕获

document.body.addEventListener('click', function() {
  console.log('body');
}, false);

document.getElementById('div1').addEventListener('click', function() {
  console.log('div1');
}, true);
//div1 body
//捕获优先级高

父元素设为捕获,子元素设为冒泡

document.body.addEventListener('click', function() {
  console.log('body');
}, true);

document.getElementById('div1').addEventListener('click', function() {
  console.log('div1');
}, false);
//body div1
//捕获优先级高

IE model中this的指向

IEModel的实现给绑定处理程序设置了错误的上下文,使的处理程序内的this引用的是全局上下文而不是事件目标元素,使用apply、call或bind将事件绑定在目标元素上。

兼容IEModel与DOMModel的写法

if(document.addEventListener) {
  this.addEvent = function(elem, type, fn) {
    elem.addEventListener(type, fn, false);
    return fn;//返回函数用于解绑
  }
  this.removeEvent = function(elem, type, fn) {
    elem.removeEventListener(type, fn, false);
  }
}else if(document.attachEvent) {
  this.addEvent = function(elem, type, fn) {
    var bound = function() {
      return fn.apply(elem, arguments);
    }//将this的指向elem
    elem.attachEvent('on' + type, bound);
    return bound;//返回函数用于解绑
  }
  this.removeEvent = function(elem, type, fn) {
    elem.detachEvent('on' + type, fn);
  }
}

node.js中的multer中间件

本文记录了一些node.js中的multer中间件的学习笔记。以下的例子基于express框架
###单个文件上传

<form enctype="multipart/form-data" method="post">
        <input type="file" name="uploadInput" />
        <input id='submitFile' type="submit" />
</form>
router.get('/uploadSingle', function(req, res, next) {
	res.render('uploadSingle', {});
});

router.post('/uploadSingle', function(req, res, next) {
	var upload = muilter.single('uploadInput');
	upload(req, res, function(err) {
		console.log(req.file);
	})
})

多个文件上传

<form enctype="multipart/form-data" method="post">
        <input type="file" name="uploadInput" />
        <input type="file" name="uploadInput" />
        <input type="file" name="uploadInput" />
        <input id='submitFile' type="submit" />
</form>
router.get('/uploadArray', function(req, res, next) {
    res.render('uploadArray', {});
});

router.post('/uploadArray', function(req, res, next) {
    var upload = muilter.array('uploadInput', 3);
    upload(req, res, function(err) {
        console.log(req.files);
    })
})

混合文件上传

<form enctype="multipart/form-data" method="post">
        <input type="file" name="uploadInput" />
        <input type="file" name="uploadInput" />
        <input type="file" name="uploadInput1" />
        <input type="file" name="uploadInput1" />
        <input type="file" name="uploadInput2" />
        <input type="file" name="uploadInput2" />
        <input type="file" name="uploadInput2" />
        <input id='submitFile' type="submit" />
</form>
router.get('/uploadFilter', function(req, res, next) {
    res.render('uploadFilter', {});
});

router.post('/uploadFilter', function(req, res, next) {
    var upload = muilter.fields([{ name: 'uploadInput', maxCount: 2 }, { name: 'uploadInput1', maxCount: 2 }, { name: 'uploadInput2', maxCount: 4 }])
    upload(req, res, function(err) {
        if(err) {
            console.log(err);
        }
        console.log(req.files);
    })
});

浮动详解

浮动效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>浮动</title>
    <style type="text/css">
    *{
        padding: 0;
        margin: 0;
    }
    #main {
        background-color: green;
    }
    #div1 {
        background: yellow;
        width: 200px;
        height: 200px;
        float: left;
    }
    #div2 {
        background-color: blue;
        width: 200px;
        height: 200px;
        float: left;
    }
    </style>
</head>
<body>
    <div id="main">
        <div id="div1">
            
        </div>
        <div id="div2">
            
        </div>
    </div>
</body>
</html>

此时父元素#main的高度宽度由子元素#div1与#div2决定,由于子元素设置浮动脱离文档流,导致父元素高度为0,使其父元素#main背景颜色无效果。

清除浮动方式一

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>浮动</title>
    <style type="text/css">
    *{
        padding: 0;
        margin: 0;
    }
    #main {
        background-color: green;
        overflow: hidden;
    }
    #div1 {
        background: yellow;
        width: 200px;
        height: 200px;
        float: left;
    }
    #div2 {
        background-color: blue;
        width: 200px;
        height: 200px;
        float: left;
    }
    </style>
</head>
<body>
    <div id="main">
        <div id="div1">
            
        </div>
        <div id="div2">
            
        </div>
    </div>
</body>
</html>

父元素设置overflow: hidden;属性
原理:因为overflow.hidden会触发BFC(块级排版上下文)。都会为他们的内容创建新的块级格式化上下文

清除浮动方式二

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>浮动</title>
    <style type="text/css">
    *{
        padding: 0;
        margin: 0;
    }
    #main {
        background-color: green;
    }
    #div1 {
        background: yellow;
        width: 200px;
        height: 200px;
        float: left;
    }
    #div2 {
        background-color: blue;
        width: 200px;
        height: 200px;
        float: left;
    }
    .clear{
        clear:both;
        height: 0;
        line-height: 0;
        font-size: 0
    }
    </style>
</head>
<body>
    <div id="main">
        <div id="div1">
            
        </div>
        <div id="div2">
            
        </div>
        <div class="clear">
            
        </div>
    </div>
</body>
</html>

添加一个子元素div,设置clear: both;属性
缺点:每次清除浮动都需要添加一个子元素div

清楚浮动方式三

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>浮动</title>
    <style type="text/css">
    *{
        padding: 0;
        margin: 0;
    }
    #main {
        background-color: green;
    }
    #div1 {
        background: yellow;
        width: 200px;
        height: 200px;
        float: left;
    }
    #div2 {
        background-color: blue;
        width: 200px;
        height: 200px;
        float: left;
    }
    .clear:after { 
        content:"."; 
        display:block; 
        height:0; 
        visibility:hidden; 
        clear:both; 
    } 
    </style>
</head>
<body>
    <div id="main" class="clear">
        <div id="div1">
            
        </div>
        <div id="div2">
            
        </div>
    </div>
</body>
</html>

使用after伪类,则不需要每次清除浮动都新建一个子元素

git指令

作为新一代的青年,当当会用sourcetree怎么能行,上git指令。今后工作中会使用git指令修改代码,会不断进行修改与补充。

基础配置

  • $ git config --global user.name "pcd"//使用者名字
  • $ git config --global user.email "[email protected]"//使用者邮箱
  • $ mkdir testgit//创建文件
  • $ pwd//显示当前目录
  • $ git init// 变成可以管理的仓库
  • $ cat readme.txt //查看内容
  • $ rm b.txt//删除b.txt

提交更改

  • $ git add readme.txt //提交readme.txt文件 放在暂存区
  • $ git reset HEAD //如果后面什么都不跟的话 就是上一次add 里面的全部撤销了
  • $ git reset HEAD XXX //就是对某个文件进行撤销了
  • $ git commit -m "readme.txt提交" //提交的注释
  • $ git status //文件提交后是否有发生改变
  • $ git diff reademe.txt //文件如何发生了改变,查看改变了什么
  • $ git checkout -- readme.txt//git status发生更改,删除更改

查看历史

  • $ git log //查看历史记录
  • $ git log --pretty=oneline //历史记录只显示版权号与注释
  • $ git show 版本号//查看修改了什么
  • $ git log text.txt //查看某一文件的修改历史

版本回退

  • $ git reset --hard HEAD^ //退回到上一个版本
  • $ git reset --hard HEAD^^ //退回到上上个版本
  • $ git reset --hard HEAD~100 //退回到前一百个版本
  • $ git reflog // 可显示退回的历史记录与版本号; $ git log不能
  • $ git reset --hard 6fcfc89 //退回版本时如何恢复,6fcfc89为$ git relog显示的版本号

分支

  • $ git checkout -b dev//创建并切换分支
  • $ git branch dev2 //创建分支
  • $ git checkout dev2 //切换分支
  • $ git branch //查看当前分支
  • $ git merge dev // master合并dev上的内容
  • $ git branch -d dev //删除分支

远程库

  • $ git remote //查看远程库的信息
  • $ git remote -v //详细信息
  • $ git push origin master //将本地的master分支推送到github上
  • $ git checkout -b dev origin/dev //创建远程origin的dev分支到本地来
  • $ git clone https://github.com/PLDaily/blog.git//从远端克隆
  • $ git fetch orign//从远端获取所有分支的更新
  • $ git remote add origin https://github.com/PLDaily/ubuntu-laravel.git//添加远程库
  • $ git pull orign//从远端获取更新并与本地分枝合并,相当于git fetch加git merge

暂存

  • $ git stash 或 git stash save "注释"//暂存
  • $ git stash list//查看所有暂存
  • $ git stash apply stash@{id}//使用暂存
  • $ git stash pop//使用最近一个暂存,与git stash apply stash@{0}的区别是其会从git stash list中删除
  • $ git stash drop stash@{id}//删除茉欧意暂存
  • $ git stash clear//删除所有暂存

DOM节点详解

本文主要介绍DOM的元素、文本与属性三个节点

节点的方法

nodeName:节点名字
nodeValue:节点的值
nodeType:节点的类型常数值

元素节点

DOM的原子是元素节点

<div id="div1" title="div2">div3</div>
console.log(document.getElementById('div1').nodeName);//DIV
console.log(document.getElementById('div1').nodeValue);//null
console.log(document.getElementById('div1').nodeType);//1

文本节点

<div id="div1" title="div2">div3</div>
console.log(document.getElementById('div1').childNodes[0].nodeName);//#text
console.log(document.getElementById('div1').childNodes[0].nodeValue);//div3
console.log(document.getElementById('div1').childNodes[0].nodeType);//3

属性节点

<div id="div1" title="div2">div3</div>
console.log(document.getElementById('div1').getAttributeNode('title').nodeName);//title
console.log(document.getElementById('div1').getAttributeNode('title').nodeValue);//div2
console.log(document.getElementById('div1').getAttributeNode('title').nodeType);//2

只获取返回元素节点

返回元素节点 返回所有节点
children childNodes
childElementCount childNodes.length
firstElementChild firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling

文档碎片

var fragment = document.createDocumentFragement();
//添加节点到fragment
//...
document.body.appendChild(fragment);

querySelector与querySelectorAll

querySelector()用来获取第一个匹配的节点
querySelectorAll()用来获取全部匹配到的节点

classList

<div id="main" class="foo"></div>
var oDiv = document.getElementById('main');
console.log(oDiv.classList);//["foo"]

oDiv.classList.add('bar');
console.log(oDiv.classList);//["foo", "bar"]

console.log(oDiv.classList.item(0));//foo
console.log(oDiv.classList.item(1));//bar
console.log(oDiv.classList.item(2));//null


oDiv.classList.remove('foo');
console.log(oDiv.classList);//["bar"]

oDiv.classList.toggle('foo');
console.log(oDiv.classList);//["bar", "foo"]

oDiv.classList.toggle('foo');
console.log(oDiv.classList);//["bar"]

console.log(oDiv.classList.contains("bar"));//true
console.log(oDiv.classList.contains("foo"));//false

classList

<div id="main" class="foo"></div>
var oDiv = document.getElementById('main');
console.log(oDiv.classList);//["foo"]

oDiv.classList.add('bar');
console.log(oDiv.classList);//["foo", "bar"]

console.log(oDiv.classList.item(0));//foo
console.log(oDiv.classList.item(1));//bar
console.log(oDiv.classList.item(2));//null


oDiv.classList.remove('foo');
console.log(oDiv.classList);//["bar"]

oDiv.classList.toggle('foo');
console.log(oDiv.classList);//["bar", "foo"]

oDiv.classList.toggle('foo');
console.log(oDiv.classList);//["bar"]

console.log(oDiv.classList.contains("bar"));//true
console.log(oDiv.classList.contains("foo"));//false

跨域处理详解

跨域

环境搭建

配置域名

本文跨域处理后台以PHP为例,首先下载wamp,我们先在本地配置俩个域名:pcd.me(一级域名)与dev.pcd.me(二级域名)。在www目录下新建pcd和dev俩个文件,当访问pcd.me时则访问pcd文件夹下的文件,访问dev.pcd.me则访问dev文件夹下的文件。

更改host

更改C:\Windows\System32\drivers\etc\host文件:添加

127.0.0.1 pcd.me
127.0.0.1 dev.pcd.me

更改httpd-vhosts.conf

更改F:\wamp2\wamp\bin\apache\apache2.4.23\conf\extra\httpd-vhosts.conf(该目录为我的配置,具体以实际为准)

<VirtualHost *:80>
	ServerName pcd.me
	DocumentRoot F:/wamp2/wamp/www/pcd
	<Directory  "F:/wamp2/wamp/www/pcd/">
		Options +Indexes +Includes +FollowSymLinks +MultiViews
		AllowOverride All
		Require local
	</Directory>
</VirtualHost>
<VirtualHost *:80>
	ServerName dev.pcd.me
	DocumentRoot F:/wamp2/wamp/www/dev
	<Directory  "F:/wamp2/wamp/www/dev/">
		Options +Indexes +Includes +FollowSymLinks +MultiViews
		AllowOverride All
		Require local
	</Directory>
</VirtualHost>

访问

在pcd文件下新建index.html

<!DOCTYPE html>
 <html lang="en">
 <head>
 	<meta charset="UTF-8">
 	<title>Document</title>
 </head>
 <body>
 	main
 </body>
 </html> 

在dev文件下新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	dev
</body>
</html>

此时访问pcd.me页面显示main,访问dev.pcd.me页面显示dev。

跨域请求

在pcd文件下ajax.php

<?php
	echo 1;
?>

在pcd.me发起异步请求即
pcd文件下的index文件更改为

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
</head>
<body>
	dev
<script type="text/javascript">
$.ajax({
	type: "GET",
	url: "http://pcd.me/ajax.php"
	success: function(data) {
		console.log(data);//11
	}
})
</script>
</body>
</html>

在dev.pcd.me发起异步请求即
dev文件下的index文件更改为

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
</head>
<body>
	dev
<script type="text/javascript">
$.ajax({
	type: "GET",
	url: "http://pcd.me/ajax.php"
	success: function(data) {
		console.log(data);//报错
		//XMLHttpRequest cannot load http://pcd.me/ajax.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://dev.pcd.me' is therefore not allowed access.
	}
})
</script>
</body>
</html>

当dev.pcd.me页面向pcd.me发起异步请求时则为跨域处理。至此环境搭建成功。

跨域处理之基本知识

浏览器厂商针对Web客户端制定实现重要的安全概念:同源策略(SOP)。它的核心是确保不同源提供的文件之间是相互独立的,只有当不同的脚本文件是有相同的域、端口、HTTP协议提供时,才没有特殊限制访问对方的DOM方法和属性。当一个脚本访问不同源的文档中的方法和属性使,便会抛出异常错误。
当我们需要访问不同源的文件时,要么绕开同源策略(JSNP与子域代理),要么使用跨域资源共享(CORS)的“正式”技术。

跨域处理之JSONP

实现原理

同源策略有个例外: HTML脚本是可以规避SOP检查的。JSONP利用这个例外实现跨域数据加载。

simple example

<script type="text/javascript" src="http://pcd.me/ajax.php"></script>

dev.pcd.me页面通过以上方式发起请求时,避开SOP,实现跨域请求,但对于返回的数据无法解析。
于是需要我们改写返回的数据形式

<?php
	$number = 11;
	echo 'abc('.$number.')';
?>
 <!DOCTYPE html>
 <html lang="en">
 <head>
 	<meta charset="UTF-8">
 	<title>Document</title>
 	<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
 </head>
 <body>
 	dev
 	<script type="text/javascript">
 	Window.abc = function(number) {
 		console.log(number);//11
 	}
 	</script>
	<script type="text/javascript" src="http://pcd.me/ajax.php"></script>
	
 </body>
 </html>

客户端通过定义一个函数,异步回调执行该函数,则函数中的参数为数据。

动态回调函数

window.jsonpCallback = function(json) {
	console.log(json)
}
var script = document.createElement('script');
script.async = true;
script.src = "http://pcd.me/ajax.php?callback=jsonpCallback";
document.body.appendChild(script);
<?php
	header('Content-type: application/javascript');
	$callback = $_GET['callback'];
	$person = json_encode(array(
			'name' => 'pcd',
			'age' => '21',
			'sex' => 'man'

		), JSON_PRETTY_PRINT);

	echo "$callback($person)";
?>

jquery中ajax的调用

jquery中ajax实现JSONP跨域处理

局限性

1.JSONP仅适用于HTTP的GET请求,譬如图片、文字等无法实现上传
2.JSONP返回调用回调函数,如何没有调用成功,则无任何提示,只能是给定一个时间,没有收到响应则为请求失败

子域名代理

浏览器根据SOP认为dev.pcd.me与pcd.me是不同的源,但浏览器允许网站将主机部分更改为原始值的后缀,即寄放在dev.pcd.me的页面可以将他的源设置为pcd.me,但不能修改源的端口号与HTTP协议。

ajax.php

<?php
	echo 11;
?>

dev.pcd.me下的请求

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>子页面</title>
    <script type="text/javascript">document.domain = 'pcd.me'</script>
    <script type="text/javascript" src="jquery-1.11.2.min.js"></script>
</head>
<body>
	<div>dev</div>
	<script type="text/javascript">
    function getProducData() {
        var iframe = document.createElement('iframe');
        iframe.src = 'http://pcd.me/proxy.html';
        iframe.onload = function() {
            iframe.contentWindow.jQuery.ajax({//这里使用jquery的ajax方式调用则需要在proxy.html中引入jquery
                method: "GET",
                url: "http://pcd.me/ajax.php",
                success: function(data) {
                    console.log(data);
                }
            })
        }

        document.getElementsByTagName('head')[0].appendChild(iframe);
    }
    getProducData();
	</script>
</body>
</html>

pcd文件下添加一个代理文件proxy.html文件

<!DOCTYPE html>
<html>
	<script>
		document.domain = 'pcd.me';
	</script>
	<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
</html>

局限性

1.只适用于子域名下,运用范围窄
2.需要添加一个代理文件

使用子域名代理及JSOP

ajax.php

<?php
	echo '<!DOCTYPE>
			<html>
				<script>
					document.domain = "pcd.me";
					window.parent.jsonpCallback("{\"status\": \"success\"}");
				</script>
			</html>
	';
?>

dev.pcd.me下的请求

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>子页面</title>
    <script type="text/javascript">document.domain = 'pcd.me'</script>
    <script type="text/javascript" src="jquery-1.11.2.min.js"></script>
</head>
<body>
	<div>dev</div>
    <script type="text/javascript">
    function jsonpCallback(value) {
        console.log(value);
    }
    </script>
	<script type="text/javascript">

    var frame = document.createElement('iframe');
    frame.name = "post-review";
    frame.style.display = "none";

    var form = document.createElement("form");
    form.action = "http://pcd.me/ajax.php";
    form.method = "GET";
    form.target = "post-review";
    document.body.appendChild(form);
    document.body.appendChild(frame);

    form.submit();

    document.body.removeChild(form);
	</script>
</body>
</html>

局限性

1.JSONP仅适用于HTTP的GET请求,譬如图片、文字等无法实现上传
2.JSONP返回调用回调函数,如何没有调用成功,则无任何提示,只能是给定一个时间,没有收到响应则为请求失败
3.相较于JSONP,其能实现POST请求

跨资源共享(CORS)

当发起http请求时,支持CORS的浏览器会通过引入额外的Origin头信息来指定请求源。
Origin: http://de.pcd.me

服务端的工作是检查头信息是否接受该请求,如果一个请求被接受,他必须发挥一个包含Access-Control-Allow-Origin: http://dev.pcd.me

<?php
	header('Access-Control-Allow-Origin: http://dev.pcd.me');
	echo 11;
?>

dev.pcd.me下的请求

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>子页面</title>
    <script type="text/javascript" src="jquery-1.11.2.min.js"></script>
</head>
<body>
	<div>dev</div>
    <script type="text/javascript">
    $.ajax({
        type: "GET",
        url: "http://pcd.me/ajax.php",
        success: function(data) {
            console.log(data);
        }
    })    
	</script>
</body>
</html>

使用postMessage实现iframe之间的通信

dev目录下index.html

 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <title>Test Cross-domain communication using HTML5</title> 
 <script type="text/JavaScript"> 
     function sendIt(){ 
         // 通过 postMessage 向子窗口发送数据
         document.getElementById("otherPage").contentWindow 
             .postMessage( 
                 document.getElementById("message").value, 
                "http://pcd.me"
             ); 
     } 
 </script> 
 </head> 
 <body> 
     <!-- 通过 iframe 嵌入子页面 --> 
     <iframe src="//pcd.me/other-domain.html" 
                 id="otherPage"></iframe> 
     <br/><br/> 
     <input type="text" id="message"><input type="button" 
             value="Send to child.com" onclick="sendIt()" /> 
 </body> 
 </html>

pcd目录下other-domain.html

<html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <title>Web page from dev.pcd.me</title> 
 <script type="text/JavaScript"> 
	 //event 参数中有 data 属性,就是父窗口发送过来的数据
	 window.addEventListener("message", function( event ) { 
		 // 把父窗口发送过来的数据显示在子窗口中
	   document.getElementById("content").innerHTML+=event.data+"<br/>"; 
	 }, false ); 

 </script> 
 </head> 
 <body> 
	 Web page from http://dev.pcd.me
	 <div id="content"></div> 
 </body> 
 </html>

页面自适应布局

关于页面布局之前找了很多案例,当时是看懂了,但当让你写的时候发现自己视乎并不清楚他的原理,于是决定花些时间去整理一下关于页面布局的资料。主要分为俩列自适应布局,三列自适应布局,等高布局。

俩列布局: 左边固定,右边自适应

1.左边设置为浮动,右边不设浮动与宽度

* {
	padding: 0;
	margin: 0;
}
#main {
	height: 800px;
	background-color: yellow;
}
#left {
	float: left;
	width: 220px;
	height: 500px;
	background-color: red;
}
#right {
	padding-left: 220px;/* margin-left: 220px;border-left: 220px solid; */
	background-color: green;
	height: 500px;
}
<div id="main">
	<div id="left"></div>
	<div id="right"></div>
</div>

原理:
在元素不设宽度的情况下:
若元素为普通流中元素,元素宽度等于父元素宽度;
若元素不在文档流中,元素宽度等于内容宽度;

2.左边设置为浮动,右边不设浮动但设置宽度为100%

* {
	padding: 0;
	margin: 0;
}
#main {
	height: 800px;
	background-color: yellow;
}
#left {
	float: left;
	width: 220px;
	height: 500px;
	background-color: red;
}
#right {
	width: 100%;
	padding-left: 220px;
	box-sizing: border-box; 
	/* border-left: 220px solid;
	box-sizing: border-box; */
	background-color: green;
	height: 500px;
}
<div id="main">
	<div id="left"></div>
	<div id="right"></div>
</div>

原理:
在元素宽度为100%的情况下:
若元素为普通流元素或者浮动元素,元素宽度为父元素宽度的100%;若元素为绝对定位元素,元素宽度为元素offset-parent宽度的100%;若元素为固定定位元素,元素宽度始终为body的100%
设置box-sizing(兼容性IE8+)使#right的宽度为原来的100%。

3.俩边都设置浮动时

* {
	padding: 0;
	margin: 0;
}
#main {
	height: 800px;
	background-color: yellow;
	overflow: hidden;
	width: 100%;
}
#left {
	float: left;
	width: 200px;
	background-color: red;
	height: 300px;
	margin-right: -200px;
}
#right {
	float: left;
	width: 100%;
	height: 300px;
	border: 1px solid;
}
#rightContent {
	margin-left: 200px;
	height: 300px;
	background-color: white;
}

<div id="main">
	<div id="left"></div>
	<div id="right">
		<div id="rightContent">
		</div>
	</div>
</div>

原理:
1.首先将#left设置固定宽度并向左浮动,#right设置width: 100%; 并向左浮动。由于页面宽度限制#left与#right会处于俩行。
2.将#left设置css属性margin-right: -#left的宽度; 让其不占据原来的位置,此时的#left与#right处于同一行,但#right会覆盖掉#left。
3.设置#rightContent的margin-left: #left的宽度px。

三列布局: 左边右边固定,中间自适应

1.基本的布局方式

* {
	padding: 0;
	margin: 0;
}
#main {
	width: 100%;
	background-color: yellow;
	overflow: hidden;
	height: 500px;
}
#left {
	float: left;
	width: 150px;
	background-color: red;
	height: 300px;
	margin-right: -150px;
}
#right {
	float: left;
	width: 200px;
	background-color: green;
	height: 300px;
	margin-left: -200px;
}
#center {
	float: left;
	height: 300px;
	width: 100%;
}
#centerContent {
	height: 300px;
	margin: 0 200px 0 150px;
	background-color: white;
}

<div id="main">
	<div id="left"></div>
	<div id="center">
		<div id="centerContent">
		</div>
	</div>
	<div id="right"></div>
</div>

原理:
原理跟俩列布局的俩边都浮动的自适应相同
右边的定位使用了marin-left: -#left的宽度值; 原理与#right相同。

2.定位的方式布局

该方式简单,不做叙述。该方法不能实现等高布局,在设置定位的时候父元素要有高度,不能实现高度自适应。

三列布局: 左边中间固定,右边自适应

1.基本的布局方式

* {
	padding: 0;
	margin: 0;
}
#main {
	width: 100%;
	background-color: yellow;
	overflow: hidden;
	position: relative;
	height: 500px;
}
#left {
	width: 150px;
	background-color: red;
	height: 300px;
	float: left;
	margin-right: -150px;
}
#center {
	width: 200px;
	background-color: green;
	height: 300px;
	float: left;
	position: relative;
	left: 150px; 
}
#right {
	width: 100%;
	float: left;
	height: 300px;
}
#rightContent {
	background-color: white;
	margin-left: 350px;
	height: 300px;
}
<div id="main">
	<div id="left"></div>
	<div id="center">
	</div>
	<div id="right">
		<div id="rightContent">
		</div>
	</div>
</div>

原理:
原理跟俩列布局的俩边都浮动的自适应相同
但中间块#center的布局使用了绝对定位的方式

等高布局

1.基本的布局方式

* {
	padding: 0;
	margin: 0;
}
#main {
	width: 100%;
	background-color: yellow;
	overflow: hidden;
}
#left {
	float: left;
	width: 150px;
	background-color: red;
	padding-bottom: 9999px;
	margin-bottom: -9999px;
	margin-right: -150px;
}
#right {
	float: left;
	width: 100%;
	padding-bottom: 9999px;
	margin-bottom: -9999px;
}
#rightContent {
	margin-left: 150px;
	background-color: green;
}

<div id="main">
	<div id="left">aaaa</div>
	<div id="right">
		<div id="rightContent">
			aaaa<br><br><br>aaaa
		</div>
	</div>
</div>

原理:
父元素#main不设高度设置overflow: hidden;,#left与#right设置css属性padding-bottom: 9999px; margin-bottom: -9999px;以上示例以俩列布局俩边均浮动为例,其余俩种俩列自适应方式及三列自适应均可实现。
使用定位的布局方式不可实现等高布局,使用定位布局的时候需要先给父级添加一个高度值

Object.is与==与===与if(){}的判断

Object.is与==与===与if(){}的判断

==

使用 == 会做类型的转化

conosle.log('1' == 1);//true

===

前后的类型也要相同

console.log('1' === 1);//false

Object.is

与===相近,有俩个特殊情况

console.log(-0 === +0);//true
console.log(Object.is(-0, +0));//false
console.log(NaN === NaN);//false
console.log(Object.is(NaN, NaN));//true

JStype-compare

if(){}

如需执行if中的语句,则if中的条件需要为真值,
js里的“真值”很好判断,因为“假值”总共只有6个:
false,undefined,null,0,""(空字符串),NaN
除此之外的所有值,都是“真值”,即在逻辑判断中可以当true来使用

参考资料

http://dorey.github.io/JavaScript-Equality-Table/
https://www.zhihu.com/question/47555543
http://stackoverflow.com/questions/30543190/object-is-vs

原生JS中的this详解

this的绑定方式

默认绑定

没调用对象指向全局

function foo() {
    console.log(this.a);
}
var a = 1;
foo();//1

隐式绑定

var a = 1;
function foo() {
    console.log(this.a);        
}
var obj = {
    a: 2,
    foo: foo
}
obj.foo();//2
var bar = obj.foo;
bar();//1

显式绑定

function foo() {
    console.log(this.a);        
}
var a = 1;
var obj = {
    a: 2
}

foo.call(obj);//2
foo.apply(obj);//2
var bar = foo.bind(obj);
bar();//2

new绑定

function foo(a) {
    this.a = a;
}

var a = 1;

var bar = new foo(3);
console.log(bar.a);//3

各绑定方式的优先级

显示与隐式的比较

function foo() {
    console.log(this.a);
}

var obj1 = {
    a: 1,
    foo: foo
}
var obj2 = {
    a: 2
}

obj1.foo();//1
obj1.foo.call(obj2);//2

所以显式优先级高于隐式

new与隐式的比较

function foo(a) {
    this.a = a;
}
var obj = {
    a: 1,
    foo: foo
}

obj.foo(2);
console.log(obj.a);//2

var bar = new obj.foo(3);
console.log(obj.a);//2此时obj.foo(3)未起作用,new优先级大于隐式
console.log(bar.a);//3

显式与new的比较

function foo(a) {
    this.a = a;
}

var obj = {};

var bar = foo.bind(obj);
bar(2);
console.log(obj.a);//2

var baz = new bar(3);
console.log(obj.a);//2使用new未将obj.a改变为3,显式优先级高于new
console.log(baz.a);//3this绑定新创建的对象baz

bind的用法

#20

null、undefined作为this绑定对象传入call、apply和bind时,实际应用的是默认绑定规则

function foo() {
    console.log(this.a);
}
var a = 1;
foo.call(null);//1

绑定到全局的this改为局部,使用空集合符号

var Ø = Object.create(null);
function foo() {
    console.log(this.a);
}
foo.call(Ø);//undefined

ES6箭头函数中的this(与self=this的机制一样)

以下例子需要ES6环境下使用,可参考https://github.com/PLDaily/ES6

var Person = function() {
    this.name = 'pcd';
    this.hello = () => {
        console.log(this.name);//this在定义是已经被锁死
    }
}

var bb = new Person();
bb.hello.bind({"name": "dcp"});
bb.hello();//pcd

注意事项

var a;
a++;
console.log(a);//NaN

XMLHttpRequest详解

封装xhr

xhr = new XMLHttpRequest();

Level1到Level2的区别

  • 可以设置HTTP请求的时限。
  • 可以使用FormData对象管理表单数据。
  • 可以上传文件。
  • 可以请求不同域名下的数据(跨域请求)。
  • 可以获取服务器端的二进制数据。
  • 可以获得数据传输的进度信息。

xhr发送的数据类型

  • ArrayBuffer
  • Blob
  • Document
  • DOMString
  • FormData
  • null

readyState

  • 0 UNSENT (初始状态,未打开)
  • 1 OPENED (已打开,未发送)
  • 2 HEADERS_RECEIVED (已获取响应头)
  • 3 LOADING (正在下载响应体)
  • 4 DONE (整个数据传输过程结束)

response

  • xhr.response
  • xhr.responseText
  • xhr.responseXML

responseType

  • "" String字符串
  • "text" String字符串
  • "document" Document对象
  • "json" javascript 对象 存在兼容性问题,IE10/IE11不支持
  • "blob" Blob对象
  • "arrayBuffer"

事件

  • onloadstart
  • onprogress
  • onabort
  • ontimeout
  • onerror
  • onload
  • onloadend

status

http返回的状态码

withCredentials与CORS

xhr.withCredentials代表是否携带cookie。
cookies也是一种认证信息,在跨域请求中,client端必须手动设置xhr.withCredentials=true,且server端也必须允许request能携带认证信息(即response header中包含Access-Control-Allow-Credentials:true),这样浏览器才会自动将cookie加在request header中。

实现一个原生ajax

var form_data = new FormData();
form_data.append('longitude', _this.nowloc.lng);
form_data.append('latitude', _this.nowloc.lat);
form_data.append('city', _this.nowloc.city);
var xhr = new XMLHttpRequest();

xhr.open('POST', '/point/near', true);

xhr.send(form_data);

xhr.onreadystatechange = function(){
    if(xhr.readyState == 4 && xhr.status == 200) {
        console.log(typeof xhr.responseText);//string
    }
};

参考资料

https://segmentfault.com/a/1190000004322487
http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html

网络编程

网络由下往上分为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。其中IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。

TCP/IP

PC/IP协议是传输层协议,主要解决数据如何在网络中传输

什么是TCP连接的三次握手

  • 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
  • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

socket

TCP/IP协议的封装和应用

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

  • 服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
  • 客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
  • 连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。
    而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

http

HTTP是应用层协议,主要解决如何包装数据。我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”

长连接与短连接

  • 在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包含有其他的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。

  • 但从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:

UDP

UDP不是面向连接的,UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,所以说UDP是无连接的、不可靠的一种数据传输协议

UDP与TCP的区别

  • TCP通过三次握手在最低限度上(实际上也很大程度上保证了)保证了连接的可靠性
  • UDP不对数据进行验证确认,实现了TCP所无法达到的传输效率

参考资料

http://www.cnblogs.com/0201zcr/p/4694945.html
https://www.zhihu.com/question/39541968/answer/81841947

javascript原生bind使用实例

一般情况下在使用对象调用函数是会使用以下方式书写,将对象赋值给函数内部的局部变量,调用函数时能从局部变量中获取

var x = 10;
var myObj = {
    x: 0,
    deal: function() {
        var that = this;
        return function() {
            return ++that.x;
        }
    }
};

var aaa = myObj.deal();
console.log(aaa());//1

不将对象赋值到函数内部的局部变量,函数调用时从全局中去获取

var aaa = myObj.deal();
console.log(aaa());//1

var x = 10;
var myObj = {
    x: 0,
    deal: function() {
        return function() {
            return ++this.x;
        }
    }
};

var aaa = myObj.deal();
console.log(aaa());//11

bind相当于将myObj(上下文)传入到函数中

var x = 10;
var myObj = {
    x: 0,
    deal: function() {
        return (function() {
            return ++this.x;
        }).bind(this);
    }
};

var aaa = myObj.deal();
console.log(aaa());//1

bind使用参数的形式传入时

function foo(p1) {
    this.val = p1;
}

var baz = foo.bind(null, 'p1');
var bar = new baz('p2');
console.log(bar.val);//p1
function foo(p1, p2) {
    this.val = p1 + p2;
}

var baz = foo.bind(null, 'p1');
var bar = new baz('p2');
console.log(bar.val);//p1p2
function foo(p1) {
    this.val = p1;
}

var baz = foo.bind('p1');
var bar = new baz('p2');
console.log(bar.val);//p2

使用new新建对象时即被忽略

JS取随机数

从3到100中取一个随机的整数

Math.floor(Math.random() * 98 + 3 + 1) - 1;
  • Math.random() 获取到的数值为[0, 1)

  • Math.random() * 98 获取到的值为[0, 98)

  • Math.random() * 98 + 3 获取到的值为[3, 101)

  • Math.random() * 98 + 3 + 1 获取到的值为[4, 102)

  • Math.floor(Math.random() * 98 + 3 + 1) 获取到的值为[4, 101]中的整数

  • Math.floor(Math.random() * 98 + 3 + 1) - 1; 获取到值为为[3, 100]中的整数

mac下如何搭建php环境

加班近一个星期,回学校写一个星期的论文,又俩个星期没有更新博客了。最近新入手一台mac,记录一下搭建php环境的过程。mac自带apache与php,但由于mac系统更新时会更新apache与PHP,导致php环境需要重新配置,故选择使用xampp。

mysql无法开启

  • 首先使用改命令
sudo /Applications/XAMPP/xamppfiles/bin/mysql.server start
  • 不起作用则使用
  1. sudo killall mysqld

  2. manager-osx > start mysql

更改vhost

  • 启动httpd.conf中的
LoadModule vhost_alias_module modules/mod_vhost_alias.so
Include etc/extra/httpd-vhosts.conf
  • 更改httpd-vhost.conf
    示例:
<VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot "/Applications/XAMPP/xamppfiles/htdocs/laravel/public"
    ServerName www.shop123.com
    ErrorLog "logs/dummy-host2.example.com-error_log"
    CustomLog "logs/dummy-host2.example.com-access_log" common
    <Directory "/Applications/XAMPP/xamppfiles/htdocs/laravel/public">
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
  • 更改httpd.conf中的user
<IfModule unixd_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.  
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User pengchangdong
Group daemon
</IfModule>

如何实现链式调用

以一个简单的例子为例,我们要实现一个链式的调用,例:

Man('pcd').eat('lunch').eat('dinner')

该方法简单,只需我们新建一个对象,将Man、eat作为对象的属性,每次调用时返回该对象变可以实现
例:

function _Man(name) {
	console.log('I am ' + name);
}

_Man.prototype.eat = function(something) {
	console.log('eat ' + something);
	return this;
}

function Man(name) {
	return new _Man(name);
}

Man('pcd').eat('lunch').eat('dinner');
//I am pcd
//eat lunch
//eat dinner

此时我们想改变一下,pcd不想吃完中饭后直接去吃晚饭,他想过5s之后再去吃晚饭

Man('pcd').eat('lunch').wait(5000).eat('dinner');

改变上面的例子

function _Man(name) {
	console.log('I am ' + name);
}

_Man.prototype.eat = function(something) {
	console.log('eat ' + something);
	return this;
}

_Man.prototype.wait = function(time) {
	setTimeout(function() {
		console.log('wait ' + time);
	}, time);
	return this;
}

function Man(name) {
	return new _Man(name);
}

Man('pcd').eat('lunch').wait(5000).eat('dinner');
//I am pcd
//eat lunch
//eat dinner
//wait 5000

输出的结果跟我们的预期不符,我们需要先"wait 5000"后再"ear dinner";当然你会想我们可以再执行完定时器之后再回到this对象就好了。如下

function _Man(name) {
	console.log('I am ' + name);
}

_Man.prototype.eat = function(something) {
	console.log('eat ' + something);
	return this;
}

_Man.prototype.wait = function(time) {
	var _this = this;
	setTimeout(function() {
		console.log('wait ' + time);
		return _this;
	}, time);
}

function Man(name) {
	return new _Man(name);
}

Man('pcd').eat('lunch').wait(5000).eat('dinner');
//I am pcd
//eat lunch
//Cannot read property 'eat' of undefined

但是却报了eat为undefined,因为我们在执行定时器操作相当于异步操作,而Man('pcd').eat('lunch').wait(5000).eat('dinner');中wait函数后的eat还需要继续执行,但此时的this还没有返回,故找不到eat的方法。所以我们需要换一种方式解决。
首先,我们先执行链式调用的Man('pcd').eat('lunch').wait(5000).eat('dinner');但不实际操作该函数,只是将其存放在数组中,通过setTimeout执行异步操作,从数组中一个个去除执行变可以了,如下:

function _Man(name) {
	console.log('I am ' + name);
	var _this = this;
	this.funcArr = [];
	setTimeout(function() {
		_this.next();
	})
}

_Man.prototype.next = function() {
	var fn = this.funcArr.shift();
	fn&&fn();
}

_Man.prototype.eat = function(something) {
	var _this = this;
	var func = function(something) {
		console.log('eat ' + something);
		_this.next()
	}
	this.funcArr.push(func);
	return this;
}

_Man.prototype.wait = function(time) {
	var _this = this;
	var func = function(time) {
		setTimeout(function() {
			console.log('wait ' + time);
			_this.next();
		}, time);
	}
	this.funcArr.push(func);
	return this;
}

function Man(name) {
	return new _Man(name);
}

Man('pcd').eat('lunch').wait(5000).eat('dinner');
//I am pcd
//eat undefined
//wait undefined
//eat undefined

此时你会发现函数虽然放入数组并一个个执行了,但函数的参数并没有传入进去。此时我们便需要使用高大上的闭包存储变量了。

function _Man(name) {
	console.log('I am ' + name);
	var _this = this;
	this.funcArr = [];
	setTimeout(function() {
		_this.next();
	})
}

_Man.prototype.next = function() {
	var fn = this.funcArr.shift();
	fn&&fn();
}

_Man.prototype.eat = function(something) {
	var _this = this;
	var func = (function(something) {
		return function() {
			console.log('eat ' + something);
			_this.next();
		}
	})(something);
	this.funcArr.push(func);
	return this;
}

_Man.prototype.wait = function(time) {
	var _this = this;
	var func = (function(time) {
		return function() {
			setTimeout(function() {
				console.log('wait ' + time);
				_this.next();
			}, time);
		}
	})(time);
	this.funcArr.push(func);
	return this;
}

function Man(name) {
	return new _Man(name);
}

Man('pcd').eat('lunch').wait(5000).eat('dinner');
//I am pcd
//eat lunch
//wait 5000
//eat dinner

大功告成。。。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.