- browser checks cache; if requested object is in cache and is fresh, skip to #9(浏览器检查缓存,若缓存中存储着要请求的内容,并且内容是最新的,直接跳转到第9步)
- browser asks OS for server's IP address(浏览器请求操作系统(OS)解析服务器的IP地址)
- OS makes a DNS lookup and replies the IP address to the browser(操作系统做DNS解析,查找并返回IP地址给浏览器)
- browser opens a TCP connection to server (this step is much more complex with HTTPS)(浏览器与服务器建立TCP连接(若使用的是https协议,连接过程会更加的复杂)
- browser sends the HTTP request through TCP connection(浏览器通过TCP连接发送http请求)
- browser receives HTTP response and may close the TCP connection, or reuse it for another request(浏览器接收到http相应后,关闭(断开)TCP连接或者利用TCP发送其他http请求)
- browser checks if the response is a redirect (3xx result status codes), authorization request (401), error (4xx and 5xx), etc.; these are handled differently from normal responses (2xx)(浏览器检测http相应是否是重定向(http状态码为3xx),授权相应(401),错误相应(4xx和5xx)等;浏览器对这些状态码的处理与正常的相应(2xx)是不同的)
- if cacheable, response is stored in cache(若可以被缓存,则将相应存储到缓存中)
- browser decodes response (e.g. if it's gzipped)(浏览器解压相应(比如页面被gzip压缩过))
- browser determines what to do with response (e.g. is it a HTML page, is it an image, is it a sound clip?)(浏览器决定以什么样式的方式解析http相应(比如他可能是个html网页,可能是一张图片,或者可能是个音频短片等))
- browser renders response, or offers a download dialog for unrecognized types(若相应的是浏览器不能解析的格式,则下载该文件;否则浏览器就解析相应)
闭包
闭包:当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用arguments和其他命名参数的值来初始化函数的活动对象。但是在作用域链中,外部函数的活动对象始终处于第二位,外部函数的活动对象处于第三位,...直至作为作用域链终点的全局执行环境。
闭包与变量
作用域链的这种配置机制引出了一个副作用,即闭包只能去地包含函数中任何变量的最后一个值。
demo
function createFunctions() {
var result = new Array();
for(var i=0; i<10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var re = createFunctions();
re[0](); // 10
demo2
function createFunctions() {
var result = new Array();
for(var i=0; i<10; i++) {
result[i] = function(num) {
return function() {
return num;
}
}(i)
}
}
在调用每个匿名函数时, 我们传入了变量i。由于函数参数是按值传递的,所以会将变量i的当前值复制给参数num.而在这个匿名函数内部,又创建返回了一个访问num的闭包.这样一来,result 数组中的每个函数都有自己num变量的一个副本,因此就可以返回各自不同的数值了.
冒泡排序和快速排序
依次比较相邻的两个元素,如果后一个小于前一个,则交换,这样从头到尾一次,就将最大的放到了末尾。
## 冒泡排序Demo
// 冒泡排序法
console.time('冒泡排序法共花费了')
function bubbleSort(oArr, endIndex) {
endIndex = typeof endIndex !== 'number' ? oArr.length - 1 : endIndex
for(let i = 0; i <= endIndex; i++) {
if(i + 1 <= endIndex) {
if(oArr[i] > oArr[i + 1]) {
swap(oArr, i, i+1);
}
}
}
--endIndex;
if(endIndex > 0) {
bubbleSort(oArr, endIndex)
}else {
// console.log('冒泡排序法', oArr);
}
}
let oArr2 = arrPro();
bubbleSort(oArr2);
console.timeEnd('冒泡排序法共花费了') // 冒泡排序法共花费了: 0.369ms
快速排序
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
快速排序Demo1
// 快速排序法
console.time('快速排序法共花费了')
function quickSort(oArray, leftIndex, rightIndex) {
let len = oArray.length;
let partitionIndex;
leftIndex = typeof leftIndex !== 'number' ? 0 : leftIndex;
rightIndex = typeof rightIndex !== 'number' ? len - 1 : rightIndex;
if(leftIndex < rightIndex) {
partitionIndex = partition(oArray, leftIndex, rightIndex);
// console.log('partitionIndex', partitionIndex);
quickSort(oArray, leftIndex, partitionIndex - 1);
quickSort(oArray, partitionIndex + 1, rightIndex);
}else {
// console.log('oArray', oArray);
// return oArray;
}
}
function partition(oArray, leftIndex, rightIndex) {
let swapIndex = leftIndex + 1;
for (let i=swapIndex; i<=rightIndex ; i++) {
if(oArray[i] < oArray[leftIndex]) {
swap(oArray, i, swapIndex);
++swapIndex;
}
}
swap(oArray, leftIndex, swapIndex-1);
return swapIndex-1;
}
let oArr = arrPro();
quickSort(oArr);
// console.log('快速排序法', oArr);
console.timeEnd('快速排序法共花费了') // 快速排序法共花费了: 0.221ms
快速排序Demo2
console.time('快速排序2法共花费了')
function partition2(arr, low, high) {
let pivot = arr[low];
while (low < high) {
while (low < high && arr[high] > pivot) {
--high;
}
arr[low] = arr[high];
while (low < high && arr[low] <= pivot) {
++low;
}
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
function quickSort2(arr, low, high) {
if (low < high) {
let pivot = partition2(arr, low, high);
quickSort2(arr, low, pivot - 1);
quickSort2(arr, pivot + 1, high);
}
return arr;
}
let oArr3 = arrPro();
quickSort2(oArr3, 0, oArr3.length - 1)
// console.log('oArr3', oArr3);
console.timeEnd('快速排序2法共花费了') // 快速排序2法共花费了: 0.110ms
javascript 创建对象
工厂模式
用函数封装以特定接口创建对象
function createPerson(name, age, job) {
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name)
}
return o;
}
let person1 = createPerson('nick', 29, 'Software Engineer');
let person2 = createPerson('Greg', 27, 'Doctor');
构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
// this.sayName = function() {
// console.log('name is:', this.name);
// }
}
function sayName() {
return this.name;
}
let person1 = new Person('Nicholas', 29, 'Software Engineer');
let person2 = new Person('Greg', 27, 'Doctor');
function readPerson(oPerson) {
for(let i in oPerson) {
if(typeof oPerson[i] === 'function') {
console.log(i, oPerson[i]())
}else {
console.log(i, oPerson[i])
}
}
}
readPerson(person1);
readPerson(person2);
原型模式
每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
function Person() {
Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function() {
console.log(this.name);
}
}
let person1 = new Person();
person1.sayName(); // 'Nicholas'
let person2 = new Person();
person2.sayName(); // 'Nicholas'
console.log(person1.sayName == person2.sayName); // trueperson1和person2 的内部属性proto(read virtual) 指向Person prototype; Person prototype.constructor 指向Person constructor 属性也是共享的,也可以通过实例共享。
person1.name = 'KKK';
console.log(person1.name); // "KKK" -----来自实例
delete person1.name;
console.log(person1.name); // "Nicholas" -----来自原型
console.log(person2.name); // "Nicholas" -----来自原型
console('name' in person1);// true
原型与in操作符
有两种方式使用in操作符: 单独使用和在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true, 无论该属性存在于实例中还是原型中
原型的动态性
let friend = new Person();
Person.prototype.sayHi = function() {
console.log('hi');
}
friend.sayHi(); // "hi"
在原型中查找值的过程是一次搜索
原生对象的原型
所有原生的引用类型(Object、Array、String, 等等) 都在其构造函数的原型上定义了方法
console.log(typeof Array.prototype.sort); // "function"
组合使用构造函数模式和原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Count"];
}
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name;)
}
}
let person1 = new Person('Nicholas', 27, "Software Engineer");
let person2 = new Person('Greg', 22, "Doctor");
person1.friends.push("Van");
console.log(person1.friends); // "Shelby, Count, Van"
console.log(person1.friends); // "Shelby, Count"
console.log(person1.friends === person2.friends); // false;
console.log(person1.sayName === person2.sayName); // true;
动态原型模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
// 方法
if(typeof this.sayName != 'function') {
Person.prototype.sayName = function() {
console.log('this.name');
}
}
}
let friend = new Person('Nicholas', 29, 'software Engineer');
friend.sayName();
只有在sayName()方法不存在的情况下,才能将它添加到原型当中去.
寄生构造函数模式
function SpecialArray() {
let values = new Array();
values.push.apply(values, arguments);
values.toPipedString = function() {
return this.join('|');
}
return values;
}
let colors = new SpecialArray("red", "blue", "green");
console.log(colors.toPipedString()); // "red|blue|green"
- 返回的对象和构造函数或者与构造函数的原型属性之间没有关系。
- 构造函数返回的对象与在构造函数外部创建的对象没有什么不同。
- 不能依赖instanceof操作符确定对象类型。
- 末尾的return 语句,可以重写调用构造函数时返回的值。
稳妥构造函数模式
function Person(name, age, job) {
let o = new Object();
o.sayName = function() {
console.log(name);
}
return o;
}
let friend = Person('Nicholas', 29, 'Software Engineer');
friend.sayName(); // 'Nicholas'
所谓的稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象,不使用new操作符号调用构造函数。在这种模式创建的对象中,除了使用sayName()方法之外,没有其他办法访问name的值。
使用原型链实现继承出现的问题
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {}
subType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); // ['red','blue','green','black']
let instance2 = new SubType();
console.log(instance2.colors); // ['red','blue','green','black']
没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
借用构造函数
通过使用apply()和call()方法在新创建的对象上执行构造函数
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {
// 继承了SuperType
SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); // ['red','blue','green','black']
let instance2 = new SubType();
console.log(instance2.colors); // ['red','blue','green']
借用构造函数的缺点 方法都在构造函数中定义,因此函数复用就无从谈起。
组合继承函数
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
let instance1 = new SubType('Nicholas', 29);
instance1.colors.push('black');
console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
instance1.sayName(); // 'Nicholas';
instance1.sayAge(); // 29
let instance2 = new SubType('Greg', 27);
console.log(instance2.colors); // ['red', 'blue', 'green']
instance2.sayName(); // 'Greg';
instance2.sayAge(); // 27
instanceof 和 isPrototypeOf() 也能够识别基于组合继承创建的对象
原型式继承
let person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van']
}
let anotherPerson = Object.create(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = 'Linda';
yetAnotherPerson.friends.push('Barbie');
console.log(person.friends); // ['Shelby', 'Court', 'Van', 'Rob', 'Barbie']
Object.create() 方法第二个参数与Object.defineProperties() 方法第二个参数格式相同
let person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van']
}
let anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
console.log(anotherPerson.name); // 'Greg'
寄生式继承
function createAnother(original) {
let clone = object(original);
clone.sayHi = function() {
console.log("hi");
};
return clone;
}
let person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van']
};
let anotherPerson = createAnother(person);
anotherPerson.sayHi(); // 'hi'
寄生组合式继承
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name); // 第二次调用SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); // 第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
寄生组合式继承基本模式如下:
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}
git-reset
三棵树
Git 作为一个系统,是以它的一般操作来管理并操纵这三棵树的:
树 | 用途 |
---|---|
HEAD | 上一次提交的快照,下一次提交的父结点 |
Index | 预期的下一次提交的快照 |
Working Directory | 沙盒 |
HEAD
HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,理解 HEAD 的最简方式,就是将它看做 你的上一次提交 的快照。
索引
索引是你的 预期的下一次提交。 我们也会将这个概念引用为 Git 的 “暂存区域”,这就是当你运行 git commit 时 Git 看起来的样子。
工作目录
最后,你就有了自己的工作目录。 另外两棵树以一种高效但并不直观的方式,将它们的内容存储在 .git 文件夹中。 工作目录会将它们解包为实际的文件以便编辑。 你可以把工作目录当做 沙盒。在你将修改提交到暂存区并记录到历史之前,可以随意更改。
reset 命令会以特定的顺序重写这三棵树,在你指定以下选项时停止:
移动 HEAD 分支的指向 (若指定了 --soft,则到此停止)
使索引看起来像 HEAD (若未指定 --hard,则到此停止)
使工作目录看起来像索引
throttle && debounce
OverView
throttle
throttle 降低触发回调的频率
debounce
debounce 对于一定时间段的连续的函数调用,只让其执行一次
throttle
throttle应用场景
- DOM 元素的拖拽功能实现(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 搜索联想(keyup)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
throttle Function
function throttle(fn, interval = 300) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, interval);
};
}
debounce
debounce 应用场景
- 每次 resize/scroll 触发统计事件
- 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)
### debounce Function
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
Object.defineProperty
方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。
参数
- obj 需要定义属性的对象
- prop 需要定义或修改的属性的名字
- descriptor 将被定义或修改的属性的描述符
- 返回值 返回传入函数的对象,即第一个参数obj
- configurable 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,也能够被删除。默认为 false。
- enumerable 当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。
- value 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
- writable 当且仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变。默认为 false。
- get 一个给属性提供 getter 的方法,如果没有 getter 则为undefined。当我们读取某个属性的时候,其实是在对象内部调用了该方法,此方法必须要有return语句。该方法返回值被用作属性值。默认为 undefined。
- set 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。也就是说,当我们设置某个属性的时候,实际上是在对象的内部调用了该方法。
实例
var a = {}
Object.defineProperty(a, "b", {
set: function(newValue) {
console.log('新值: ' + newValue)
},
get: function() {
console.log('需要返回的值')
return 2
}
})
a.b = 1 // 新值: 1
a.b // 需要返回的值 2