开发技巧
需求分析
输入查询条件得出结果的需求:留意是数据筛选仍是查询
JavaScript
数组操做
segmentfault.com/a/119000001…
this指向
首先this指向的对象只有三个:windows、函数实例、所处对象
若是函数有this,可是没有所处对象或函数实例调用,那么this指向windows
function a (){
var user = "追梦子" ;
console.log(this.user); //undefined
console.log(this); //Window
}
a();
复制代码
var o = {
a:10,
b:{
a:12,
fn:function (){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
复制代码
若是this被所处对象或函数实例调用,this指向该对象
var o = {
a:10,
b:{
//a:12,
fn:function (){
console.log(this.a); //undefind 有两个对象b和o,因此此this.a指向它的上一级
}
},
fn1:function (){
console.log(this.a); //10
}
}
o.fn1();
o.b.fn();
复制代码
当函数中有this,且最后使用了return,若是return的是对象,那么this指向该对象,不然this指向函数实例
function fn ()
{
this.user = '追梦子' ;
return {}; // 或function (){}
}
var a = new fn;
console.log(a.user); //undefined
复制代码
function fn ()
{
this.user = '追梦子' ;
return 1;
}
var a = new fn;
console.log(a.user); //追梦子
复制代码
虽然null也是对象,可是null比较特殊,return null,this依然指向函数实例,不指向null
bind、apply、call的区别
apply和call不一样点在于,call须要一个个传入参数,apply能够数组形式传入参数;使用方法分别为funA.apply(funB, args)和funA.call(funB, arg1, arg2, arg3...)。
bind的传参方式与call相同,但永久改变this的指向,并返回新的函数;使用方法为var newFun = funA.bind(funB, arg1, arg2...);若是在使用bind时传入了参数,那么以后调用newFun时参数也是默认被传入,如var newFun = funA.bind(null, a, b); newFun(c, d); 这里至关于传入了a、b、c、d四个参数。
柯里化
柯里化是建立一个已经设置好一个或多个参数的函数,是用于动态建立函数的强大功能
柯里化函数的通用方式:
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var in Args = Array.prototype.slice.call(null, arguments);
var finalArgs = args.concat(in Args);
return fn.apply(null, finalArgs);
};
}
复制代码
不可扩展对象
使用Object.preventExtensions()能够防止对象添加新成员
var ob = {name: 'tom' };
Object.preventExtensions(ob);
ob.age = 22;
alert(ob.age); // undefined;
复制代码
使用Object.preventExtensions()后的对象,非严格模式下,添加新成员静默失败;严格模式下,添加新成员抛错。
使用Object.preventExtensions()后的对象,依旧能够删除修改为员
可使用Object.isExtensible()可判断是否能够扩展;
密封对象
使用Object.seal(ob)密封对象ob,对象不可添加和删除成员。非严格模式默认失败,严格模式下抛错。可是能够修改为员。
冻结对象
使用Object.freeze(ob)冻结对象ob,对象不可修改、添加、删除成员。非严格模式默认失败,严格模式下抛错。
定时器原理
指定的时间间隔表示什么时候将定时器的代码放入队列,而不是什么时候当即执行代码,若是队列里面还有其它任务,那么这些排在前面的任务将会被优先执行,定时器代码会被滞后执行。
异步队列中的任务会在进程空闲期执行
重复的定时器原理
重复定时器主要是setInterval,但使用setInterval有不少问题
若是再添加定时器代码到队列时,上一次添加到队列的代码尚未执行完,可能会致使定时器代码连续执行屡次,没有停顿。为了解决这个问题,浏览器会判断队列里有无定时器代码实例,若是有则不添加代码到队列。
上述浏览器的解决方案也存在另外一问题:某些定时任务被跳过
数据属性
访问器属性
获取通用惟一识别码
https://blog.csdn.net/mr_raptor/article/details/52280753
复制代码
纯函数
函数返回结果只依赖于参数
函数没有反作用,不会更改参数
运算符
NaN === NaN // false
+0 === -0 // true (但这里本应该是不等的,是JS的缺陷)
字符串
replace(arg1, arg2):用于字符串内容替换,第一参数为正则表达式或字符串,第二参数为需替换的字符串或函数,函数为function(matchStr, group1[...groupN], index, sourceStr),matchStr为正则匹配到的字符串,group为捕获组,index为匹配项在字符串中的开始下标,sourceStr为原字符串
CSS
布局
.clearfix {
display: block;
zoom: 1;
&:after {
content: " " ;
display: block;
font-size: 0;
height: 0;
clear: both;
visibility: hidden;
}
}
复制代码
ES6
对象的新增方法
Object.is()方法能够准确比较NaN与其自身,+0和-0的比较,如:
Object.assign()参数为非对象则转换为对象并返回
Object.assign()参数为null或undefined则报错
利用Object.assign()克隆对象并拷贝对象的原型链
function clone (origin) {
let originProto = Object.getPrototypeOf(origin);
return Object.assign(Object.create(originProto), origin);
}
复制代码
function shallowMerge(target, source ) {
return Object.defineProperties(target, Object.getOwnPropertyDescriptors(source ))
}
复制代码
利用Object.create()克隆对象,对象属性能够为访问器属性,并拷贝对象的原型链
function merge(ob) {
return Object.create(Object.getPrototypeOf(ob), Object.getOwnPropertyDescriptors(ob));
};
复制代码
Obeject.entries()不能输出Symbol键值对
Symbol类型的实例具备惟一性,通常用做属性,可用于替换魔术字符串
使用Symbol.for()可建立全局的惟一Symbol类型实例
数据结构Set的成员惟一,可用于数组去重,如:
Array.from(new Set(arr))
[...new Set(arr)]
复制代码
也能够用于字符串去重,如:javascript
[...new Set(str)].join('' )
复制代码
Map和Set
Set的原型链属性有contructor、size,操做属性有add、delete、has、clear,遍历方法有keys、values、entries、forEach
WeakSet的成员只是对象,且对象都为弱引用,若对象引用次数为0,对象则被gc
WeakSet的实例操做方法有add、has、delete
Map数据结构的键能够为任意类型
Map数据类型+0和-0等同于一个键、NaN虽然等同于自身可是会把NaN视为一个键
Map数据类型具备继承属性size,操做方法set、get、delete、clear、has,遍历方法有keys、values、entries、forEach
WeakMap数据结构只接受对象做为键名
Proxy
元编程即为对编程语言进行编程
proxy是一种元编程方法,机制是在操做对象前会做一层拦截
proxy中的get、set能够被继承
proxy可用于属性的链式操做
var ob = {
n: 0,
a: function (n) {
return n + 1;
},
b: function (n) {
return n + 1;
}
}
var pipe = function (ob, value) {
var funcStack = [];
var oproxy = new Proxy(ob , {
get : function (pipeObject, fnName) {
if (fnName === 'get' ) {
return funcStack.reduce(function (val, fn) {
return fn(val);
}, value);
}
console.log('fnName' , fnName)
funcStack.push(ob[fnName]);
console.log('funcStack' , funcStack)
return oproxy;
}
});
return oproxy;
};
var proxy = pipe(ob, 1);
console.log(proxy.a.b.get) // 3
复制代码
Promise
概念:Promise是一个容器,保存着将来发生的事情。Promise是一个对象,能够获取异步操做的消息。
优势: (1)状态不受外界影响:三种状态pending、fullfilled、reject (2)状态改变后不会再改变,何时均可以获得这个结果:从pending到fullfilled或从pending到reject后,状态resolved(定型),任意时刻能够得 到结果
缺点: (1)Promise一旦建立,不能够取消 (2)若是在pending状态,不能够知道是处于刚开始仍是即将完成状态
Interator
Interator是一种接口,为不一样的数据结构提供统一的访问机制,具有Interator的数据结构完成遍历操做
具有Interator的数据结构:Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList对象
对象不具有Iterator接口,是由于属性的顺序是不肯定的
只有类数组对象可使用[Symbol.iterator]方法,如:
let iterable = {
0: 'a' ,
1: 'b' ,
2: 'c' ,
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a' , 'b' , 'c'
}
复制代码
普通对象使用[Symbol.iterator]方法无效
let iterable = {
a: 'a' ,
b: 'b' ,
c: 'c' ,
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // undefined, undefined, undefined
}
复制代码
[Symbol.iterator]方法的使用场合,在解构赋值、扩展运算符、yield*(yield*后面为可遍历结构时,会调用后面的可遍历结构的[Symbol.iterator]方法),其它场合:(for...of Array.from() Map(), Set(), WeakMap(), WeakSet()(好比new Map([['a',1],['b',2]])) Promise.all() Promise.race())
除了next方法外,还有return(),throw()方法
for...of循环可使用的范围包括数组、Set 和 Map 结构、某些相似数组的对象(好比arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串
Generator
Generator是一个状态机,同时也是遍历器对象生成器
特征:function和函数名之间有一个*号,函数内部使用field表达式,表示不一样的状态
返回:指向内部状态的指针对象
能够把Generator赋值给对象的[Symbol.iterator]实现对象的iterator操做
Generator执行后返回的遍历器也具备[Symbol.iterator],而且Symbol.iterator 指向其自身
若是next带有参数,那么这个参数就是上一个field的返回值
function * generator () {
yield 1;
yield 2;
return 3;
}
var g = generator();
console.log(g.next())
console.log(g.next())
console.log(g.next())
/**
* Object {value: 1, done : false }
Object {value: 2, done : false }
Object {value: 3, done : true }
Object {value: undefined, done : true }
*/
复制代码
若是调用next时遇到return,返回值中的done就会提早为true;若是没有return,当把generator的值遍历完后调用next,返回的done才为truejava
function * fibonacci () {
let [prev, cur] = [0, 1];
for (;;) {
yield cur;
[prev, cur] = [cur, prev + cur]
}
}
for (let i of fibonacci()) {
if (i > 10000) break ;
console.log(i)
}
复制代码
const ob = {
* generator () { //... }
}
等同于
const ob = {
generator: function * () { //... }
}
复制代码
协程是指并行执行,能够交互执行权的线程
求值策略,便是函数的参数什么时候取值的问题,有两种类型:传值调用和传名调用
编译器的“传名调用”实现,每每是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫作 Thunk 函数。
基于Thunk的流程自动化管理
var fs = require('fs' );
var thunkify = require('thunkify' );
var read FileThunk = thunkify(fs.readFile);
function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen.next(data);
console.log(result)
if (result.done) return ;
result.value(next);
}
next();
}
var g = function * (){
var f1 = yield read FileThunk('fileA' );
var f2 = yield read FileThunk('fileB' );
var fn = yield read FileThunk('fileN' );
};
run(g);
复制代码
async
async是generator的语法糖,优势:(1)内置执行器 (2)更好的语义 (3)更广的适用性,await后面能够是Promise或原始类型值(若原始类型值为n,至关于Promise.resolve(n)) (4)返回的值是Promise
class
子类没有定义constructor方法,这个方法会被默认添加
在子类的constructor方法中,须要调用super才可使用this
super在静态方法中指向父类,在普通方法中指向父类的原型
在子类普通方法中使用super调用父类的方法,方法中的this指向子类实例
在子类的静态方法中使用super调用父类方法,方法中的this指向子类而不是子类的实例
经过super为属性赋值,此时super就是this,致使赋值的是子类的属性
class A {
constructor () {
this.x = 1;
}
}
class B extends A {
constructor () {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
复制代码
Object.getPrototypeOf方法能够用来从子类上获取父类
子类的构造函数必须执行一次super()
若A为B的父类,super()至关于A.prototype.constructor.call(this)
new.target只能在constructor中使用,而且指向当前执行的函数
子类的__proto__指向父类,子类的prototype的__proto__指向父类的prototype
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
复制代码
若是只是单纯的类,没有继承,类的__proto__指向Function.prototype,类的prototype.__proto__指向Object.prototype
class A {
}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true
复制代码
子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性
class Point {}
class ColorPoint extends Point {}
var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red' );
console.log(p2.__proto__.__proto__ === p1.__proto__ ) // true
console.log(p2.__proto__ === p1 ) // false
复制代码
ES的原生构造函数有以下: Boolean() Number() String() Array() Date() Function() RegExp() Error() Object() 在ES5中不能经过继承内部属性和内部方法,在ES6中能够实现。注意在继承Object时会有异常:
class NewObj extends Object{
constructor (){
super(...arguments);
}
}
var o = new NewObj({attr: true });
o.attr === true // false
复制代码
由于若是不是使用new Object()新建对象,ES6会忽略传入的参数jquery
Mixin混入模式是指多个对象合成新对象,新对象具备这些原对象成员的接口
Module
export用于定义模块对外的接口
export能够输出变量、函数、类,不能输出常量
须要注意使用export输出函数的写法
function fn () { ... }
export fn; // 错
export { fn }; // 对
复制代码
export 只能放在模块的顶层,能够放在顶层的任意位置,但不能够放在函数内部
export default A等同于将A赋值给default而后输出,因此能够
import { default as B } from 'xx.js' ;
复制代码
也能够正则表达式
export { A as default };
复制代码
export { A } from 'xxx.js'
复制代码
import用于引入其余模块的功能
import引入的变量是动态绑定,如import a,若是输出的a是异步变化的,那么引入a的也是变化的
import命令具备提高效果,由于import再编译期间(代码运行以前)就会执行
ArrayBuffer
ArrayBuffer是内存中一段二进制数据
TypeArray包括9类视图: (1)Init8Array: 8位整型 (2)Uinit8Array: 8位无符号整型 (3)Uinit8CArray: 8位无符号整型(自动过滤溢出) (4)Init16Array: 16位整型 (5)Uinit16Array: 无符号16位整型 (6)Init32Array: 32位整型 (7)Uinit32Array: 无符号32位整型 (8)Float64Array: 64位浮点型 (9)Ufloat64Array: 无符号64位浮点型
DataArray: 自定义的复合格式的视图
ArrayBuffer.prototype.byteLength:用户获取ArrayBuffer的实例
ArrayBuffer.prototype.slice():用于将ArrayBuffer内存的一部分,拷贝为一段新的内存;过程为建立一段新的内存,而后拷贝
ArrayBuffer.prototype.slice():判断是否为ArrayBuffer的视图实例(如 DataArray、TypeArray的实例)
TypeArray与Array的区别: (1)成员连续 (2)成员默认为0 (3)成员类型一致 (4)自己只是视图,实际数据存储在底层的ArrayBuffer
计算机基础
编码
编码是信息从一种形式转换到另外一种信息的过程,解码便是逆编码。
JavaScript有全局方法encodeURI()和encodeURIComponent();
encodeURI不对如下字符编码
非转义字符:字母 数字 - _ . ! ~ * ' ( )
encodeURIComponent不会对如下字符编码
非转义字符:字母 数字 - _ . ! ~ * ' ( )
二者的区别是encodeURI不能对保留字符和#转义
解码
encodeURI()和encodeURIComponent()对应的解码方法有decodeURI()和decodeURIComponent()
进程和线程
进程是资源分配的最小单位,线程是程序执行的最小单位
进程有独立的内存地址空间
一个进程下能够有多个线程
同一进程下的多个线程可共享进程的资源
多进程程序更加健壮,由于一个线程死掉,进程即死掉,该进程下的全部全部线程都会死掉;而一个进程死掉不会影响另外一进程,由于进程占用独立的内存地址空间
计算机网络
链路层
每个网卡都用一个mac地址
广播:假设子网略内有若干台主机,一台主机发出一个数据包,接收到包的主机会读取数据包头部的MAC地址,并与自身的MAC地址比较,若是相同则作进一步处理,不然丢弃这个包
网络层
网络层的存在是由于:链路层广播的效率低,通讯双方须要在一个子网略,局限性太大
网络层引进了“网络地址”,引入网络地址能够区分哪一些MAC地址处于同一个子网络
规定网络地址的协议成为IP地址,由32位二进制数组成 11111111.11111111.11111111.00000000,可分为网络部分和主机部分,若是网络部分为前24位,主机部分位后8位,那么前24位相同的两个ip地址确定在同一个子网络。
子网掩码用于肯定IP地址的网络部分,好比子网掩码位255.255.255.0,那么确定是前24位为网络部分
ARP协议:用于同一子网络的通讯,协议内容是:发出的包含有所要查询主机的ip地址和mac地址,mac地址为FF:FF:FF:FF:FF:FF(12位16进制),表示广播到全部主机。所在子网络的每一台主机会收到这个包,取出其中的IP地址并与自身比对,若是相同双方都作出回复,告诉对方本身的mac地址,不然丢弃这个包
实现数据包的成功发送,须要获取接收方的MAC地址、IP地址、网关地址、DNP服务器IP地址
动态IP地址
静态IP地址:每次开机都用一个IP地址上网,便是“静态IP地址上网”。为了更方便地为新接入的主机添加IP地址,通常会使用动态IP地址。DHCP协议(动态主机设置协议)可实现动态IP地址
获取动态IP地址过程
(1)新加入的主机会向DHCP服务器发送一个DHCP请求数据包,数据包的结构为 |以太网标头|IP标头|UDP标头|data(数据内容)|编程
以太网标头含有发送方和接收方的MAC地址,前者为新加入主机自身的MAC地址,后者为FF.FF.FF.FF.FF.FF,便是广播地址
IP标头含有发送方和接收方的IP地址,由于二者都不知道,因而发送发IP地址设为0.0.0.0,接收方IP地址设为255.255.255.255
UDP标头含有发送方和接收方的端口,前者设为68,后者设为67
请求数据包在子网络内广播,接收到数据包的主机首先解析数据包MAC地址,由于接收方MAC地址为FF.FF.FF.FF.FF.FF广播地址,因此会解析数据包的IP地址,接收方IP为255.255.255.255,将之与自身IP地址比对,发现不一样则丢包 (2)当DHCP服务器接收到请求数据包并解析接收方IP地址,发现为255.255.255.255,服务器知道须要去分配IP,发出响应数据包 (3)响应数据包的数据内容中含有为新主机分配的IP地址、网关IP地址、DNS的IP地址
响应数据包的以太网标头含有发送方和接收方的MAC地址,前者为DHCP服务器看的MAC地址,后者为新主机的MAC地址
IP标头含有发送方IP地址为DHCP服务器自身的IP地址,接收方IP地址依然为255.255.255.255
UDP标头的发送方端口为67,接收方端口为68 (4)新加入的数据包接收到响应数据包,拿到IP地址、子网掩码、网关IP、DNS服务器的IP。
传输层
端口:表示数据包供哪一个程序使用,端口号能够为0到65535,0到1023供系统使用
传输层是"端口到端口"的通讯
UDP的优势是简单,可是可靠性差,没法知道对方是否接收到。
TCP可靠性比UDP强,缺点是过程复杂、消耗较多资源
应用层
一次网络通讯的过程
好比说用咱们的计算机访问一次百度(baidu.com):segmentfault
设置好本机IP、子网掩码、DNS_IP、网关IP
本机向DNS_IP发送一个DNS数据包,经过DNS协议得到将baidu.com解析为IP地址
接下来建立咱们的数据包,首先将HTTP数据包嵌入TCP数据包,新数据包再嵌入IP数据包,这个由HTTP数据包、TCP数据包、IP数据包组成的新数据包最后会嵌入以太网数据包(它的结构以下图)
经过子网掩码判断本机与百度服务器是否在同一网关(咱们和百度服务器的必然是不一样的)
以太网标头的目标MAC地址改成本机网关的MAC地址,咱们的数据包转由网关发送
经过ARP协议将百度服务器的IP地址解析为MAC地址,获得了最终的数据包。假设这一个数据包大小为4600字节,由于以太网数据包的大小限制为1500字节,因此须要切割为大小分别为1500、1500、1500、100字节的四个以太网数据包,最后再发送到百度的服务。
JS替换语言
TypeScript
基础类型
基础类型有:number、boolean、string、null、undefined、symbol、object
null和undefined是全部类型的子类型,所以能够把它们赋给任何已经定义类型的变量
never是那些不存在值的类型,如只会抛错和没有return值的函数,是全部类型的子类型,但它没有子类型(包括any)
类型断言:相似于类型转换、不进行类型检查,可有两个方式:和as string
若是想变量只读使用const,想对象的属性只读,可定义类型为只读类型,好比
interface Point {
readonly x: number;
readonly y: number;
}
复制代码
处理额外属性可使用索引签名,索引签名能够为数字也能够是字符串,可是前者值的类型必须是后者的子类型,如(propName为其他属性):
interface Ob {
name: string;
[rest: string]: any; // string定义属性名类型、any定义值的类型
}
复制代码
模块化
AMD(异步模块规范)
define(['jquery' , 'underscore' ], function ($, _) {
// 方法
function a (){}; // 私有方法,由于没有被返回(见下面)
function b (){}; // 公共方法,由于被返回了
function c (){}; // 公共方法,由于被返回了
// 暴露公共方法
return {
b: b,
c: c
}
});
复制代码
CommonJS
var $ = require('jquery' );
复制代码
UMD
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery' ], factory);
} else if (typeof exports === 'object' ) {
// Node, CommonJS之类的
module.exports = factory(require('jquery' ));
} else {
// 浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// 方法
function myFunc (){};
// 暴露公共方法
return myFunc;
}));
复制代码
设计模式
单一指责原则
里氏替换
接口拆分
依赖倒置
开放封闭原则